package db;

import util.Config;

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

import model.PersonMapping;

/**
 * DAO für die Schulungs-Tabelle.
 * Führt INSERT-, SELECT- und UPDATE-Operationen aus, öffnet/schließt die DB-Verbindung automatisch.
 */
public class TrainingDAO {

    private static final String INSERT_SQL = """
        INSERT INTO trainings (
            datum, startzeit, dauer_minuten, modus,
            teilnehmende_anzahl, teilnehmende_typ, durchfuehrende_person,
            ausgefallen_grund, titel, inhaltstyp, veranstaltungstyp, kommentar,
            geplant, evaluation
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """;

    private Connection connect() throws SQLException {
        return DriverManager.getConnection("jdbc:sqlite:" + Config.getDbPath());
    }

    // --------------------------------------------------------
    // INSERT (Overload mit geplant)
    // --------------------------------------------------------
    public void insertTraining(
            String datum,
            String startzeit,
            Integer dauerMinuten,
            String modus,
            Integer teilnehmendeAnzahl,
            String teilnehmendeTyp,
            String durchfuehrendePerson,
            String ausgefallenGrund,
            String titel,
            String inhaltstyp,
            String veranstaltungstyp,
            String kommentar,
            Integer geplant, // 0/1 (null -> 0)
            Double evaluation
    ) {
        try (Connection conn = connect();
             PreparedStatement ps = conn.prepareStatement(INSERT_SQL)) {

            ps.setString(1, datum);
            ps.setString(2, startzeit);
            if (dauerMinuten != null) ps.setInt(3, dauerMinuten); else ps.setNull(3, Types.INTEGER);
            ps.setString(4, modus);
            if (teilnehmendeAnzahl != null) ps.setInt(5, teilnehmendeAnzahl); else ps.setNull(5, Types.INTEGER);
            ps.setString(6, teilnehmendeTyp);
            ps.setString(7, PersonMapping.toKuerzel(durchfuehrendePerson));
            ps.setString(8, ausgefallenGrund);
            ps.setString(9, titel);
            ps.setString(10, inhaltstyp);
            ps.setString(11, veranstaltungstyp);
            ps.setString(12, kommentar);
            ps.setInt(13, (geplant != null && geplant != 0) ? 1 : 0);

            if (evaluation != null) ps.setDouble(14, evaluation);
            else ps.setNull(14, Types.REAL);
            
            ps.executeUpdate();

        } catch (SQLException e) {
            System.err.println("Fehler beim Speichern des Trainingsdatensatzes: " + e.getMessage());
            e.printStackTrace();
        }
    }

    // Abwärtskompatibel: delegiert auf geplant=0
    public void insertTraining(
            String datum,
            String startzeit,
            Integer dauerMinuten,
            String modus,
            Integer teilnehmendeAnzahl,
            String teilnehmendeTyp,
            String durchfuehrendePerson,
            String ausgefallenGrund,
            String titel,
            String inhaltstyp,
            String veranstaltungstyp,
            String kommentar
    ) {
        insertTraining(datum, startzeit, dauerMinuten, modus, teilnehmendeAnzahl, teilnehmendeTyp,
                durchfuehrendePerson, ausgefallenGrund, titel, inhaltstyp, veranstaltungstyp, kommentar, 0, null);
    }

