package fi.lpam.dataluokat;

import fi.lpam.gui.virheDialogit.SiirtoVirhe;

import java.sql.*;
import java.time.LocalDate;
import java.util.ArrayList;

@SuppressWarnings("DuplicatedCode")
public class Kuljetus implements TietokantaOlio {
    private int id = -1;
    private int asiakasID;
    private String nimi;
    private LocalDate kuljetusPäivämäärä;
    private int kuljetusKierros;
    private int salaatit, pääruoat, jälkiruoat;
    private String osoite, lisätieto, avainTieto, ruokarajoite;

    public Kuljetus(Asiakas asiakas, LocalDate pvm, int kuljetusKierros) {
        this.asiakasID = asiakas.getId();
        this.nimi = asiakas.getNimi();
        this.kuljetusPäivämäärä = pvm;
        this.kuljetusKierros = kuljetusKierros;
        int kuljetusKerroin = switch (kuljetusPäivämäärä.getDayOfWeek()) {
            case MONDAY -> asiakas.getMaanantaiKpl();
            case TUESDAY -> asiakas.getTiistaiKpl();
            case WEDNESDAY -> asiakas.getKeskiviikkoKpl();
            case THURSDAY -> asiakas.getTorstaiKpl();
            case FRIDAY -> asiakas.getPerjantaiKpl();
            default -> 0;
        };
        this.salaatit = kuljetusKerroin * asiakas.getSalaatit();
        this.pääruoat = kuljetusKerroin * asiakas.getPääruoat();
        this.jälkiruoat = kuljetusKerroin * asiakas.getJälkiruoat();
        this.osoite = asiakas.getOsoite();
        this.avainTieto = asiakas.getAvainTieto();
        this.lisätieto = asiakas.getLisätiedot();
        this.ruokarajoite = asiakas.getErityisruokavalio();
    }

    private Kuljetus(int id, int asiakasID, String nimi, LocalDate kuljetusPäivämäärä, int kuljetusKierros, int salaatit, int pääruoat, int jälkiruoat, String osoite, String lisätieto, String avainTieto, String ruokarajoite) {
        this.id = id;
        this.asiakasID = asiakasID;
        this.nimi = nimi;
        this.kuljetusPäivämäärä = kuljetusPäivämäärä;
        this.kuljetusKierros = kuljetusKierros;
        this.salaatit = salaatit;
        this.pääruoat = pääruoat;
        this.jälkiruoat = jälkiruoat;
        this.osoite = osoite;
        this.lisätieto = lisätieto;
        this.avainTieto = avainTieto;
        this.ruokarajoite = ruokarajoite;
    }

    public static ArrayList<Kuljetus> haeKaikki() {
        try (Connection tietokanta = Tietokanta.haeYhteys()) {
            Statement stmt = tietokanta.createStatement();
            ResultSet rs = stmt.executeQuery("SELECT * FROM kuljetukset;");
            return parsiResultSet(rs);
        } catch (SQLException e) {
            SiirtoVirhe virhe = new SiirtoVirhe(e);
            virhe.showAndWait();
            return null;
        }
    }

    /**
     * Hakee yhden päivän kuljetukset riippumatta kierroksesta
     * @param päivä haettava päivä
     * @return päivän kuljetukset
     */
    public static ArrayList<Kuljetus> haePäivänKuljetukset(LocalDate päivä) {
        try (Connection tietokanta = Tietokanta.haeYhteys()) {
            PreparedStatement stmt = tietokanta.prepareStatement("SELECT * FROM kuljetukset WHERE kuljetusPäivämäärä = ?;");
            stmt.setDate(1, Date.valueOf(päivä));
            ResultSet rs = stmt.executeQuery();
            return parsiResultSet(rs);
        } catch (SQLException e) {
            SiirtoVirhe virhe = new SiirtoVirhe(e);
            virhe.showAndWait();
            return null;
        }
    }

