package main.java.rdg;

import main.java.DbConnection;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class Movie {
    private long id = 0;
    private String name;
    private int length;
    private String description;
    private Set<Genre> genres = new HashSet<>();

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Set<Genre> getGenres() {
        return genres;
    }

    public void setGenres(Set<Genre> genres) {
        this.genres = genres;
    }

    public void addGenre(Genre genre){
        this.genres.add(genre);
    }

    public void addGenres(Set<Genre> genres){
        this.genres.addAll(genres);
    }

    @Override
    public String toString() {
        return "Movie{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", length=" + length +
                ", description='" + description + '\'' +
                ", genres=" + genres +
                '}';
    }
//String name, int length, String desrciption, Set<Genre> genres
    public boolean create(){
        DbConnection.setIsolationCommitted();

        try (PreparedStatement s = DbConnection.getCon().prepareStatement("INSERT INTO movies (name, length, description) VALUES (?, ?, ?)", Statement.RETURN_GENERATED_KEYS)){
            DbConnection.disableCommit();

            s.setString(1, name);
            s.setInt(2, length);
            s.setString(3, description);

            s.executeUpdate();

            try (ResultSet r = s.getGeneratedKeys()) {
                r.next();
                id = r.getLong(1);
            }

            if (id == 0) throw new SQLException("Movie not found");

            // priradit zanre filmu
            for (Genre genre : genres){
                addGenreToMovie(genre);
            }

            DbConnection.getCon().commit();
        } catch (SQLException e){
            DbConnection.rollback();
            return false;
        } finally {
            DbConnection.resetIsolation();
            DbConnection.resetCommit();
        }

        return true;
    }

    public void addGenreToMovie(Genre genre) throws SQLException {
        try (PreparedStatement ss = DbConnection.getCon().prepareStatement("INSERT INTO movie_genres (movie_id, genre_id) VALUES (?, ?)")){
            ss.setLong(1, id);
            ss.setLong(2, genre.getId());

            ss.executeUpdate();
        }
    }

    public static Movie read (long id){
        try (PreparedStatement s = DbConnection.getCon().prepareStatement("SELECT name, length, description FROM movies WHERE movie_id=?")){
            s.setLong(1, id);

            Movie movie = new Movie();

            try (ResultSet r = s.executeQuery()){
                r.next();
                movie.setId(id);
                movie.setName(r.getString(1));
                movie.setLength(r.getInt(2));
                movie.setDescription(r.getString(3));

                //najst priradene zanre nasho filmu
                movie.addGenres(getMovieGenres(id));
            }

            return movie;

        } catch (SQLException e){
            return null;
        }
    }

    public static Set<Genre> getMovieGenres(long id) throws SQLException{
        Set<Genre> genres = new HashSet<>();
        try (PreparedStatement ss = DbConnection.getCon().prepareStatement("SELECT genre_id FROM movie_genres WHERE movie_id=?")){
            ss.setLong(1,id);

            try (ResultSet rr = ss.executeQuery()){
                while (rr.next()){
                    long genreID = rr.getInt(1);

                    genres.add(Genre.read(genreID));
                }
            }
        }
        return genres;
    }

    public static List<Object> readDetailed (long id) {
        try (PreparedStatement s = DbConnection.getCon().prepareStatement("SELECT name, length, description FROM movies WHERE movie_id=?")){
            s.setLong(1, id);

            Movie movie = new Movie();

            try (ResultSet r = s.executeQuery()){
                r.next();
                movie.setId(id);
                movie.setName(r.getString(1));
                movie.setLength(r.getInt(2));
                movie.setDescription(r.getString(3));

                //najst priradene zanre nasho filmu
                movie.addGenres(getMovieGenres(id));
            }
            List<Screening> screenings = Screening.readByMovie(id);

            return screenings!= null ? List.of(movie, screenings) : null;

        } catch (SQLException e){
            DbConnection.rollback();
            return null;
        }
    }
//    long id, String name, int length, String description, Set<Genre> genres
    public boolean update (){
        DbConnection.setIsolationCommitted();
        try (PreparedStatement s = DbConnection.getCon().prepareStatement("UPDATE movies SET name=?, length=?, description=? WHERE movie_id=?")){
            DbConnection.disableCommit();

            s.setString(1, name);
            s.setInt(2, length);
            s.setString(3, description);
            s.setLong(4, id);

            s.executeUpdate();

            //pridat nove zanre a vymazat ostatne
            try (PreparedStatement ss = DbConnection.getCon().prepareStatement("SELECT genre_id FROM movie_genres WHERE movie_id=?")){
                ss.setLong(1,id);

                Set<Long> genreIDs = genres.stream().map(Genre::getId).collect(Collectors.toSet());

                //vymazanie vyradenych zanrov
                try (ResultSet rr = ss.executeQuery()){
                    while (rr.next()){
                        long genreID = rr.getLong(1);
                        if (genreIDs.contains(genreID)){
                            genreIDs.remove(genreID);
                        }

                        else {
                            try (PreparedStatement sss = DbConnection.getCon().prepareStatement("DELETE FROM movie_genres WHERE movie_id=? AND genre_id=?")){
                                sss.setLong(1, id);
                                sss.setLong(2, genreID);

                                sss.executeUpdate();
                            }
                        }
                    }
                }

                //pridanie novych zanrov
                for (long genreID : genreIDs){
                    addGenreToMovie(Genre.read(genreID));
                }
            }

            DbConnection.getCon().commit();

        } catch (SQLException e){
            DbConnection.rollback();
            return false;
        } finally {
            DbConnection.resetCommit();
            DbConnection.resetIsolation();
        }

        return true;
    }

    public boolean delete () {
        try (PreparedStatement s = DbConnection.getCon().prepareStatement("DELETE FROM movies WHERE movie_id=?")) {
            s.setLong(1, id);
            s.executeUpdate();
        } catch (SQLException e){
            return false;
        }
        return true;
    }

    public static Set<Movie> getMoviesOfGenre (long id) {
        Set<Movie> movies = new HashSet<>();

        try (PreparedStatement s = DbConnection.getCon().prepareStatement("SELECT movie_id FROM movie_genres WHERE genre_id=?")){
            s.setLong(1, id);

            try (ResultSet r = s.executeQuery()){
                while (r.next()){
                    long movieID = r.getInt(1);

                    movies.add(read(movieID));
                }
            }
        } catch (SQLException e) {
            return null;
        }

        return movies;
    }

    public static Set<Movie> listAll () {
        try (PreparedStatement s = DbConnection.getCon().prepareStatement("SELECT * FROM movies")){
            Set<Movie> movies = new HashSet<>();

            try (ResultSet r = s.executeQuery()) {
                while (r.next()) {
                    movies.add(read(r.getLong(1)));
                }
            }

            return movies;
        } catch (SQLException e) {
            return null;
        }
    }
}
