package org.example.petshopdesktop.controllers;

import javafx.application.Platform;
import org.example.petshopdesktop.auth.UserSession;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Modality;
import javafx.stage.Stage;
import org.example.petshopdesktop.api.dto.adoption.AdoptionResponse;
import org.example.petshopdesktop.api.dto.common.DropdownOption;
import org.example.petshopdesktop.api.endpoints.AdoptionApi;
import org.example.petshopdesktop.api.endpoints.DropdownApi;
import org.example.petshopdesktop.controllers.dialogcontrollers.AdoptionDialogController;
import org.example.petshopdesktop.models.Adoption;
import org.example.petshopdesktop.ui.CalendarPane;
import org.example.petshopdesktop.util.ActivityLogger;
import org.example.petshopdesktop.util.TableViewSupport;

import java.io.IOException;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class AdoptionController {

    @FXML
    private Button btnAdd;

    @FXML
    private Button btnDelete;

    @FXML
    private Button btnEdit;

    @FXML
    private Button btnRefresh;

    @FXML
    private Label lblStatus;

    @FXML
    private TableColumn<Adoption, Integer> colAdoptionId;

    @FXML
    private TableColumn<Adoption, String> colPetId;

    @FXML
    private TableColumn<Adoption, String> colCustomerName;

    @FXML
    private TableColumn<Adoption, String> colEmployeeName;

    @FXML
    private TableColumn<Adoption, String> colAdoptionDate;

    @FXML
    private TableColumn<Adoption, Double> colAdoptionFee;

    @FXML
    private TableColumn<Adoption, String> colAdoptionStatus;

    @FXML
    private TableColumn<Adoption, String> colStoreName;

    @FXML
    private TableView<Adoption> tvAdoptions;

    @FXML
    private TextField txtSearch;

    @FXML
    private ComboBox<String> cbStatusFilter;

    @FXML
    private ComboBox<DropdownOption> cbStoreFilter;

    private final java.util.ArrayList<DropdownOption> storeOptions = new java.util.ArrayList<>();

    @FXML
    private CalendarPane calendarPane;
    private LocalDate selectedCalendarDate = null;
    private FilteredList<Adoption> filteredAdoptions;

    private ObservableList<Adoption> data = FXCollections.observableArrayList();
    private String mode = null;

    @FXML
    void initialize() {
        btnEdit.setDisable(true);
        btnDelete.setDisable(true);
        
        tvAdoptions.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE);

        colAdoptionId.setCellValueFactory(new PropertyValueFactory<>("adoptionId"));
        colPetId.setCellValueFactory(new PropertyValueFactory<>("petName"));
        colCustomerName.setCellValueFactory(new PropertyValueFactory<>("customerName"));
        colEmployeeName.setCellValueFactory(new PropertyValueFactory<>("employeeName"));
        colAdoptionDate.setCellValueFactory(new PropertyValueFactory<>("adoptionDate"));
        colAdoptionFee.setCellValueFactory(new PropertyValueFactory<>("adoptionFee"));
        colAdoptionStatus.setCellValueFactory(new PropertyValueFactory<>("adoptionStatus"));
        colStoreName.setCellValueFactory(new PropertyValueFactory<>("storeName"));
        TableViewSupport.applyCurrencyColumn(colAdoptionFee);

        filteredAdoptions = new FilteredList<>(data, a -> true);
        TableViewSupport.bindSortedItems(tvAdoptions, filteredAdoptions);

        cbStatusFilter.setItems(FXCollections.observableArrayList("All Statuses", "Pending", "Completed", "Cancelled"));
        cbStatusFilter.getSelectionModel().selectFirst();
        cbStatusFilter.valueProperty().addListener((obs, o, n) -> applyFilterPredicate());

        if (UserSession.getInstance().isAdmin()) {
            cbStoreFilter.setVisible(true);
            cbStoreFilter.setManaged(true);
            loadStoreFilter();
        }
        cbStoreFilter.valueProperty().addListener((obs, o, n) -> displayAdoptions());

        displayAdoptions();
        TableViewSupport.installDoubleClickAction(tvAdoptions, selected -> openDialog(selected, "Edit"));

        tvAdoptions.getSelectionModel().selectedItemProperty().addListener(
                (observable, oldValue, newValue) -> {
                    boolean hasSelection = newValue != null;
                    btnEdit.setDisable(!hasSelection);
                    btnDelete.setDisable(!hasSelection);
                });

        txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
            displayFilteredAdoptions(newValue);
        });

        calendarPane.setOnDateSelected(date -> {
            selectedCalendarDate = date;
            applyFilterPredicate();
        });

        tvAdoptions.setOnKeyPressed(event -> {
            if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
                if (tvAdoptions.getSelectionModel().getSelectedItem() != null) {
                    btnDeleteClicked(null);
                }
            }
        });
    }

    @FXML
    void btnRefresh(ActionEvent event) {
        txtSearch.clear();
        cbStatusFilter.getSelectionModel().selectFirst();
        cbStoreFilter.getSelectionModel().selectFirst();
        selectedCalendarDate = null;
        if (filteredAdoptions != null) filteredAdoptions.setPredicate(a -> true);
        tvAdoptions.getSortOrder().clear();
        displayAdoptions();
        TableViewSupport.flashStatus(lblStatus, "Refreshed");
    }

    @FXML
    void btnAddClicked(ActionEvent event) {
        mode = "Add";
        openDialog(null, mode);
    }

    @FXML
    void btnDeleteClicked(ActionEvent event) {
        
        var selectedAdoptions = tvAdoptions.getSelectionModel().getSelectedItems();
        if (selectedAdoptions.isEmpty()) return;

        Alert question = new Alert(Alert.AlertType.CONFIRMATION);
        question.setHeaderText("Please confirm delete");
        String message = selectedAdoptions.size() == 1
            ? "Are you sure you want to delete this adoption record?"
            : "Are you sure you want to delete " + selectedAdoptions.size() + " adoption records?";
        question.setContentText(message);
        question.getDialogPane().lookupButton(ButtonType.OK).requestFocus();
        Optional<ButtonType> result = question.showAndWait();

        if (result.isPresent() && result.get() == ButtonType.OK) {
            List<Long> ids = selectedAdoptions.stream()
                .map(a -> (long) a.getAdoptionId())
                .collect(Collectors.toList());

            try {
                AdoptionApi.getInstance().deleteAdoptions(ids);
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.setHeaderText("Database Operation Confirmed");
                alert.setContentText("Successfully deleted " + ids.size() + " adoption record(s)");
                alert.showAndWait();
            } catch (Exception e) {
                ActivityLogger.getInstance().logException(
                    "AdoptionController.btnDeleteClicked",
                    e,
                    "Deleting adoptions");
                Alert alert = new Alert(Alert.AlertType.ERROR);
                alert.setHeaderText("Delete Operation Failed");
                alert.setContentText(e.getMessage());
                alert.showAndWait();
            }

            displayAdoptions();
            btnDelete.setDisable(true);
            btnEdit.setDisable(true);
            txtSearch.setText("");
        }
    }

    @FXML
    void btnEditClicked(ActionEvent event) {
        Adoption selectedAdoption = tvAdoptions.getSelectionModel().getSelectedItem();

        if (selectedAdoption != null) {
            mode = "Edit";
            openDialog(selectedAdoption, mode);
        }
    }

    private void displayFilteredAdoptions(String filter) {
        if (txtSearch.getText() == null || txtSearch.getText().isEmpty()) {
            displayAdoptions();
        } else {
            new Thread(() -> {
                try {
                    Long storeId = selectedStoreId();
                    List<AdoptionResponse> adoptions = AdoptionApi.getInstance().listAdoptions(filter, storeId);
                    List<Adoption> adoptionList = adoptions.stream()
                        .map(this::mapToAdoption)
                        .sorted(Comparator.comparing(Adoption::getAdoptionDate).reversed())
                        .collect(Collectors.toList());

                    Platform.runLater(() -> {
                        data.setAll(adoptionList);
                        Set<LocalDate> dates = adoptionList.stream()
                            .filter(a -> a.getAdoptionDate() != null && !a.getAdoptionDate().isEmpty())
                            .map(a -> { try { return LocalDate.parse(a.getAdoptionDate()); } catch (Exception ex) { return null; } })
                            .filter(d -> d != null)
                            .collect(java.util.stream.Collectors.toSet());
                        calendarPane.setEventDates(dates);
                        applyFilterPredicate();
                    });
                } catch (Exception e) {
                    Platform.runLater(() -> {
                        System.out.println("Error while fetching table data: " + e.getMessage());
                        ActivityLogger.getInstance().logException(
                            "AdoptionController.displayFilteredAdoptions",
                            e,
                            "Filtering adoptions with filter: " + filter);
                    });
                }
            }).start();
        }
    }

    private void displayAdoptions() {
        new Thread(() -> {
            try {
                Long storeId = UserSession.getInstance().isAdmin() ? null : UserSession.getInstance().getStoreId();
                List<AdoptionResponse> adoptions = AdoptionApi.getInstance().listAdoptions(null, storeId);
                List<Adoption> adoptionList = adoptions.stream()
                    .map(this::mapToAdoption)
                    .sorted(Comparator.comparing(Adoption::getAdoptionDate).reversed())
                    .collect(Collectors.toList());

                Platform.runLater(() -> {
                    data.setAll(adoptionList);
                    Set<LocalDate> dates = adoptionList.stream()
                        .filter(a -> a.getAdoptionDate() != null && !a.getAdoptionDate().isEmpty())
                        .map(a -> { try { return LocalDate.parse(a.getAdoptionDate()); } catch (Exception ex) { return null; } })
                        .filter(d -> d != null)
                        .collect(java.util.stream.Collectors.toSet());
                    calendarPane.setEventDates(dates);
                    if (selectedCalendarDate != null) {
                        filteredAdoptions.setPredicate(a -> a.getAdoptionDate().equals(selectedCalendarDate.toString()));
                    }
                });
            } catch (Exception e) {
                Platform.runLater(() -> {
                    System.out.println("Error while fetching table data: " + e.getMessage());
                    ActivityLogger.getInstance().logException(
                        "AdoptionController.displayAdoptions",
                        e,
                        "Fetching adoption data for table display");
                });
            }
        }).start();
    }

    private void openDialog(Adoption adoption, String mode) {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(
                "/org/example/petshopdesktop/dialogviews/adoption-dialog-view.fxml"));
        Scene scene = null;
        try {
            scene = new Scene(fxmlLoader.load());
        } catch (IOException e) {
            ActivityLogger.getInstance().logException(
                "AdoptionController.openDialog",
                e,
                "Loading adoption dialog in " + mode + " mode");
            throw new RuntimeException(e);
        }

        AdoptionDialogController dialogController = fxmlLoader.getController();
        dialogController.setMode(mode);

        if (mode.equals("Edit")) {
            dialogController.displayAdoptionDetails(adoption);
        }

        Stage dialogStage = new Stage();
        dialogStage.initModality(Modality.APPLICATION_MODAL);
        dialogStage.setTitle(mode.equals("Add") ? "Add Adoption" : "Edit Adoption");
        dialogStage.setScene(scene);
        dialogStage.showAndWait();

        displayAdoptions();
        btnDelete.setDisable(true);
        btnEdit.setDisable(true);
        txtSearch.setText("");
    }

    private void loadStoreFilter() {
        new Thread(() -> {
            try {
                List<DropdownOption> stores = DropdownApi.getInstance().getStores();
                DropdownOption allStores = new DropdownOption();
                allStores.setLabel("All Stores");
                Platform.runLater(() -> {
                    storeOptions.clear();
                    storeOptions.addAll(stores);
                    java.util.List<DropdownOption> items = new java.util.ArrayList<>();
                    items.add(allStores);
                    items.addAll(stores);
                    cbStoreFilter.setItems(FXCollections.observableArrayList(items));
                    cbStoreFilter.getSelectionModel().selectFirst();
                });
            } catch (Exception e) {
                ActivityLogger.getInstance().logException("AdoptionController.loadStoreFilter", e, "Loading store filter");
            }
        }).start();
    }

    private Long selectedStoreId() {
        if (!UserSession.getInstance().isAdmin()) return UserSession.getInstance().getStoreId();
        DropdownOption selected = cbStoreFilter.getValue();
        return (selected != null && selected.getId() != null) ? selected.getId() : null;
    }

    private void applyFilterPredicate() {
        String selectedStatus = cbStatusFilter.getValue();
        filteredAdoptions.setPredicate(a -> {
            boolean dateMatch = selectedCalendarDate == null
                || a.getAdoptionDate().equals(selectedCalendarDate.toString());
            boolean statusMatch = selectedStatus == null || selectedStatus.equals("All Statuses")
                || a.getAdoptionStatus().equalsIgnoreCase(selectedStatus);
            return dateMatch && statusMatch;
        });
    }

    private Adoption mapToAdoption(AdoptionResponse response) {
        return new Adoption(
            response.getAdoptionId().intValue(),
            response.getPetId() != null ? response.getPetId().intValue() : 0,
            response.getCustomerId() != null ? response.getCustomerId().intValue() : 0,
            response.getEmployeeId() != null ? response.getEmployeeId().intValue() : 0,
            response.getPetName(),
            response.getCustomerName(),
            response.getEmployeeName(),
            response.getAdoptionDate() != null ? response.getAdoptionDate().toString() : "",
            response.getAdoptionFee() != null ? response.getAdoptionFee().doubleValue() : 0.0,
            response.getAdoptionStatus(),
            response.getSourceStoreName(),
            response.getSourceStoreId()
        );
    }
}