    /**
     * Hakee tietyn kierroksen kuljetukset
     * @param päivä kierroksen päivä
     * @param kuljetusKierros kierroksen numero
     * @return valitun päivän valitun kierroksen kuljetukset
     */
    public static ArrayList<Kuljetus> haePäivänKuljetuksetKierroksella(LocalDate päivä, int kuljetusKierros) {
        try (Connection tietokanta = Tietokanta.haeYhteys()) {
            PreparedStatement stmt = tietokanta.prepareStatement("SELECT * FROM kuljetukset WHERE kuljetusKierros = ? and kuljetusPäivämäärä = ?");
            stmt.setInt(1, kuljetusKierros);
            stmt.setDate(2, Date.valueOf(päivä));
            ResultSet rs = stmt.executeQuery();
            return parsiResultSet(rs);
        } catch (SQLException e) {
            SiirtoVirhe virhe = new SiirtoVirhe(e);
            virhe.showAndWait();
            return null;
        }
    }
    /**
     * Hakee kuljetukset aikajakson perusteella
     *
     * @param alku  jakson alku (sisältyen)
     * @param loppu jakson loppu (sisältyen)
     * @return Jakson kuljetukset
     */
    public static ArrayList<Kuljetus> haeAikaJakso(LocalDate alku, LocalDate loppu) {
        try (Connection tietokanta = Tietokanta.haeYhteys()) {
            PreparedStatement stmt = tietokanta.prepareStatement("SELECT * FROM kuljetukset WHERE kuljetusPäivämäärä BETWEEN ? AND ?");
            stmt.setDate(1, Date.valueOf(alku));
            stmt.setDate(2, Date.valueOf(loppu));
            ResultSet rs = stmt.executeQuery();
            return parsiResultSet(rs);
        } catch (SQLException e) {
            SiirtoVirhe virhe = new SiirtoVirhe(e);
            virhe.showAndWait();
            return null;
        }
    }

    public static ArrayList<Kuljetus> haeAikaJaksollaJaKierroksella(LocalDate alku, LocalDate loppu, int kuljetusKierros) {
        try (Connection tietokanta = Tietokanta.haeYhteys()) {
            PreparedStatement stmt = tietokanta.prepareStatement("SELECT * FROM kuljetukset WHERE kuljetusKierros = ? and kuljetusPäivämäärä BETWEEN ? AND ?");
            stmt.setInt(1, kuljetusKierros);
            stmt.setDate(2, Date.valueOf(alku));
            stmt.setDate(3, Date.valueOf(loppu));
            ResultSet rs = stmt.executeQuery();
            return parsiResultSet(rs);
        } catch (SQLException e) {
            SiirtoVirhe virhe = new SiirtoVirhe(e);
            virhe.showAndWait();
            return null;
        }
    }

