// File: ui/TrainingFormUIPlanner.java
package ui;

import db.TrainingDAO;
import db.TrainingTypeDAO;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import util.Config;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;

/**
 * Tab 2: Schulungen vorausplanen (bis zu 100 Termine).
 * Unterschiede zu EnterEvent:
 * - Keine Felder „Ausgefallen“ und „Teilnehmende“.
 * - Pro Zeile: Datum, Startzeit, Durchführende Person (optional, jetzt auch Freitext).
 * - 100 Zeilen, 1 sichtbar; jede befüllte Zeile schaltet die nächste frei.
 * Beim Speichern werden alle befüllten Zeilen als geplant=1 gespeichert.
 */
public class TrainingFormUIPlanner extends BorderPane {

    private static final int MAX_ROWS = 100;
    private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");

    // Oberer Block
    private final ComboBox<String> veranstaltungstypBox = new ComboBox<>();
    private final ComboBox<String> inhaltstypBox = new ComboBox<>();
    private final TextField titelField = new TextField();
    private final TextField dauerField = new TextField();
    private final ComboBox<String> modusBox = new ComboBox<>();
    private final ComboBox<String> teilnehmerTypBox = new ComboBox<>();
    private final TextArea kommentarField = new TextArea();

    private final Button speichernButton = new Button("Alle geplanten speichern");
    private final Button resetButton = new Button("Zurücksetzen");

    // Zeilenbereich
    private final VBox rowsBox = new VBox(6);
    private final List<RowControls> rows = new ArrayList<>(MAX_ROWS);

    // Datenquellen (Personenliste aus DB)
    private List<String> personen = new ArrayList<>();

    public TrainingFormUIPlanner() {
        setPadding(new Insets(15));

        Label heading = new Label("Schulungen vorausplanen");
        heading.setStyle("-fx-font-size: 18; -fx-font-weight: bold;");
        setTop(heading);

        // Oberes Formular
        GridPane topGrid = new GridPane();
        topGrid.setHgap(10);
        topGrid.setVgap(8);
        topGrid.setPadding(new Insets(15, 0, 10, 0));

        int r = 0;
        topGrid.add(new Label("Veranstaltungstyp:"), 0, r); topGrid.add(veranstaltungstypBox, 1, r++);
        topGrid.add(new Label("Inhaltstyp:"), 0, r); topGrid.add(inhaltstypBox, 1, r++);
        topGrid.add(new Label("Titel Veranstaltung:"), 0, r); topGrid.add(titelField, 1, r++);
        topGrid.add(new Label("Dauer (Min):"), 0, r); topGrid.add(dauerField, 1, r++);
        topGrid.add(new Label("Modus:"), 0, r); topGrid.add(modusBox, 1, r++);
        topGrid.add(new Label("Typ Teilnehmende:"), 0, r); topGrid.add(teilnehmerTypBox, 1, r++);
        topGrid.add(new Label("Kommentar:"), 0, r); topGrid.add(kommentarField, 1, r++);

        veranstaltungstypBox.getItems().setAll(model.DataModels.getVeranstaltungstypen());
        modusBox.getItems().setAll(model.DataModels.getModi());
        teilnehmerTypBox.getItems().setAll(model.DataModels.getTeilnehmendeTypen());
        kommentarField.setPrefRowCount(2);
        kommentarField.setWrapText(true);

        fillInhaltstypBoxFromDatabase();

        // Titel-Autofill wie im EnterEvent
        veranstaltungstypBox.setOnAction(e -> updateFieldsForVeranstaltungstyp());
        inhaltstypBox.setOnAction(e -> updateTitleFromInhaltstyp());

        applyIntegerFilter(dauerField);

        // Zeilenbereich: 100 Zeilen vorbereiten
        buildRows();

        // Buttons
        HBox actions = new HBox(10, speichernButton, resetButton);
        speichernButton.setOnAction(e -> savePlanned());
        resetButton.setOnAction(e -> resetForm());

        VBox center = new VBox(12, topGrid, new Separator(), new Label("Termine (max. 100):"), rowsBox, actions);
        setCenter(new ScrollPane(center));

        // Defaults
        modusBox.setValue("praesenz");
        teilnehmerTypBox.setValue("studierende");
        if (!inhaltstypBox.getItems().isEmpty()) inhaltstypBox.setValue(inhaltstypBox.getItems().get(0));

        validateForm(); // initial Button-Status
    }

