package org.example.petshopdesktop.controllers.dialogcontrollers;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import org.example.petshopdesktop.DTOs.ProductSupplierDTO;
import org.example.petshopdesktop.Validator;
import org.example.petshopdesktop.api.dto.common.DropdownOption;
import org.example.petshopdesktop.api.dto.productsupplier.ProductSupplierRequest;
import org.example.petshopdesktop.api.dto.productsupplier.ProductSupplierResponse;
import org.example.petshopdesktop.api.endpoints.DropdownApi;
import org.example.petshopdesktop.api.endpoints.ProductSupplierApi;
import org.example.petshopdesktop.util.ActivityLogger;

import java.math.BigDecimal;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ProductSupplierDialogController {

    @FXML
    private Button btnCancel;

    @FXML
    private Button btnSave;

    @FXML
    private ComboBox<DropdownOption> cbProduct;

    @FXML
    private ComboBox<DropdownOption> cbSupplier;

    @FXML
    private Label lblMode;

    @FXML
    private Label lblProductSupplierId;

    @FXML
    private TextField txtCost;

    private String mode = null;
    private int selectedSupId = -1;
    private int selectedProdId = -1;
    private Long pendingSupplierId = null;
    private Long pendingProductId = null;
    private boolean updatingChoices = false;
    private ObservableList<DropdownOption> baseSuppliers = FXCollections.observableArrayList();
    private ObservableList<DropdownOption> baseProducts = FXCollections.observableArrayList();
    private List<ProductSupplierResponse> relations = List.of();

    /**
     * add event listeners to buttons and set up combobox
     */
    @FXML
    void initialize() {        //Set up mouse handlers for buttons
        btnSave.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                buttonSaveClicked(mouseEvent);
            }
        });

        btnCancel.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                closeStage(mouseEvent);
            }
        });

        cbSupplier.setButtonCell(new ListCell<DropdownOption>() {
            @Override
            protected void updateItem(DropdownOption item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText(null);
                } else {
                    setText(item.getLabel());
                }
            }
        });
        cbSupplier.setCellFactory(lv -> new ListCell<DropdownOption>() {
            @Override
            protected void updateItem(DropdownOption item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText(null);
                } else {
                    setText(item.getLabel());
                }
            }
        });

        cbProduct.setButtonCell(new ListCell<DropdownOption>() {
            @Override
            protected void updateItem(DropdownOption item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText(null);
                } else {
                    setText(item.getLabel());
                }
            }
        });
        cbProduct.setCellFactory(lv -> new ListCell<DropdownOption>() {
            @Override
            protected void updateItem(DropdownOption item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText(null);
                } else {
                    setText(item.getLabel());
                }
            }
        });

        cbProduct.valueProperty().addListener((obs, oldValue, newValue) -> {
            if (updatingChoices) {
                return;
            }
            filterSuppliersByProduct(newValue != null ? newValue.getId() : null);
        });

        cbSupplier.valueProperty().addListener((obs, oldValue, newValue) -> {
            if (updatingChoices) {
                return;
            }
            filterProductsBySupplier(newValue != null ? newValue.getId() : null);
        });

        new Thread(() -> {
            try {
                var productSupplierLinks = ProductSupplierApi.getInstance().listProductSuppliers(null);
                var suppliers = DropdownApi.getInstance().getSuppliers();
                var products = DropdownApi.getInstance().getProducts();

                Platform.runLater(() -> {
                    relations = productSupplierLinks != null ? productSupplierLinks : List.of();
                    if (suppliers != null) {
                        baseSuppliers = FXCollections.observableArrayList(suppliers);
                        cbSupplier.setItems(FXCollections.observableArrayList(baseSuppliers));
                        applyPendingSupplierSelection();
                    }
                    if (products != null) {
                        baseProducts = FXCollections.observableArrayList(products);
                        cbProduct.setItems(FXCollections.observableArrayList(baseProducts));
                        applyPendingProductSelection();
                    }
                    applyCurrentFilters();
                });
            } catch (Exception e) {
                Platform.runLater(() -> {
                    ActivityLogger.getInstance().logException(
                        "ProductSupplierDialogController.initialize",
                        e,
                        "Loading suppliers and products for combo boxes");
                    Alert alert = new Alert(Alert.AlertType.ERROR);
                    alert.setHeaderText("Initialization Error");
                    alert.setContentText("Failed to load dropdown data: " + e.getMessage());
                    alert.showAndWait();
                });
            }
        }).start();

    }

    /**
     * Validates the inputs, then add or update the database depending
     * on the mode
     * @param mouseEvent click event for save button
     */
    private void buttonSaveClicked(MouseEvent mouseEvent) {
        String errorMsg = "";

        errorMsg += Validator.isPresent(txtCost.getText(), "Cost");
        if (cbProduct.getSelectionModel().getSelectedItem() == null) {
            errorMsg += "Product is required \n";
        }
        if (cbSupplier.getSelectionModel().getSelectedItem() == null) {
            errorMsg += "Supplier is required \n";
        }

        errorMsg += Validator.isLessThanVarChars(txtCost.getText(), "Cost", 12);
        errorMsg += Validator.isNonNegativeDouble(txtCost.getText(), "Cost");

        if(errorMsg.isEmpty()){
            ProductSupplierRequest request = collectProductSupplierRequest();

            new Thread(() -> {
                try {
                    if (mode.equals("Add")) {
                        ProductSupplierApi.getInstance().createProductSupplier(request);
                    } else {
                        ProductSupplierApi.getInstance().updateProductSupplier((long) selectedProdId, (long) selectedSupId, request);
                    }

                    Platform.runLater(() -> {
                        Alert alert = new Alert(Alert.AlertType.INFORMATION);
                        alert.setHeaderText("Saved");
                        alert.setContentText(mode + " succeeded");
                        alert.showAndWait();
                        closeStage(mouseEvent);
                    });
                } catch (Exception e) {
                    Platform.runLater(() -> {
                        ActivityLogger.getInstance().logException(
                            "ProductSupplierDialogController.buttonSaveClicked",
                            e,
                            mode + " product-supplier");
                        Alert alert = new Alert(Alert.AlertType.ERROR);
                        alert.setHeaderText("Database Operation Error");
                        alert.setContentText(mode + " failed: " + e.getMessage());
                        alert.showAndWait();
                    });
                }
            }).start();
        } else {
            Alert alert = new Alert(Alert.AlertType.ERROR);
            alert.setHeaderText("Input Error");
            alert.setContentText(errorMsg);
            alert.showAndWait();
        }
    }

    /**
     * collect the data for new/updated productSupplier
     * @return productSupplier request with data
     */
    private ProductSupplierRequest collectProductSupplierRequest() {
        ProductSupplierRequest request = new ProductSupplierRequest();
        request.setSupplierId(cbSupplier.getSelectionModel().getSelectedItem().getId());
        request.setProductId(cbProduct.getSelectionModel().getSelectedItem().getId());
        try {
            request.setCost(new BigDecimal(txtCost.getText()));
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("Invalid cost format");
        }
        return request;
    }

    /**
     * Display the productsupplier data in text fields and combobox
     * @param productSupplier
     */
    public void displayProductSupplierDetails(ProductSupplierDTO productSupplier){
        if (productSupplier == null) {
            return;
        }
        txtCost.setText(productSupplier.getCost() + "");
        pendingProductId = (long) productSupplier.getProdId();
        pendingSupplierId = (long) productSupplier.getSupId();
        applyPendingProductSelection();
        applyPendingSupplierSelection();
    }

    /**
     * Close the window
     * @param mouseEvent mouse event to close
     */
    private void closeStage(MouseEvent mouseEvent) {
        Node node = (Node) mouseEvent.getSource();
        Stage stage = (Stage) node.getScene().getWindow();
        stage.close();
    }

    /**
     * Set the mode of the dialog
     * @param mode the mode for the dialog
     */
    public void setMode(String mode) {
        this.mode = mode;
        lblMode.setText(mode + " Product-Supplier");
        lblProductSupplierId.setVisible(false);
    }

    /**
     * set the current supplier and productId for (needed for update since compound primary key)
     * @param supId supplier id
     * @param prodId product id
     */
    public void setSelectedIds(int supId, int prodId){
        this.selectedSupId = supId;
        this.selectedProdId = prodId;
    }

    private void applyPendingProductSelection() {
        if (pendingProductId == null) {
            return;
        }
        DropdownOption product = findOptionById(cbProduct.getItems(), pendingProductId);
        if (product != null) {
            updatingChoices = true;
            cbProduct.getSelectionModel().select(product);
            updatingChoices = false;
            filterSuppliersByProduct(product.getId());
            pendingProductId = null;
        }
    }

    private void applyPendingSupplierSelection() {
        if (pendingSupplierId == null) {
            return;
        }
        DropdownOption supplier = findOptionById(cbSupplier.getItems(), pendingSupplierId);
        if (supplier != null) {
            updatingChoices = true;
            cbSupplier.getSelectionModel().select(supplier);
            updatingChoices = false;
            filterProductsBySupplier(supplier.getId());
            pendingSupplierId = null;
        }
    }

    private void applyCurrentFilters() {
        DropdownOption selectedProduct = cbProduct.getSelectionModel().getSelectedItem();
        DropdownOption selectedSupplier = cbSupplier.getSelectionModel().getSelectedItem();
        filterSuppliersByProduct(selectedProduct != null ? selectedProduct.getId() : null);
        filterProductsBySupplier(selectedSupplier != null ? selectedSupplier.getId() : null);
    }

    private void filterSuppliersByProduct(Long productId) {
        updatingChoices = true;
        try {
            if (productId == null) {
                cbSupplier.setItems(FXCollections.observableArrayList(baseSuppliers));
                return;
            }
            Set<Long> allowedSupplierIds = new HashSet<>();
            for (ProductSupplierResponse relation : relations) {
                if (relation.getProductId() != null && relation.getProductId().equals(productId) && relation.getSupplierId() != null) {
                    allowedSupplierIds.add(relation.getSupplierId());
                }
            }
            ObservableList<DropdownOption> filtered = FXCollections.observableArrayList(
                baseSuppliers.stream().filter(option -> option.getId() != null && allowedSupplierIds.contains(option.getId())).toList()
            );
            cbSupplier.setItems(filtered);
            DropdownOption selectedSupplier = cbSupplier.getSelectionModel().getSelectedItem();
            if (selectedSupplier != null && !allowedSupplierIds.contains(selectedSupplier.getId())) {
                cbSupplier.getSelectionModel().clearSelection();
            }
        } finally {
            updatingChoices = false;
        }
    }

    private void filterProductsBySupplier(Long supplierId) {
        updatingChoices = true;
        try {
            if (supplierId == null) {
                cbProduct.setItems(FXCollections.observableArrayList(baseProducts));
                return;
            }
            Set<Long> allowedProductIds = new HashSet<>();
            for (ProductSupplierResponse relation : relations) {
                if (relation.getSupplierId() != null && relation.getSupplierId().equals(supplierId) && relation.getProductId() != null) {
                    allowedProductIds.add(relation.getProductId());
                }
            }
            ObservableList<DropdownOption> filtered = FXCollections.observableArrayList(
                baseProducts.stream().filter(option -> option.getId() != null && allowedProductIds.contains(option.getId())).toList()
            );
            cbProduct.setItems(filtered);
            DropdownOption selectedProduct = cbProduct.getSelectionModel().getSelectedItem();
            if (selectedProduct != null && !allowedProductIds.contains(selectedProduct.getId())) {
                cbProduct.getSelectionModel().clearSelection();
            }
        } finally {
            updatingChoices = false;
        }
    }

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

}