    @Override
    public boolean tallennaUusi() {
         try (Connection tietokanta = Tietokanta.haeYhteys()) {
            PreparedStatement stmt = tietokanta.prepareStatement("INSERT INTO kuljetukset (asiakasID, nimi, kuljetusPäivämäärä, kuljetusKierros, salaatit, pääruoat, jälkiruoat, ruokarajoite, osoite, avainTieto, lisätieto) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
            stmt.setInt(1, asiakasID);
            stmt.setString(2, nimi);
            stmt.setDate(3, Date.valueOf(kuljetusPäivämäärä));
            stmt.setInt(4, kuljetusKierros);
            stmt.setInt(5, salaatit);
            stmt.setInt(6, pääruoat);
            stmt.setInt(7, jälkiruoat);
            stmt.setString(8, ruokarajoite);
            stmt.setString(9, osoite);
            stmt.setString(10, avainTieto);
            stmt.setString(11, lisätieto);
            int muokatutRivit = stmt.executeUpdate();

            if (muokatutRivit == 0) {
                throw new SQLException("Kuljetuksen luominen epäonnistui, rivejä ei muokattu");
            }
            try (ResultSet generatedKeys = stmt.getGeneratedKeys()) {
                if (generatedKeys.next()) {
                    this.setId(generatedKeys.getInt(1));
                } else {
                    throw new SQLException("Kuljetuksen luominen epäonnistui, id:tä ei saatu");
                }
            }
        } catch (SQLException e) {
            SiirtoVirhe alert = new SiirtoVirhe(e);
            alert.showAndWait();
            return false;
        }
        return true;
    }

    @Override
    public boolean päivitäTietokantaan() {
        try (Connection tietokanta = Tietokanta.haeYhteys()) {
            PreparedStatement stmt = tietokanta.prepareStatement("UPDATE kuljetukset set asiakasID = ?, nimi = ?, kuljetusPäivämäärä = ?, kuljetusKierros = ?, salaatit = ?, pääruoat = ?, jälkiruoat = ?, ruokarajoite = ?, osoite = ?, avainTieto = ?, lisätieto = ? WHERE id = ?");
            stmt.setInt(1, asiakasID);
            stmt.setString(2, nimi);
            stmt.setDate(3, Date.valueOf(kuljetusPäivämäärä));
            stmt.setInt(4, kuljetusKierros);
            stmt.setInt(5, salaatit);
            stmt.setInt(6, pääruoat);
            stmt.setInt(7, jälkiruoat);
            stmt.setString(8, ruokarajoite);
            stmt.setString(9, osoite);
            stmt.setString(10, avainTieto);
            stmt.setString(11, lisätieto);
            stmt.setInt(12, id);
            stmt.executeUpdate();
            return true;
        } catch (SQLException e) {
            SiirtoVirhe alert = new SiirtoVirhe(e);
            alert.showAndWait();
            return false;
        }
    }

    @Override
    public boolean poista() {
        try (Connection tietokanta = Tietokanta.haeYhteys()) {
            String sql = "DELETE FROM kuljetukset WHERE id = " + id;
            Statement stmt = tietokanta.createStatement();
            stmt.executeUpdate(sql);
            return true;
        } catch (SQLException e) {
            SiirtoVirhe alert = new SiirtoVirhe(e);
            alert.showAndWait();
            return false;
        }
    }

    @Override
    public boolean tallenna() {
        if (this.id == -1) {
            return this.tallennaUusi();
        }
        else {
            return this.päivitäTietokantaan();
        }
    }

    private static ArrayList<Kuljetus> parsiResultSet(ResultSet rs) {
        ArrayList<Kuljetus> kuljetukset = new ArrayList<>();

        try {
            while (rs.next()) {
                Kuljetus uusi = new Kuljetus(
                        rs.getInt("id"),
                        rs.getInt("asiakasID"),
                        rs.getString("nimi"),
                        rs.getDate("kuljetusPäivämäärä").toLocalDate(),
                        rs.getInt("kuljetusKierros"),
                        rs.getInt("salaatit"),
                        rs.getInt("pääruoat"),
                        rs.getInt("jälkiruoat"),
                        rs.getString("osoite"),
                        rs.getString("lisätieto"),
                        rs.getString("avainTieto"),
                        rs.getString("ruokarajoite")
                );
                kuljetukset.add(uusi);
            }
        } catch (SQLException e) {
            SiirtoVirhe alert = new SiirtoVirhe(e);
            alert.showAndWait();
        }
        return kuljetukset;
    }

    @Override
    public String toString() {
        return "Kuljetus{" +
                "id=" + id +
                ", asiakasID=" + asiakasID +
                ", nimi='" + nimi + '\'' +
                ", kuljetusPäivämäärä=" + kuljetusPäivämäärä +
                ", kuljetusKierros=" + kuljetusKierros +
                ", salaatit=" + salaatit +
                ", pääruoat=" + pääruoat +
                ", jälkiruoat=" + jälkiruoat +
                ", osoite='" + osoite + '\'' +
                ", lisätieto='" + lisätieto + '\'' +
                ", avainTieto='" + avainTieto + '\'' +
                ", ruokarajoite='" + ruokarajoite + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

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

    public int getAsiakasID() {
        return asiakasID;
    }

    public void setAsiakasID(int asiakasID) {
        this.asiakasID = asiakasID;
    }

    public String getNimi() {
        return nimi;
    }

    public void setNimi(String nimi) {
        this.nimi = nimi;
    }

    public LocalDate getKuljetusPäivämäärä() {
        return kuljetusPäivämäärä;
    }

    public void setKuljetusPäivämäärä(LocalDate kuljetusPäivämäärä) {
        this.kuljetusPäivämäärä = kuljetusPäivämäärä;
    }

    public int getKuljetusKierros() {
        return kuljetusKierros;
    }

    public void setKuljetusKierros(int kuljetusKierros) {
        this.kuljetusKierros = kuljetusKierros;
    }

    public int getSalaatit() {
        return salaatit;
    }

    public void setSalaatit(int salaatit) {
        this.salaatit = salaatit;
    }

    public int getPääruoat() {
        return pääruoat;
    }

    public void setPääruoat(int pääruoat) {
        this.pääruoat = pääruoat;
    }

    public int getJälkiruoat() {
        return jälkiruoat;
    }

    public void setJälkiruoat(int jälkiruoat) {
        this.jälkiruoat = jälkiruoat;
    }

    public String getOsoite() {
        return osoite;
    }

    public void setOsoite(String osoite) {
        this.osoite = osoite;
    }

    public String getLisätieto() {
        return lisätieto;
    }

    public void setLisätieto(String lisätieto) {
        this.lisätieto = lisätieto;
    }

    public String getAvainTieto() {
        return avainTieto;
    }

    public void setAvainTieto(String avainTieto) {
        this.avainTieto = avainTieto;
    }

    public String getRuokarajoite() {
        return ruokarajoite;
    }

    public void setRuokarajoite(String ruokarajoite) {
        this.ruokarajoite = ruokarajoite;
    }
}