    // ---------- Rows ----------

    private void buildRows() {
        rowsBox.getChildren().clear();
        rows.clear();
        reloadPersonen();

        for (int i = 0; i < MAX_ROWS; i++) {
            RowControls rc = new RowControls(i + 1, personen);
            rows.add(rc);
            rowsBox.getChildren().add(rc.container);
        }

        // Nur erste Zeile sichtbar
        for (int i = 0; i < MAX_ROWS; i++) {
            setRowVisible(i, i == 0);
        }
    }

    private void setRowVisible(int index, boolean visible) {
        RowControls rc = rows.get(index);
        rc.container.setManaged(visible);
        rc.container.setVisible(visible);
    }

    private void maybeRevealNext(int index) {
        if (index + 1 >= MAX_ROWS) return;
        if (rows.get(index).isFilled()) {
            setRowVisible(index + 1, true);
        }
        validateForm();
    }

    // ---------- DB-Backed dropdowns ----------

    private void fillInhaltstypBoxFromDatabase() {
        TrainingTypeDAO dao = new TrainingTypeDAO(Config.getDbPath());
        List<String> types = dao.getAllTrainingTypes();
        inhaltstypBox.getItems().setAll(types);
    }

    private void reloadPersonen() {
        try {
            // 1. Hole alle Personen der letzten 12 Monate aus der DB
            List<String> rawPersonen = new TrainingDAO().getRecentDurchfuehrendePersonen(12);

            // 2. Wandle bekannte Kürzel in Anzeigenamen um
            personen = new ArrayList<>();
            for (String raw : rawPersonen) {
                if (raw == null || raw.isBlank()) continue;
                String display = model.PersonMapping.toName(raw);
                if (!personen.contains(display)) personen.add(display);
            }

            // 3. Sortiere alphabetisch
            Collections.sort(personen);

        } catch (Exception e) {
            personen = new ArrayList<>();
        }

        // 4. Leer-Option vorne (optional)
        if (personen.isEmpty() || !"".equals(personen.get(0))) {
            personen.add(0, "");
        }

        // 5. Aktuellen Nutzer ganz oben einfügen
        String username = System.getProperty("user.name");
        String displayName = model.PersonMapping.toName(username);
        if (!personen.contains(displayName)) personen.add(1, displayName); // direkt hinter Leerzeile
    }

    public void refreshTrainingTypes() {
        fillInhaltstypBoxFromDatabase();
        reloadPersonen();
        // Personen-Combos aktualisieren, aktuelle Eingaben möglichst bewahren
        for (RowControls rc : rows) {
            rc.updatePersonItems(personen);
        }
    }

    // ---------- Titel-Autofill ----------

    private void updateFieldsForVeranstaltungstyp() {
        String typ = veranstaltungstypBox.getValue();
        if (typ == null) return;

        if (typ.equals("fuehrung")) {
            inhaltstypBox.setValue("bibliotheksnutzung");
            inhaltstypBox.setDisable(true);
            titelField.setText("bibliotheksführung");
            if (dauerField.getText().isBlank()) dauerField.setText("45");
            modusBox.setValue("praesenz");
            teilnehmerTypBox.setValue("studierende");
        } else if (typ.equals("schulung")) {
            inhaltstypBox.setDisable(false);
            teilnehmerTypBox.setValue("studierende");
            if (inhaltstypBox.getValue() != null && !inhaltstypBox.getValue().isBlank()) {
                titelField.setText("einführung in die " + inhaltstypBox.getValue());
            } else {
                titelField.clear();
            }
        }
        validateForm();
    }

    private void updateTitleFromInhaltstyp() {
        String inhalt = inhaltstypBox.getValue();
        if (inhalt != null && !inhalt.isBlank()) {
            if (inhalt.equalsIgnoreCase("sonstiges")) {
                titelField.clear();
            } else {
                titelField.setText("einführung in die " + inhalt);
            }
        }
        validateForm();
    }

    // ---------- Save ----------

