package ui;

import db.TrainingDAO;
import db.TrainingTypeDAO;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*;
import model.DataModels;
import model.PersonMapping;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Übersicht über alle gespeicherten Trainingsdatensätze.
 * - Filterzeile oben
 * - Tabs pro Jahr mit Summen (Teilnehmende / Termine)
 * - Export-Button pro Jahr (Tabulator-getrennt in Zwischenablage)
 * - Doppelklick auf Zeile -> Edit-Dialog (mit Rechteprüfung)
 * - Zeilen mit geplant=1 sind dezent orange hinterlegt
 */
public class TrainingOverviewUI extends BorderPane {

    private final TabPane yearTabs = new TabPane();

    // --- Filter-Felder ---
    private final TextField titelFilter = new TextField();
    private final ComboBox<String> typFilter = new ComboBox<>();
    private final ComboBox<String> durchfuehrendeFilter = new ComboBox<>();
    private final ComboBox<String> inhaltstypFilter = new ComboBox<>();
    private final ComboBox<String> modusFilter = new ComboBox<>();
    private final ComboBox<String> teilnehmendeTypFilter = new ComboBox<>();
    private final CheckBox ausgefallenCheck = new CheckBox("Nur ausgefallene");
    private final CheckBox ownOnlyCheck = new CheckBox("Nur eigene anzeigen");

    private final Button filterButton = new Button("Filtern");
    private final Button resetButton = new Button("Zurücksetzen");

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

        Label heading = new Label("Schulungs- und Führungsübersicht");
        heading.setStyle("-fx-font-size: 18; -fx-font-weight: bold;");

        // --- Filterzeile ---
        GridPane filterGrid = new GridPane();
        filterGrid.setHgap(10);
        filterGrid.setVgap(5);
        filterGrid.setPadding(new Insets(10, 0, 10, 0));

        int col = 0;
        filterGrid.add(new Label("Titel:"), col++, 0);
        filterGrid.add(titelFilter, col++, 0);

        filterGrid.add(new Label("Typ:"), col++, 0);
        typFilter.getItems().addAll(Stream.concat(
        	    Stream.of(""),
        	    DataModels.getVeranstaltungstypen().stream()
        	).toList());
        filterGrid.add(typFilter, col++, 0);

        filterGrid.add(new Label("Thema:"), col++, 0);
        inhaltstypFilter.setPromptText("alle");
        fillInhaltstypFilter();
        filterGrid.add(inhaltstypFilter, col++, 0);

        filterGrid.add(new Label("Modus:"), col++, 0);
        modusFilter.getItems().addAll(DataModels.getModi());
        filterGrid.add(modusFilter, col++, 0);

        filterGrid.add(new Label("Teilnehmende:"), col++, 0);
        teilnehmendeTypFilter.getItems().addAll(DataModels.getTeilnehmendeTypen());
        filterGrid.add(teilnehmendeTypFilter, col++, 0);
        
     // Durchführende (Mapping)
        filterGrid.add(new Label("Durchführende:"), col++, 0);
        List<String> personenRaw = new TrainingDAO().getAllDurchfuehrendePersonen();
        List<String> personenMapped = new ArrayList<>();
        personenMapped.add(""); // "alle"
        for (String p : personenRaw) {
            if (p == null || p.isBlank()) continue;
            String display = model.PersonMapping.toName(p);
            if (!personenMapped.contains(display)) personenMapped.add(display);
        }
        Collections.sort(personenMapped);
        durchfuehrendeFilter.getItems().setAll(personenMapped);
        filterGrid.add(durchfuehrendeFilter, col++, 0);


        filterGrid.add(ausgefallenCheck, col++, 0);
        filterGrid.add(ownOnlyCheck, col++, 0);

        HBox buttonBox = new HBox(10, filterButton, resetButton);
        filterGrid.add(buttonBox, col, 0);

        VBox topBox = new VBox(10, heading, filterGrid);
        topBox.setPadding(new Insets(10, 0, 10, 0));
        setTop(topBox);

        yearTabs.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
        setCenter(yearTabs);

        filterButton.setOnAction(e -> loadData());
        resetButton.setOnAction(e -> resetFilters());

