package org.example.petshopdesktop.controllers.dialogcontrollers;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.scene.control.ListCell;

import org.example.petshopdesktop.DTOs.AppointmentDTO;
import org.example.petshopdesktop.api.dto.appointment.AppointmentRequest;
import org.example.petshopdesktop.api.dto.common.DropdownOption;
import org.example.petshopdesktop.api.endpoints.AppointmentApi;
import org.example.petshopdesktop.api.endpoints.DropdownApi;
import org.example.petshopdesktop.auth.UserSession;
import org.example.petshopdesktop.util.ActivityLogger;

import java.time.LocalTime;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;

public class AppointmentDialogController {

    @FXML private Button btnCancel;
    @FXML private Button btnSave;

    @FXML private ComboBox<DropdownOption> cbService;
    @FXML private ComboBox<DropdownOption> cbCustomer;
    @FXML private ComboBox<DropdownOption> cbPet;
    @FXML private ComboBox<DropdownOption> cbEmployee;
    @FXML private ComboBox<DropdownOption> cbStore;
    @FXML private VBox vbStore;

    @FXML private ComboBox<Integer> cbHour;
    @FXML private ComboBox<Integer> cbMinute;

    @FXML private ComboBox<String> cbAppointmentStatus;
    @FXML private DatePicker dpAppointmentDate;

    @FXML private Label lblAppointmentId;
    @FXML private Label lblMode;

    private String mode = null;
    private AppointmentDTO selectedAppointment = null;
    private Long pendingPetSelectionId = null;
    private Long pendingStoreId = null;
    private boolean isOriginallyCancel = false;
    private boolean isOriginallyCompletedOrMissed = false;
    private boolean isEditing = false;

    public void setMode(String mode) {
        this.mode = mode;
        lblMode.setText(mode + " Appointment");
        lblAppointmentId.setVisible(!mode.equals("Add"));
        if (mode.equals("Add")) {
            cbAppointmentStatus.setItems(FXCollections.observableArrayList("Booked"));
            cbAppointmentStatus.setValue("Booked");
            cbAppointmentStatus.setDisable(true);
        }
    }