    private void savePlanned() {
        try {
            if (!validateForm()) return;

            String veranstaltungstyp = veranstaltungstypBox.getValue();
            String inhaltstyp = inhaltstypBox.getValue();
            String titel = titelField.getText();
            Integer dauer = parseIntSafe(dauerField.getText());
            String modus = modusBox.getValue();
            String teilnehmendeTyp = teilnehmerTypBox.getValue();
            String kommentar = kommentarField.getText();

            // Führung -> Inhalt sicherstellen
            if ("fuehrung".equals(veranstaltungstyp)) {
                inhaltstyp = "bibliotheksnutzung";
                if (titel == null || titel.isBlank()) titel = "bibliotheksführung";
            }

            TrainingDAO dao = new TrainingDAO();
            int count = 0;
            for (RowControls rc : rows) {
                if (!rc.container.isVisible()) break;
                if (!rc.isFilled()) break;

                LocalDate datum = rc.datePicker.getValue();
                LocalTime start = rc.getStartTimeOrNull();
                String person = rc.getPersonOrNull(); // <-- nutzt Editor-Text oder Auswahl

                String datumStr = datum.toString();
                String startStr = (start != null) ? start.format(TIME_FORMAT) : null;

                dao.insertTraining(
                        datumStr,
                        startStr,
                        dauer,
                        modus,
                        null,               // teilnehmende_anzahl -> immer NULL bei geplant
                        teilnehmendeTyp,
                        person,
                        "",                 // ausgefallen_grund -> leer
                        titel,
                        inhaltstyp,
                        veranstaltungstyp,
                        kommentar,
                        1,
                        null// geplant = 1
                );
                count++;
            }

            new Alert(Alert.AlertType.INFORMATION, count + " geplante Termine gespeichert.").showAndWait();
            resetForm();

        } catch (Exception ex) {
            new Alert(Alert.AlertType.ERROR, "Fehler beim Speichern: " + ex.getMessage()).showAndWait();
            ex.printStackTrace();
        }
    }

    private void resetForm() {
        veranstaltungstypBox.setValue(null);
        inhaltstypBox.setDisable(false);
        inhaltstypBox.setValue(null);
        titelField.clear();
        dauerField.clear();
        modusBox.setValue(null);
        teilnehmerTypBox.setValue(null);
        kommentarField.clear();

        for (int i = 0; i < rows.size(); i++) {
            rows.get(i).reset();
            setRowVisible(i, i == 0);
        }

        validateForm();
    }

    // ---------- Validation / Helpers ----------

    private void applyIntegerFilter(TextField field) {
        field.textProperty().addListener((obs, old, neu) -> {
            if (!neu.matches("\\d*")) field.setText(neu.replaceAll("[^\\d]", ""));
        });
    }

    private Integer parseIntSafe(String text) {
        if (text == null || text.isBlank()) return null;
        try { return Integer.parseInt(text.trim()); } catch (NumberFormatException e) { return null; }
    }

    /** Gesamtvalidierung: Top-Felder + mindestens eine befüllte Zeile */
    private boolean validateForm() {
        boolean valid = true;

        valid &= mark(veranstaltungstypBox, veranstaltungstypBox.getValue() != null);
        valid &= mark(inhaltstypBox, inhaltstypBox.getValue() != null && !inhaltstypBox.getValue().isBlank());
        valid &= mark(titelField, !titelField.getText().isBlank());
        valid &= mark(dauerField, !dauerField.getText().isBlank());
        valid &= mark(modusBox, modusBox.getValue() != null);
        valid &= mark(teilnehmerTypBox, teilnehmerTypBox.getValue() != null);

        boolean anyRow = rows.get(0).isFilled();
        speichernButton.setDisable(!(valid && anyRow));
        return valid && anyRow;
    }

    private boolean mark(Control c, boolean ok) {
        if (ok) c.setStyle("");
        else c.setStyle("-fx-border-color: red; -fx-border-width: 2;");
        return ok;
    }

    // ---------- RowControls (eine Terminzeile) ----------

    private final class RowControls {
        final HBox container = new HBox(8);
        final Label nrLabel = new Label();
        final DatePicker datePicker = new DatePicker();
        final TimeSpinner timeSpinner = new TimeSpinner();
        final ComboBox<String> personBox = new ComboBox<>();