        // ENTER -> Filtern: TextField & ComboBox feuern Action bei ENTER,
        // CheckBox nicht: daher dort explizit filtern.
        titelFilter.setOnAction(e -> filterButton.fire());
        typFilter.setOnAction(e -> filterButton.fire());
        inhaltstypFilter.setOnAction(e -> filterButton.fire());
        modusFilter.setOnAction(e -> filterButton.fire());
        teilnehmendeTypFilter.setOnAction(e -> filterButton.fire());
        durchfuehrendeFilter.setOnAction(e -> filterButton.fire());
        ausgefallenCheck.setOnAction(e -> filterButton.fire());
        ownOnlyCheck.setOnAction(e -> filterButton.fire());

        loadData();
    }

    // --- DB-Ladevorgänge ------------------------------------------------------

    private void fillInhaltstypFilter() {
        TrainingTypeDAO dao = new TrainingTypeDAO(util.Config.getDbPath());
        List<String> types = dao.getAllTrainingTypes();
        inhaltstypFilter.getItems().add(""); // für "alle"
        inhaltstypFilter.getItems().addAll(types);
    }

    /**
     * Lädt die Daten aus der DB, filtert sie und zeigt sie in Tabs nach Jahr an.
     * Bewahrt (wenn möglich) den zuvor ausgewählten Jahr-Tab.
     */
    private void loadData() {
        // aktuellen Jahr-Tab merken (Jahreszahl vom Tab-Titel extrahieren)
        String selectedYear = null;
        Tab sel = yearTabs.getSelectionModel().getSelectedItem();
        if (sel != null && sel.getText() != null) {
            String t = sel.getText();
            int sp = t.indexOf(' ');
            selectedYear = sp > 0 ? t.substring(0, sp) : t;
        }

        yearTabs.getTabs().clear();

        TrainingDAO dao = new TrainingDAO();
        String selectedDurchfuehrende = durchfuehrendeFilter.getValue();
        if (selectedDurchfuehrende != null && !selectedDurchfuehrende.isBlank()) {
            selectedDurchfuehrende = model.PersonMapping.toKuerzel(selectedDurchfuehrende);
        }

        List<Map<String, Object>> trainings = dao.getTrainingsWithFilters(
            titelFilter.getText(),
            typFilter.getValue(),
            inhaltstypFilter.getValue(),
            modusFilter.getValue(),
            teilnehmendeTypFilter.getValue(),
            selectedDurchfuehrende,
            ausgefallenCheck.isSelected()
        );


        // --- zusätzlicher Filter: Nur eigene anzeigen ---
        if (ownOnlyCheck.isSelected()) {
            final String login = System.getProperty("user.name");
            trainings = trainings.stream()
                    .filter(t -> {
                        Object v = t.get("durchfuehrende_person");
                        return v != null && login.equalsIgnoreCase(String.valueOf(v));
                    })
                    .collect(Collectors.toList());
        }

        if (trainings.isEmpty()) {
            yearTabs.getTabs().add(new Tab("Keine Daten", new Label("Keine passenden Datensätze gefunden.")));
            return;
        }

        // Gruppiere Datensätze nach Jahr
        Map<String, List<Map<String, Object>>> byYear = trainings.stream()
                .filter(t -> t.get("datum") != null)
                .collect(Collectors.groupingBy(t -> t.get("datum").toString().substring(0, 4)));

        // Jahre absteigend sortieren (aktuelles Jahr links)
        List<String> years = new ArrayList<>(byYear.keySet());
        years.sort(Comparator.comparingInt((String y) -> Integer.parseInt(y)).reversed());

        for (String year : years) {
            List<Map<String, Object>> rows = byYear.get(year);

            // --- Nur aktive (nicht ausgefallene) Zeilen berücksichtigen ---
            List<Map<String, Object>> aktive = rows.stream()
                    .filter(r -> {
                        Object g = r.get("ausgefallen_grund");
                        return g == null || String.valueOf(g).isBlank();
                    })
                    .toList();

            // --- Statistiken ---
            int totalTeilnehmer = aktive.stream()
                    .mapToInt(r -> {
                        Object val = r.get("teilnehmende_anzahl");
                        try { return Integer.parseInt(String.valueOf(val)); } catch (Exception e) { return 0; }
                    })
                    .sum();

            int aktiveTermine = aktive.size();
            int aktiveDauerMinuten = aktive.stream()
                    .mapToInt(r -> {
                        Object v = r.get("dauer_minuten");
                        try { return Integer.parseInt(String.valueOf(v)); } catch (Exception e) { return 0; }
                    })
                    .sum();
            double aktiveDauerStunden = Math.round((aktiveDauerMinuten / 60.0) * 10.0) / 10.0;

            long countOnline = aktive.stream()
                    .filter(r -> "online".equalsIgnoreCase(String.valueOf(r.get("modus"))))
                    .count();
            long countPraesenz = aktive.stream()
                    .filter(r -> "praesenz".equalsIgnoreCase(String.valueOf(r.get("modus"))))
                    .count();
            long countSelbstlern = aktive.stream()
                    .filter(r -> "selbstlerneinheit".equalsIgnoreCase(String.valueOf(r.get("modus"))))
                    .count();

            int sumZugriffeSelbstlern = aktive.stream()
                    .filter(r -> "selbstlerneinheit".equalsIgnoreCase(String.valueOf(r.get("modus"))))
                    .mapToInt(r -> {
                        Object val = r.get("teilnehmende_anzahl");
                        try { return Integer.parseInt(String.valueOf(val)); } catch (Exception e) { return 0; }
                    })
                    .sum();

            TableView<Map<String, Object>> table = createTable(rows);

            Button exportButton = new Button("In Zwischenablage (Excel)");
            exportButton.setOnAction(e -> copyTableToClipboard(table));

            Label stats = new Label(String.format(
                    "DBS-Werte für aktuelle Auswahl [BITTE AKTUELLE FILTER BEACHTEN!]:  Teilnehmende: %d   |   Termine: %d   |   Dauer (Std): %.1f   |   Präsenz: %d   |   Online: %d   |   Selbstlern: %d   |   Zugriffe (Selbstlern): %d",
                    totalTeilnehmer, aktiveTermine, aktiveDauerStunden, countPraesenz, countOnline, countSelbstlern, sumZugriffeSelbstlern
            ));
            stats.setStyle("-fx-font-size: 12; -fx-text-fill: #444;");

            ScrollPane scroll = new ScrollPane(table);
            scroll.setFitToWidth(true);
            scroll.setFitToHeight(true);

            VBox content = new VBox(10, exportButton, stats, scroll);
            VBox.setVgrow(scroll, Priority.ALWAYS);
            content.setPadding(new Insets(10));

            // --- Tab-Titel: nur durchgeführte Termine + Teilnehmende anzeigen ---
            String tabTitle = String.format("%s (%d / %d)", year, totalTeilnehmer, aktiveTermine);
            Tab yearTab = new Tab(tabTitle, content);
            yearTabs.getTabs().add(yearTab);
        }

        // nach dem Neuaufbau versuchen, den vorher gewählten Tab wiederherzustellen
        if (selectedYear != null) {
            for (Tab t : yearTabs.getTabs()) {
                if (t.getText() != null && t.getText().startsWith(selectedYear + " ")) {
                    yearTabs.getSelectionModel().select(t);
                    break;
                }
            }
        } else if (!yearTabs.getTabs().isEmpty()) {
            yearTabs.getSelectionModel().select(0);
        }
    }


    private void resetFilters() {
        titelFilter.clear();
        typFilter.setValue("");
        inhaltstypFilter.setValue("");
        modusFilter.setValue("");
        teilnehmendeTypFilter.setValue("");
        durchfuehrendeFilter.setValue("");
        ausgefallenCheck.setSelected(false);
        ownOnlyCheck.setSelected(false);
        loadData();
    }

    // --- Tabellenaufbau -------------------------------------------------------

    private TableView<Map<String, Object>> createTable(List<Map<String, Object>> data) {
        TableView<Map<String, Object>> table = new TableView<>();
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_SUBSEQUENT_COLUMNS);

        Map<String, String> headerToKey = new LinkedHashMap<>(model.DataModels.getCsvToSqliteMapping());

        for (Map.Entry<String, String> e : headerToKey.entrySet()) {
            String header = e.getKey();
            String col = e.getValue();
            TableColumn<Map<String, Object>, String> column = new TableColumn<>(header);
            column.setCellValueFactory(cd -> {
                Map<String, Object> r = cd.getValue();
                Object o = (r != null) ? r.get(col) : null;
                String value = "";

                if (o != null) {
                    if ("durchfuehrende_person".equals(col)) {
                        String mapped = model.PersonMapping.toName(o.toString());
                        value = (mapped != null && !mapped.isBlank()) ? mapped : o.toString();
                    } 
                    else if ("evaluation".equals(col)) {
                        // --- Hide evaluation if not own dataset ---
                        String login = System.getProperty("user.name");
                        String loginKuerzel = model.PersonMapping.normalizeLogin(login);
                        Object personObj = r.get("durchfuehrende_person");
                        String personKuerzel = (personObj != null) 
                                ? model.PersonMapping.normalizeLogin(personObj.toString()) 
                                : null;

                        boolean isOwn = personKuerzel != null && loginKuerzel.equalsIgnoreCase(personKuerzel);
                        if (isOwn || model.PersonMapping.isAdmin(login)) {
                            value = o.toString(); // show
                        } else {
                            value = ""; // hide
                        }
                    } 
                    else {
                        value = o.toString();
                    }
                }

                return new javafx.beans.property.ReadOnlyStringWrapper(value);
            });


            // ID-Spalte einfach rendern
            if (col.equals("id")) {
                column.setCellFactory(colParam -> new TableCell<>() {
                    @Override
                    protected void updateItem(String item, boolean empty) {
                        super.updateItem(item, empty);
                        if (empty || item == null) {
                            setText(null);
                            setStyle("");
                            return;
                        }
                        setText(item);
                    }
                });
            }

            // Spaltenbreiten
            double weight = switch (col) {
                case "id" -> 0.5;
                case "datum" -> 0.8;
                case "startzeit" -> 0.6;
                case "titel" -> 2.0;
                case "dauer_minuten" -> 0.6;
                case "inhaltstyp" -> 1.0;
                case "modus" -> 0.7;
                case "teilnehmende_typ" -> 1.0;
                case "teilnehmende_anzahl" -> 0.7;
                case "veranstaltungstyp" -> 0.8;
                case "durchfuehrende_person" -> 1.0;
                case "ausgefallen_grund" -> 0.8;
                case "kommentar" -> 2.0;
                default -> 1.0;
            };
            column.setUserData(weight);
            table.getColumns().add(column);
        }

        // Dynamische Spaltenbreiten
        table.widthProperty().addListener((obs, oldW, newW) -> {
            double totalWeight = table.getColumns().stream()
                    .mapToDouble(c -> (double) c.getUserData())
                    .sum();
            double available = newW.doubleValue() - 20;
            for (TableColumn<Map<String, Object>, ?> c : table.getColumns()) {
                double w = (double) c.getUserData() / totalWeight * available;
                c.setPrefWidth(w);
            }
        });

        ObservableList<Map<String, Object>> observableData = FXCollections.observableArrayList(data);
        table.setItems(observableData);

        // --- Zeilenaufbau ---
        table.setRowFactory(tv -> {
            TableRow<Map<String, Object>> r = new TableRow<>();

            // Doppelklick -> Edit-Dialog mit Rechteprüfung
            r.setOnMouseClicked(ev -> {
                if (ev.getClickCount() == 2 && !r.isEmpty()) {
                    Map<String, Object> row = r.getItem();
                    if (!canEdit(row)) {
                        new Alert(Alert.AlertType.INFORMATION,
                                "Keine Berechtigung zum Bearbeiten.").showAndWait();
                        return;
                    }

                    List<String> modi = model.DataModels.getModi();
                    List<String> teilnTypen = model.DataModels.getTeilnehmendeTypen();
                    List<String> veranstaltungstypen = model.DataModels.getVeranstaltungstypen();
                    List<String> inhaltstypen = new db.TrainingTypeDAO(util.Config.getDbPath()).getAllTrainingTypes();
                    List<String> personen = new db.TrainingDAO().getAllDurchfuehrendePersonen();

                    ui.TrainingEditDialog dlg = new ui.TrainingEditDialog(
                            row, modi, teilnTypen, veranstaltungstypen, inhaltstypen, personen
                    );
                    Optional<Boolean> ok = dlg.showAndWait();
                    if (ok.isPresent() && ok.get()) {
                        int idx = table.getItems().indexOf(row);
                        if (idx >= 0) {
                            Map<String, Object> updated = new LinkedHashMap<>(row);
                            table.getItems().set(idx, updated);
                        }
                        table.refresh();
                    }
                }
            });

            // Farb-Logik
            r.itemProperty().addListener((obs, oldItem, newItem) -> updateRowStyle(r, newItem));
            r.selectedProperty().addListener((obs, oldSel, newSel) -> updateRowStyle(r, r.getItem()));

            return r;
        });

        table.refresh();
        return table;
    }

    // ------------------------------------------------------------
    // Zeilenstil-Logik
    // ------------------------------------------------------------
    private void updateRowStyle(TableRow<Map<String, Object>> r, Map<String, Object> newItem) {
        if (newItem == null) {
            r.setStyle("");
            return;
        }

        Object dateObj = newItem.get("datum");
        Object teiln = newItem.get("teilnehmende_anzahl");
        Object ausgefallen = newItem.get("ausgefallen_grund");

        boolean noParticipants = (teiln == null || teiln.toString().isBlank() || teiln.toString().equals("0"));
        boolean notCancelled = (ausgefallen == null || ausgefallen.toString().isBlank());

        String baseStyle = "";
        if (noParticipants && notCancelled && dateObj != null) {
            try {
                java.time.LocalDate date = java.time.LocalDate.parse(dateObj.toString());
                java.time.LocalDate today = java.time.LocalDate.now();

                if (date.isAfter(today)) {
                    baseStyle = "-fx-background-color: #FFE5CC; -fx-font-weight: bold;";
                } else if (date.isBefore(today)) {
                    baseStyle = "-fx-background-color: #FFCCCC; -fx-font-weight: bold;";
                }
            } catch (Exception ignored) {}
        }

        // Bei Selektion – standardmäßig blau
        if (r.isSelected()) {
            r.setStyle("-fx-background-color: -fx-focus-color; -fx-text-fill: white; -fx-font-weight: bold;");
        } else {
            r.setStyle(baseStyle);
        }
    }

    // ------------------------------------------------------------
    // Rechteprüfung
    // ------------------------------------------------------------
    private boolean canEdit(Map<String, Object> row) {
        String login = System.getProperty("user.name");
        if (model.PersonMapping.isAdmin(login)) return true;

        Object p = row.get("durchfuehrende_person");
        return p != null && model.PersonMapping.normalizeLogin(login)
                .equalsIgnoreCase(String.valueOf(p));
    }


        
    public void reloadAllData() {
        loadData();
    }

    private boolean isPlanned(Map<String, Object> row) {
        Object v = row.get("geplant");
        if (v == null) return false;
        try {
            if (v instanceof Number n) return n.intValue() == 1;
            return Integer.parseInt(String.valueOf(v)) == 1;
        } catch (Exception e) {
            return false;
        }
    }


    // --- Exportfunktion -------------------------------------------------------

    private void copyTableToClipboard(TableView<Map<String, Object>> table) {
        if (table.getItems().isEmpty()) {
            new Alert(Alert.AlertType.INFORMATION, "Keine Daten zum Kopieren vorhanden.").showAndWait();
            return;
        }

        StringBuilder sb = new StringBuilder();

        // Mapping zwischen sichtbaren Headern und internen Keys
        Map<String, String> headerToKey = model.DataModels.getCsvToSqliteMapping();


        // Überschriften
        List<String> headers = table.getColumns().stream()
                .map(TableColumn::getText)
                .toList();
        sb.append(String.join("\t", headers)).append("\n");

        // Zeilen exportieren
        for (Map<String, Object> row : table.getItems()) {
            List<String> cells = new ArrayList<>();
            for (String header : headers) {
                String key = headerToKey.get(header);
                Object v = key != null ? row.get(key) : null;
                cells.add(v != null ? v.toString() : "");
            }
            sb.append(String.join("\t", cells)).append("\n");
        }

        final javafx.scene.input.ClipboardContent content = new javafx.scene.input.ClipboardContent();
        content.putString(sb.toString());
        javafx.scene.input.Clipboard.getSystemClipboard().setContent(content);

        new Alert(Alert.AlertType.INFORMATION, "Tabelleninhalt in Zwischenablage kopiert.").showAndWait();
    }

}