    @FXML
    public void initialize() {
        cbAppointmentStatus.setItems(FXCollections.observableArrayList("Booked", "Completed", "Missed", "Cancelled"));
        cbAppointmentStatus.valueProperty().addListener((obs, oldVal, newVal) -> {
            if (newVal == null) return;
            boolean lockAll = "Cancelled".equalsIgnoreCase(newVal)
                    || "Completed".equalsIgnoreCase(newVal)
                    || "Missed".equalsIgnoreCase(newVal);
            if (lockAll) {
                cbEmployee.setDisable(true);
                cbHour.setDisable(true);
                cbMinute.setDisable(true);
                dpAppointmentDate.setDisable(true);
                cbStore.setDisable(true);
            } else {
                if (!isOriginallyCancel && !isOriginallyCompletedOrMissed) {
                    cbEmployee.setDisable(false);
                    cbHour.setDisable(false);
                    cbMinute.setDisable(false);
                    dpAppointmentDate.setDisable(false);
                    if (UserSession.getInstance().isAdmin() && !isEditing) cbStore.setDisable(false);
                }
            }
        });
        cbPet.setDisable(true);
        cbEmployee.setPromptText("Select an employee");
        cbPet.setPromptText("Select a customer first");
        cbCustomer.setPromptText("Select a customer");
        cbService.setPromptText("Select a service");
        LocalDate today = LocalDate.now();
        dpAppointmentDate.setValue(today);
        dpAppointmentDate.setDayCellFactory(picker -> new javafx.scene.control.DateCell() {
            @Override
            public void updateItem(LocalDate item, boolean empty) {
                super.updateItem(item, empty);
                setDisable(empty || item.isBefore(today));
            }
        });
        cbAppointmentStatus.setValue("Booked");

        for (int i = 9; i <= 17; i++) {
            cbHour.getItems().add(i);
        }

        cbMinute.getItems().addAll(0, 15, 30, 45);

        cbHour.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(Integer option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : String.valueOf(option));
            }
        });
        cbHour.setButtonCell(new ListCell<>() {
            @Override
            protected void updateItem(Integer option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : String.valueOf(option));
            }
        });

        cbMinute.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(Integer option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : String.format("%02d", option));
            }
        });
        cbMinute.setButtonCell(new ListCell<>() {
            @Override
            protected void updateItem(Integer option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : String.format("%02d", option));
            }
        });

        cbService.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(DropdownOption option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : option.getLabel());
            }
        });
        cbService.setButtonCell(new ListCell<>() {
            @Override
            protected void updateItem(DropdownOption option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : option.getLabel());
            }
        });

        cbCustomer.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(DropdownOption option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : option.getLabel());
            }
        });
        cbCustomer.setButtonCell(new ListCell<>() {
            @Override
            protected void updateItem(DropdownOption option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : option.getLabel());
            }
        });

        cbPet.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(DropdownOption option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : option.getLabel());
            }
        });
        cbPet.setButtonCell(new ListCell<>() {
            @Override
            protected void updateItem(DropdownOption option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : option.getLabel());
            }
        });

        cbEmployee.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(DropdownOption option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : option.getLabel());
            }
        });
        cbEmployee.setButtonCell(new ListCell<>() {
            @Override
            protected void updateItem(DropdownOption option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : option.getLabel());
            }
        });

        cbCustomer.valueProperty().addListener((obs, oldValue, newValue) -> {
            Long customerId = newValue != null ? newValue.getId() : null;
            cbPet.setValue(null);
            cbPet.setItems(FXCollections.observableArrayList());
            cbPet.setDisable(customerId == null || isOriginallyCancel || isOriginallyCompletedOrMissed || isEditing);
            if (customerId != null) {
                cbPet.setPromptText("Loading customer pets...");
                loadCustomerPets(customerId);
            } else {
                cbPet.setPromptText("Select a customer first");
                pendingPetSelectionId = null;
            }
        });

        cbStore.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(DropdownOption option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : option.getLabel());
            }
        });
        cbStore.setButtonCell(new ListCell<>() {
            @Override
            protected void updateItem(DropdownOption option, boolean empty) {
                super.updateItem(option, empty);
                setText(empty || option == null ? null : option.getLabel());
            }
        });

        if (UserSession.getInstance().isAdmin()) {
            vbStore.setVisible(true);
            vbStore.setManaged(true);
            cbEmployee.setDisable(true);
            cbEmployee.setPromptText("Select a store first");
        }

        btnSave.setOnMouseClicked(this::buttonSaveClicked);
        btnCancel.setOnMouseClicked(this::closeStage);

        loadServices();
        loadAppointmentCustomers();
        if (UserSession.getInstance().isAdmin()) {
            loadStores();
        } else {
            loadEmployees();
        }
    }

    public void displayAppointmentDetails(AppointmentDTO appt) {

        selectedAppointment = appt;
        isEditing = true;
        lblAppointmentId.setText("ID: " + appt.getAppointmentId());
        pendingPetSelectionId = appt.getPetId() > 0 ? (long) appt.getPetId() : null;
        pendingStoreId = appt.getStoreId();

        try {
            dpAppointmentDate.setValue(
                    java.time.LocalDate.parse(appt.getAppointmentDate())
            );
        } catch (Exception e) {
            ActivityLogger.getInstance().logException(
                "AppointmentDialogController.displayAppointmentDetails",
                e,
                "Parsing appointment date");
        }

        cbAppointmentStatus.setValue(normalizeAppointmentStatus(appt.getAppointmentStatus()));

        try {
            LocalTime time = LocalTime.parse(appt.getAppointmentTime());
            cbHour.setValue(time.getHour());
            cbMinute.setValue(time.getMinute());
        } catch (Exception e) {
            ActivityLogger.getInstance().logException(
                "AppointmentDialogController.displayAppointmentDetails",
                e,
                "Parsing appointment time");
        }

        applySelectedService();
        applySelectedCustomer();
        applySelectedEmployee();
        applyEditModeLock();
    }

    private void applyEditModeLock() {
        String status = cbAppointmentStatus.getValue();
        isOriginallyCancel = "Cancelled".equalsIgnoreCase(status);
        isOriginallyCompletedOrMissed = "Completed".equalsIgnoreCase(status) || "Missed".equalsIgnoreCase(status);

        if (isOriginallyCancel) {
            cbService.setDisable(true);
            cbCustomer.setDisable(true);
            cbPet.setDisable(true);
            cbEmployee.setDisable(true);
            cbHour.setDisable(true);
            cbMinute.setDisable(true);
            dpAppointmentDate.setDisable(true);
            cbAppointmentStatus.setDisable(true);
            cbAppointmentStatus.setItems(FXCollections.observableArrayList("Cancelled"));
            cbStore.setDisable(true);
            btnSave.setDisable(true);
        } else if (isOriginallyCompletedOrMissed) {
            cbService.setDisable(true);
            cbCustomer.setDisable(true);
            cbPet.setDisable(true);
            cbEmployee.setDisable(true);
            cbHour.setDisable(true);
            cbMinute.setDisable(true);
            dpAppointmentDate.setDisable(true);
            cbStore.setDisable(true);
            cbAppointmentStatus.setDisable(false);
            cbAppointmentStatus.setItems(FXCollections.observableArrayList("Completed", "Missed"));
        } else {
            cbService.setDisable(true);
            cbCustomer.setDisable(true);
            cbPet.setDisable(true);
            cbStore.setDisable(true);
            cbEmployee.setDisable(false);
            cbHour.setDisable(false);
            cbMinute.setDisable(false);
            dpAppointmentDate.setDisable(false);
            cbAppointmentStatus.setDisable(false);
            cbAppointmentStatus.setItems(FXCollections.observableArrayList("Booked", "Cancelled"));
        }
    }

    private void buttonSaveClicked(MouseEvent e) {

        if (cbService.getValue() == null ||
                cbCustomer.getValue() == null ||
                cbPet.getValue() == null ||
                cbEmployee.getValue() == null ||
                dpAppointmentDate.getValue() == null ||
                cbHour.getValue() == null ||
                cbMinute.getValue() == null ||
                cbAppointmentStatus.getValue() == null) {

            showError("All fields are required");
            return;
        }

        LocalTime appointmentTime = LocalTime.of(cbHour.getValue(), cbMinute.getValue());
        Long storeId;
        if (UserSession.getInstance().isAdmin()) {
            if (cbStore.getSelectionModel().getSelectedItem() == null) {
                showError("Store is required.");
                return;
            }
            storeId = cbStore.getSelectionModel().getSelectedItem().getId();
        } else {
            storeId = UserSession.getInstance().getStoreId();
        }
        if (storeId == null || storeId <= 0) {
            showError("Store is not set for this account");
            return;
        }

        AppointmentRequest request = new AppointmentRequest();
        request.setPetId(cbPet.getValue().getId());
        request.setCustomerId(cbCustomer.getValue().getId());
        request.setStoreId(storeId);
        request.setServiceId(cbService.getValue().getId());
        request.setEmployeeId(cbEmployee.getValue().getId());
        request.setAppointmentDate(dpAppointmentDate.getValue());
        request.setAppointmentTime(appointmentTime);
        request.setAppointmentStatus(normalizeAppointmentStatus(cbAppointmentStatus.getValue()));

        new Thread(() -> {
            try {
                if (mode.equals("Add")) {
                    AppointmentApi.getInstance().createAppointment(request);
                } else {
                    AppointmentApi.getInstance().updateAppointment(
                            (long) selectedAppointment.getAppointmentId(),
                            request
                    );
                }

                Platform.runLater(() -> closeStage(e));

            } catch (Exception ex) {
                Platform.runLater(() -> {
                    ActivityLogger.getInstance().logException(
                        "AppointmentDialogController.buttonSaveClicked",
                        ex,
                        "Saving appointment in " + mode + " mode");
                    ex.printStackTrace();
                    showError("Error saving appointment: " + ex.getMessage());
                });
            }
        }).start();
    }

    private void closeStage(MouseEvent e) {
        Stage stage = (Stage) ((Node) e.getSource()).getScene().getWindow();
        stage.close();
    }

    private void showError(String msg) {
        Alert alert = new Alert(Alert.AlertType.ERROR);
        alert.setHeaderText("Input Error");
        alert.setContentText(msg);
        alert.showAndWait();
    }

    private void syncSelectedAppointment() {
        if (selectedAppointment != null) {
            displayAppointmentDetails(selectedAppointment);
        }
    }

    private void applySelectedService() {
        if (selectedAppointment == null || selectedAppointment.getServiceId() <= 0) {
            return;
        }
        DropdownOption selected = findOptionById(cbService.getItems(), (long) selectedAppointment.getServiceId());
        if (selected != null && !Objects.equals(cbService.getValue(), selected)) {
            cbService.setValue(selected);
        }
    }

    private void applySelectedCustomer() {
        if (selectedAppointment == null || selectedAppointment.getCustomerId() <= 0) {
            return;
        }

        DropdownOption selected = findOptionById(cbCustomer.getItems(), (long) selectedAppointment.getCustomerId());
        if (selected != null && !Objects.equals(cbCustomer.getValue(), selected)) {
            cbCustomer.setValue(selected);
        }
    }

    private void applySelectedEmployee() {
        if (selectedAppointment == null || selectedAppointment.getEmployeeId() <= 0) {
            return;
        }
        DropdownOption selected = findOptionById(cbEmployee.getItems(), (long) selectedAppointment.getEmployeeId());
        if (selected != null && !Objects.equals(cbEmployee.getValue(), selected)) {
            cbEmployee.setValue(selected);
        }
    }

    private DropdownOption findOptionById(List<DropdownOption> options, Long id) {
        if (id == null || options == null) {
            return null;
        }
        for (DropdownOption option : options) {
            if (option.getId() != null && option.getId().equals(id)) {
                return option;
            }
        }
        return null;
    }

    private void loadCustomerPets(Long customerId) {
        new Thread(() -> {
            try {
                List<DropdownOption> pets = DropdownApi.getInstance().getCustomerPets(customerId);
                Platform.runLater(() -> {
                    ObservableList<DropdownOption> petOptions = FXCollections.observableArrayList(pets != null ? pets : List.of());
                    if (pendingPetSelectionId != null) {
                        boolean found = petOptions.stream().anyMatch(p -> pendingPetSelectionId.equals(p.getId()));
                        if (!found && selectedAppointment != null && selectedAppointment.getPetId() > 0) {
                            DropdownOption placeholder = new DropdownOption();
                            placeholder.setId((long) selectedAppointment.getPetId());
                            placeholder.setLabel(selectedAppointment.getPetName() != null && !selectedAppointment.getPetName().isBlank()
                                    ? selectedAppointment.getPetName() : "Pet #" + selectedAppointment.getPetId());
                            petOptions.add(0, placeholder);
                        }
                    }
                    cbPet.setItems(petOptions);
                    cbPet.setDisable(petOptions.isEmpty() || isOriginallyCancel || isOriginallyCompletedOrMissed || isEditing);
                    cbPet.setPromptText(petOptions.isEmpty() ? "No pets for selected customer" : "Select a pet");
                    if (pendingPetSelectionId != null) {
                        for (DropdownOption pet : cbPet.getItems()) {
                            if (pet.getId() != null && pet.getId().equals(pendingPetSelectionId)) {
                                cbPet.setValue(pet);
                                break;
                            }
                        }
                        pendingPetSelectionId = null;
                    }
                });
            } catch (Exception ex) {
                Platform.runLater(() -> {
                    ActivityLogger.getInstance().logException(
                            "AppointmentDialogController.loadCustomerPets",
                            ex,
                            "Loading customer pets for appointment dialog");
                    cbPet.setDisable(true);
                    cbPet.setPromptText("Unable to load pets");
                    showError("Error loading pets for selected customer");
                });
            }
        }).start();
    }

    private void loadServices() {
        new Thread(() -> {
            try {
                List<DropdownOption> services = DropdownApi.getInstance().getServices();
                Platform.runLater(() -> {
                    cbService.setItems(FXCollections.observableArrayList(services));
                    applySelectedService();
                });
            } catch (Exception e) {
                Platform.runLater(() -> {
                    ActivityLogger.getInstance().logException(
                            "AppointmentDialogController.loadServices",
                            e,
                            "Loading services for appointment dialog");
                    cbService.setDisable(true);
                    cbService.setPromptText("Unable to load services");
                });
            }
        }).start();
    }

    private void loadAppointmentCustomers() {
        new Thread(() -> {
            try {
                List<DropdownOption> customers = DropdownApi.getInstance().getAppointmentCustomers();
                Platform.runLater(() -> {
                    cbCustomer.setItems(FXCollections.observableArrayList(customers));
                    boolean hasCustomers = customers != null && !customers.isEmpty();
                    cbCustomer.setDisable(!hasCustomers || isOriginallyCancel || isOriginallyCompletedOrMissed || isEditing);
                    cbPet.setDisable(true);
                    cbPet.setItems(FXCollections.observableArrayList());
                    cbCustomer.setPromptText(hasCustomers ? "Select a customer" : "No customers with pets yet");
                    cbPet.setPromptText(hasCustomers ? "Select a customer first" : "No customer pets available");
                    applySelectedCustomer();
                });
            } catch (Exception e) {
                Platform.runLater(() -> {
                    ActivityLogger.getInstance().logException(
                            "AppointmentDialogController.loadAppointmentCustomers",
                            e,
                            "Loading appointment customers for appointment dialog");
                    cbCustomer.setDisable(true);
                    cbPet.setDisable(true);
                    cbCustomer.setPromptText("Unable to load customers");
                    cbPet.setPromptText("Unable to load pets");
                });
            }
        }).start();
    }


    private void loadStores() {
        new Thread(() -> {
            try {
                List<DropdownOption> stores = DropdownApi.getInstance().getStores();
                Platform.runLater(() -> {
                    if (stores != null) {
                        cbStore.setItems(FXCollections.observableArrayList(stores));
                        cbStore.valueProperty().addListener((obs, oldVal, newVal) -> {
                            Long sid = newVal != null ? newVal.getId() : null;
                            cbEmployee.setValue(null);
                            cbEmployee.setItems(FXCollections.observableArrayList());
                            if (sid != null) {
                                if (!isOriginallyCancel && !isOriginallyCompletedOrMissed) {
                                    cbEmployee.setDisable(false);
                                    cbEmployee.setPromptText("Select an employee");
                                }
                                loadEmployeesForStore(sid);
                            } else {
                                if (!isOriginallyCancel && !isOriginallyCompletedOrMissed) {
                                    cbEmployee.setDisable(true);
                                    cbEmployee.setPromptText("Select a store first");
                                }
                            }
                        });
                        if (pendingStoreId != null) {
                            for (DropdownOption store : cbStore.getItems()) {
                                if (pendingStoreId.equals(store.getId())) {
                                    cbStore.setValue(store);
                                    break;
                                }
                            }
                            pendingStoreId = null;
                        }
                    }
                });
            } catch (Exception e) {
                Platform.runLater(() -> ActivityLogger.getInstance().logException(
                        "AppointmentDialogController.loadStores",
                        e,
                        "Loading stores for appointment dialog"));
            }
        }).start();
    }

    private void loadEmployeesForStore(Long storeId) {
        new Thread(() -> {
            try {
                List<DropdownOption> employees = DropdownApi.getInstance().getStoreEmployees(storeId);
                Platform.runLater(() -> {
                    cbEmployee.setItems(FXCollections.observableArrayList(employees));
                    applySelectedEmployee();
                });
            } catch (Exception e) {
                Platform.runLater(() -> {
                    ActivityLogger.getInstance().logException(
                            "AppointmentDialogController.loadEmployeesForStore",
                            e,
                            "Loading employees for store");
                    cbEmployee.setDisable(true);
                    cbEmployee.setPromptText("Unable to load employees");
                });
            }
        }).start();
    }

    private void loadEmployees() {
        new Thread(() -> {
            try {
                Long storeId = UserSession.getInstance().getStoreId();
                List<DropdownOption> employees;
                if (storeId != null && storeId > 0) {
                    employees = DropdownApi.getInstance().getStoreEmployees(storeId);
                } else {
                    employees = DropdownApi.getInstance().getEmployees();
                }
                Platform.runLater(() -> {
                    cbEmployee.setItems(FXCollections.observableArrayList(employees));
                    applySelectedEmployee();
                });
            } catch (Exception e) {
                Platform.runLater(() -> {
                    ActivityLogger.getInstance().logException(
                            "AppointmentDialogController.loadEmployees",
                            e,
                            "Loading employees for appointment dialog");
                    cbEmployee.setDisable(true);
                    cbEmployee.setPromptText("Unable to load employees");
                });
            }
        }).start();
    }

    private String normalizeAppointmentStatus(String status) {
        if (status == null) {
            return "Booked";
        }
        return switch (status.trim().toLowerCase()) {
            case "booked" -> "Booked";
            case "completed" -> "Completed";
            case "missed" -> "Missed";
            case "cancelled", "canceled" -> "Cancelled";
            default -> "Booked";
        };
    }
}