        RowControls(int nr, List<String> personen) {
            nrLabel.setText(String.format("#%02d", nr));

            // Personen-Combo: jetzt EDITIERBAR (Freitext)
            personBox.getItems().setAll(personen);
            personBox.setEditable(true);
            personBox.setPromptText("Kürzel (abc123)");

            // Standardwert = aktueller Nutzer
            String username = System.getProperty("user.name");
            String displayName = model.PersonMapping.toName(username);
            personBox.setValue(displayName);
            personBox.getEditor().setText(displayName);

            timeSpinner.getValueFactory().setValue(LocalTime.of(10, 0));

            container.getChildren().addAll(
                    nrLabel,
                    new Label("Datum:"), datePicker,
                    new Label("Startzeit:"), timeSpinner,
                    new Label("Durchführende Person:"), personBox
            );

            // Editor-Commit: ENTER/Fokusverlust -> Text ggf. in Items aufnehmen
            personBox.getEditor().setOnAction(e -> commitPersonEditor());
            personBox.focusedProperty().addListener((o, was, isNow) -> {
                if (!isNow) commitPersonEditor();
            });

            // Reveal-Mechanik
            ChangeListener<Object> reveal = (obs, o, n) -> {
                if (isFilled()) {
                    maybeRevealNext(nr - 1);
                }
                validateForm();
            };
            datePicker.valueProperty().addListener(reveal);
            timeSpinner.getEditor().textProperty().addListener(reveal);
        }

        /** Editor-Text in Items übernehmen, falls neu */
        /** Editor-Text in Items übernehmen, falls neu */
        private void commitPersonEditor() {
            String text = personBox.getEditor().getText();
            if (text == null) return;
            String val = text.trim();

            // Leere Eingabe -> leer setzen
            if (val.isEmpty()) {
                personBox.setValue("");
                return;
            }

            // Wenn bereits in der Liste -> akzeptieren
            if (personBox.getItems().contains(val)) {
                personBox.setValue(val);
                return;
            }

            // Wenn neu -> prüfen, ob Kürzel-Format stimmt
            if (!val.matches("^[a-z]{3}\\d{3}$")) {
                new Alert(Alert.AlertType.WARNING,
                        "Ungültiges Kürzel.\nBitte geben Sie ein Kürzel im Format 'abc123' ein (3 Buchstaben + 3 Ziffern).")
                        .showAndWait();
                personBox.getEditor().setText("");
                personBox.setValue("");
                return;
            }

            // Wenn gültig -> hinzufügen
            personBox.getItems().add(val);
            personBox.setValue(val);
        }


        /** Von außen aufrufbar, um Items zu erneuern und aktuelle Eingabe zu bewahren */
        void updatePersonItems(List<String> newItems) {
            // aktuellen Wert (aus Editor oder Auswahl) sichern
            String current = getPersonRaw();
            personBox.getItems().setAll(newItems);
            if (current != null && !current.isBlank() && !personBox.getItems().contains(current)) {
                personBox.getItems().add(current);
            }
            // Wert zurücksetzen (Editor und Selection)
            if (current == null) {
                personBox.setValue("");
                personBox.getEditor().setText("");
            } else {
                personBox.setValue(current);
                personBox.getEditor().setText(current);
            }
        }

        /** true, wenn Datum gesetzt und Startzeit parsebar; Person optional */
        boolean isFilled() {
            if (datePicker.getValue() == null) return false;
            String t = timeSpinner.getEditor().getText();
            if (t == null || t.isBlank()) return false;
            try { LocalTime.parse(t, TIME_FORMAT); } catch (DateTimeParseException e) { return false; }
            return true;
        }

        LocalTime getStartTimeOrNull() {
            try { return LocalTime.parse(timeSpinner.getEditor().getText(), TIME_FORMAT); }
            catch (Exception e) { return null; }
        }

        /** Liefert den aktuellen Personenwert (Editor-Text hat Vorrang), oder null/leer -> null */
        String getPersonOrNull() {
            String raw = getPersonRaw();
            if (raw == null) return null;
            String v = raw.trim();
            return v.isEmpty() ? null : v;
            }

        private String getPersonRaw() {
            String editor = personBox.getEditor().getText();
            if (editor != null && !editor.trim().isEmpty()) return editor.trim();
            String selected = personBox.getValue();
            return selected != null ? selected : null;
        }

        void reset() {
            datePicker.setValue(null);
            timeSpinner.getValueFactory().setValue(LocalTime.of(10, 0));
            personBox.setValue("");
            personBox.getEditor().setText("");
        }
    }
}