    // --------------------------------------------------------
    // UPDATE inkl. "geplant" (für Edit-Dialog mit Planungs-Flag)
    // --------------------------------------------------------
    public void updateTraining(
            int id,
            LocalDate datum,
            LocalTime startzeit,
            Integer dauerMinuten,
            String modus,
            Integer teilnehmendeAnzahl,
            String teilnehmendeTyp,
            String durchfuehrendePerson,
            String ausgefallenGrund,
            String titel,
            String inhaltstyp,
            String veranstaltungstyp,
            String kommentar,
            Integer geplant, // 0/1; null wird zu 0 normalisiert
            Double evaluation
    ) {
    	final String sql = """
    		    UPDATE trainings SET
    		        datum = ?,
    		        startzeit = ?,
    		        dauer_minuten = ?,
    		        modus = ?,
    		        teilnehmende_anzahl = ?,
    		        teilnehmende_typ = ?,
    		        durchfuehrende_person = ?,
    		        ausgefallen_grund = ?,
    		        titel = ?,
    		        inhaltstyp = ?,
    		        veranstaltungstyp = ?,
    		        kommentar = ?,
    		        geplant = ?,
    		        evaluation = ?
    		    WHERE id = ?
    		    """;

    		try (Connection conn = connect();
    		     PreparedStatement ps = conn.prepareStatement(sql)) {

    		    if (datum != null) ps.setString(1, datum.toString()); else ps.setNull(1, Types.VARCHAR);
    		    if (startzeit != null) ps.setString(2, startzeit.toString()); else ps.setNull(2, Types.VARCHAR);
    		    if (dauerMinuten != null) ps.setInt(3, dauerMinuten); else ps.setNull(3, Types.INTEGER);
    		    setNullableString(ps, 4, modus);
    		    if (teilnehmendeAnzahl != null) ps.setInt(5, teilnehmendeAnzahl); else ps.setNull(5, Types.INTEGER);
    		    setNullableString(ps, 6, teilnehmendeTyp);
    		    setNullableString(ps, 7, durchfuehrendePerson);
    		    setNullableString(ps, 8, ausgefallenGrund);
    		    setNullableString(ps, 9, titel);
    		    setNullableString(ps, 10, inhaltstyp);
    		    setNullableString(ps, 11, veranstaltungstyp);
    		    setNullableString(ps, 12, kommentar);

    		    ps.setInt(13, (geplant != null && geplant != 0) ? 1 : 0);
    		    if (evaluation != null) ps.setDouble(14, evaluation); else ps.setNull(14, Types.REAL);
    		    ps.setInt(15, id);

    		    ps.executeUpdate();
    		}
    			catch (SQLException e) {
            System.err.println("Fehler beim Aktualisieren des Trainingsdatensatzes (id=" + id + "): " + e.getMessage());
            throw new RuntimeException(e);
        }
    }

    private static void setNullableString(PreparedStatement ps, int index, String value) throws SQLException {
        if (value != null) ps.setString(index, value); else ps.setNull(index, Types.VARCHAR);
    }

    // --------------------------------------------------------
    // SELECTS
    // --------------------------------------------------------
    public List<Map<String, Object>> getAllTrainings() {
        List<Map<String, Object>> result = new ArrayList<>();
        String sql = "SELECT * FROM trainings ORDER BY datum DESC, startzeit ASC";

        try (Connection conn = connect();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {

            ResultSetMetaData meta = rs.getMetaData();
            int colCount = meta.getColumnCount();

            while (rs.next()) {
                Map<String, Object> row = new LinkedHashMap<>();
                for (int i = 1; i <= colCount; i++) {
                    row.put(meta.getColumnName(i), rs.getObject(i));
                }
                result.add(row);
            }

        } catch (SQLException e) {
            System.err.println("Fehler beim Laden der Trainingsdaten: " + e.getMessage());
            e.printStackTrace();
        }

        return result;
    }

    public List<Map<String, Object>> getTrainingsWithFilters(
            String titel,
            String typ,
            String inhaltstyp,
            String modus,
            String teilnehmendeTyp,
            String durchfuehrendePerson,
            boolean nurAusgefallene
    ) {
        List<Map<String, Object>> result = new ArrayList<>();

        StringBuilder sql = new StringBuilder("SELECT * FROM trainings WHERE 1=1");
        List<Object> params = new ArrayList<>();

        if (titel != null && !titel.isBlank()) {
            sql.append(" AND titel LIKE ?");
            params.add("%" + titel + "%");
        }
        if (typ != null && !typ.isBlank()) {
            sql.append(" AND veranstaltungstyp = ?");
            params.add(typ);
        }
        if (inhaltstyp != null && !inhaltstyp.isBlank()) {
            sql.append(" AND inhaltstyp = ?");
            params.add(inhaltstyp);
        }
        if (modus != null && !modus.isBlank()) {
            sql.append(" AND modus = ?");
            params.add(modus);
        }
        if (teilnehmendeTyp != null && !teilnehmendeTyp.isBlank()) {
            sql.append(" AND teilnehmende_typ = ?");
            params.add(teilnehmendeTyp);
        }
        if (durchfuehrendePerson != null && !durchfuehrendePerson.isBlank()) {
            sql.append(" AND durchfuehrende_person = ?");
            params.add(durchfuehrendePerson);
        }
        if (nurAusgefallene) {
            sql.append(" AND (ausgefallen_grund IS NOT NULL AND ausgefallen_grund <> '')");
        }

        sql.append(" ORDER BY datum DESC, startzeit ASC");

        try (Connection conn = connect();
             PreparedStatement ps = conn.prepareStatement(sql.toString())) {

            for (int i = 0; i < params.size(); i++) {
                ps.setObject(i + 1, params.get(i));
            }

            ResultSet rs = ps.executeQuery();
            ResultSetMetaData meta = rs.getMetaData();
            int colCount = meta.getColumnCount();

            while (rs.next()) {
                Map<String, Object> row = new LinkedHashMap<>();
                for (int i = 1; i <= colCount; i++) {
                    row.put(meta.getColumnName(i), rs.getObject(i));
                }
                result.add(row);
            }

        } catch (SQLException e) {
            System.err.println("Fehler beim Filtern der Trainings: " + e.getMessage());
            e.printStackTrace();
        }

        return result;
    }

    // --------------------------------------------------------
    // Hilfs-Select: distinct durchfuehrende_person (für Dropdown)
    // --------------------------------------------------------
    
    public List<String> getRecentDurchfuehrendePersonen(int monthsBack) {
        String sql = """
            SELECT DISTINCT durchfuehrende_person
            FROM trainings
            WHERE durchfuehrende_person IS NOT NULL
              AND durchfuehrende_person <> ''
              AND datum >= date('now', ?)
            ORDER BY durchfuehrende_person COLLATE NOCASE
        """;

        List<String> list = new ArrayList<>();
        try (Connection conn = DriverManager.getConnection("jdbc:sqlite:" + util.Config.getDbPath());
             PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setString(1, "-" + monthsBack + " months");
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                list.add(rs.getString(1));
            }
        } catch (SQLException e) {
            throw new RuntimeException("Fehler beim Laden der Personen (letzte " + monthsBack + " Monate): " + e.getMessage(), e);
        }
        return list;
    }

    
    public boolean hasTrainingsWithoutParticipantsForCurrentUser() {
        final String sql = """
            SELECT COUNT(*) AS cnt
            FROM trainings
            WHERE durchfuehrende_person = ?
              AND datum < date('now')
              AND (teilnehmende_anzahl IS NULL OR teilnehmende_anzahl = 0)
              AND (ausgefallen_grund IS NULL OR ausgefallen_grund = '')
            """;

        String currentUser = System.getProperty("user.name");
        try (Connection conn = connect();
             PreparedStatement ps = conn.prepareStatement(sql)) {

            ps.setString(1, currentUser);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                return rs.getInt("cnt") > 0;
            }
        } catch (SQLException e) {
            System.err.println("Fehler bei Abfrage unvollständiger Schulungen für Benutzer '" + System.getProperty("user.name") + "': " + e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

    
    public List<String> getAllDurchfuehrendePersonen() {
        final String sql = """
            SELECT DISTINCT durchfuehrende_person
            FROM trainings
            WHERE durchfuehrende_person IS NOT NULL AND durchfuehrende_person <> ''
            ORDER BY durchfuehrende_person
            """;
        List<String> list = new ArrayList<>();
        try (Connection conn = connect();
             Statement st = conn.createStatement();
             ResultSet rs = st.executeQuery(sql)) {
            while (rs.next()) {
                list.add(rs.getString(1));
            }
        } catch (SQLException e) {
            System.err.println("Fehler beim Laden der durchführenden Personen: " + e.getMessage());
            e.printStackTrace();
        }
        if (list.isEmpty()) {
            list.add(System.getProperty("user.name"));
        }
        return list;
    }
    
    public void deleteTraining(int id) {
        final String sql = "DELETE FROM trainings WHERE id = ?";
        try (Connection conn = connect();
             PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setInt(1, id);
            ps.executeUpdate();
        } catch (SQLException e) {
            System.err.println("Fehler beim Löschen des Trainingsdatensatzes (id=" + id + "): " + e.getMessage());
            throw new RuntimeException(e);
        }
    }

}
