package org.example.petshopdesktop.controllers;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
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.DTOs.ProductSupplierDTO;
import org.example.petshopdesktop.api.dto.productsupplier.ProductSupplierResponse;
import org.example.petshopdesktop.api.endpoints.ProductSupplierApi;
import org.example.petshopdesktop.controllers.dialogcontrollers.ProductSupplierDialogController;
import org.example.petshopdesktop.util.ActivityLogger;
import org.example.petshopdesktop.util.TableViewSupport;

import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public class ProductSupplierController {

    @FXML
    private Button btnAdd;

    @FXML
    private Button btnDelete;

    @FXML
    private Button btnEdit;

    @FXML
    private Button btnRefresh;

    @FXML
    private Label lblStatus;

    @FXML
    private TableColumn<ProductSupplierDTO, Double> colCost;

    @FXML
    private TableColumn<ProductSupplierDTO, Integer> colProductId;

    @FXML
    private TableColumn<ProductSupplierDTO, String> colProductName;

    @FXML
    private TableColumn<ProductSupplierDTO, Integer> colSupplierId;

    @FXML
    private TableColumn<ProductSupplierDTO, String> colSupplierName;

    @FXML
    private TableView<ProductSupplierDTO> tvProductSuppliers;

    @FXML
    private TextField txtSearch;

    //data declaration
    private ObservableList<ProductSupplierDTO> data = FXCollections.observableArrayList();
    private String mode = null;

    /**
     * Set up the table view for table and display it when starting up
     */
    @FXML
    public void initialize() {
        //Disable buttons until a row is selected
        btnEdit.setDisable(true);
        btnDelete.setDisable(true);
        //Enable multiple selection
        tvProductSuppliers.getSelectionModel().setSelectionMode(javafx.scene.control.SelectionMode.MULTIPLE);
        //set up table columns
        colProductId.setCellValueFactory(new PropertyValueFactory<ProductSupplierDTO,Integer>("prodId"));
        colProductName.setCellValueFactory(new PropertyValueFactory<ProductSupplierDTO,String>("prodName"));
        colSupplierId.setCellValueFactory(new PropertyValueFactory<ProductSupplierDTO,Integer>("supId"));
        colSupplierName.setCellValueFactory(new PropertyValueFactory<ProductSupplierDTO,String>("supCompany"));
        colCost.setCellValueFactory(new PropertyValueFactory<ProductSupplierDTO,Double>("cost"));

        displayProductSupplier();
        TableViewSupport.installDoubleClickAction(tvProductSuppliers, selected -> openDialog(selected, "Edit"));

        //EventListener to Enable buttons when a row is selected
        tvProductSuppliers.getSelectionModel().selectedItemProperty().addListener(
                (observable, oldValue, newValue) -> {
                    boolean hasSelection = newValue != null;
                    btnEdit.setDisable(!hasSelection);
                    btnDelete.setDisable(!hasSelection);
                }
        );

        //EventListener to search when text is changed on searchbar
        txtSearch.textProperty().addListener((observable, oldValue, newValue) -> {
            displayFilteredProductSupplier(newValue);
        });

        //EventListener for DELETE key
        tvProductSuppliers.setOnKeyPressed(event -> {
            if (event.getCode() == javafx.scene.input.KeyCode.DELETE) {
                if (tvProductSuppliers.getSelectionModel().getSelectedItem() != null) {
                    btnDeleteClicked(null);
                }
            }
        });

    }

    /**
     * Display the ProductSupplierDTO to table view
     */
    private void displayProductSupplier() {
        new Thread(() -> {
            try {
                List<ProductSupplierResponse> productSuppliers = ProductSupplierApi.getInstance().listProductSuppliers(null);
                List<ProductSupplierDTO> productSupplierDTOs = productSuppliers.stream()
                    .map(this::mapToProductSupplierDTO)
                    .collect(Collectors.toList());

                Platform.runLater(() -> {
                    data.setAll(productSupplierDTOs);
                    tvProductSuppliers.setItems(data);
                });
            } catch (Exception e) {
                Platform.runLater(() -> {
                    System.out.println("Error while fetching table data: " + e.getMessage());
                    ActivityLogger.getInstance().logException(
                        "ProductSupplierController.displayProductSupplier",
                        e,
                        "Fetching product-supplier data for table display");
                });
            }
        }).start();
    }

    /**
     * Filter the table given the string from the searchbar
     * @param filter word to filter table
     */
    private void displayFilteredProductSupplier(String filter){
        if (txtSearch.getText() == null || txtSearch.getText().isEmpty()){
            displayProductSupplier();
        } else {
            new Thread(() -> {
                try {
                    List<ProductSupplierResponse> productSuppliers = ProductSupplierApi.getInstance().listProductSuppliers(filter);
                    List<ProductSupplierDTO> productSupplierDTOs = productSuppliers.stream()
                        .map(this::mapToProductSupplierDTO)
                        .collect(Collectors.toList());

                    Platform.runLater(() -> {
                        data.setAll(productSupplierDTOs);
                        tvProductSuppliers.setItems(data);
                    });
                } catch (Exception e) {
                    Platform.runLater(() -> {
                        System.out.println("Error while fetching table data: " + e.getMessage());
                        ActivityLogger.getInstance().logException(
                            "ProductSupplierController.displayFilteredProductSupplier",
                            e,
                            "Filtering product-supplier data with filter: " + filter);
                    });
                }
            }).start();
        }
    }

    /**
     * open a new dialog for adding a productSupplier
     * @param event click event for button
     */
    @FXML
    void btnRefresh(ActionEvent event) {
        txtSearch.clear();
        tvProductSuppliers.getSortOrder().clear();
        displayProductSupplier();
        TableViewSupport.flashStatus(lblStatus, "Refreshed");
    }

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

    /**
     * Delete selected product-supplier(s) when delete is clicked
     * @param event click event for button
     */
    @FXML
    void btnDeleteClicked(ActionEvent event) {
        //get selected product-suppliers
        var selectedProductSuppliers = tvProductSuppliers.getSelectionModel().getSelectedItems();
        if (selectedProductSuppliers.isEmpty()) return;

        //ask user to confirm
        Alert question = new Alert(Alert.AlertType.CONFIRMATION);
        question.setHeaderText("Please confirm delete");
        String message = selectedProductSuppliers.size() == 1
            ? "Are you sure you want to delete this product-supplier?"
            : "Are you sure you want to delete " + selectedProductSuppliers.size() + " product-suppliers?";
        question.setContentText(message);
        question.getDialogPane().lookupButton(ButtonType.OK).requestFocus();
        Optional<ButtonType> result = question.showAndWait();

        //if confirmed, start deletion
        if (result.isPresent() && result.get() == ButtonType.OK) {
            int deleteCount = 0;
            Exception lastException = null;

            for (ProductSupplierDTO ps : selectedProductSuppliers) {
                try {
                    ProductSupplierApi.getInstance().deleteProductSupplier(
                        (long) ps.getProdId(),
                        (long) ps.getSupId()
                    );
                    deleteCount++;
                } catch (Exception e) {
                    lastException = e;
                    ActivityLogger.getInstance().logException(
                        "ProductSupplierController.btnDeleteClicked",
                        e,
                        "Deleting product-supplier with productId=" + ps.getProdId() + ", supplierId=" + ps.getSupId());
                }
            }

            if (deleteCount > 0) {
                Alert alert = new Alert(Alert.AlertType.INFORMATION);
                alert.setHeaderText("Database Operation Confirmed");
                alert.setContentText("Successfully deleted " + deleteCount + " product-supplier(s)");
                alert.showAndWait();
            }

            if (lastException != null && deleteCount < selectedProductSuppliers.size()) {
                Alert alert = new Alert(Alert.AlertType.ERROR);
                alert.setHeaderText("Delete Operation Partially Failed");
                alert.setContentText("Deleted " + deleteCount + " of " + selectedProductSuppliers.size() + " product-supplier(s). Last error: " + lastException.getMessage());
                alert.showAndWait();
            }

            //refresh display and reset inputs
            displayProductSupplier();
            btnDelete.setDisable(true);
            btnEdit.setDisable(true);
            txtSearch.setText("");
        }
    }

    @FXML
    void btnEditClicked(ActionEvent event) {
        //set selected item
        ProductSupplierDTO selectedProductSupplier = tvProductSuppliers.getSelectionModel().getSelectedItem();

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

    /**
     * Function to open the new Dialog for edit or adding
     * depending on the mode given
     * @param productSupplier the productSupplier entity for editing, null if adding
     * @param mode the mode the dialog should be in
     */
    private void openDialog(ProductSupplierDTO productSupplier, String mode){
        //Get new view
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/example/petshopdesktop/dialogviews/product-supplier-dialog-view.fxml"));
        Scene scene = null;
        try{
            scene = new Scene(fxmlLoader.load());
        } catch (IOException e) {
            ActivityLogger.getInstance().logException(
                "ProductSupplierController.openDialog",
                e,
                "Loading product-supplier dialog in " + mode + " mode");
            throw new RuntimeException(e);
        }
        ProductSupplierDialogController dialogController = fxmlLoader.getController(); //controller associated with this view
        dialogController.setMode(mode);
        if (productSupplier != null) {
            dialogController.setSelectedIds(productSupplier.getSupId(), productSupplier.getProdId());
        }

        //Open the dialog depending on the mode
        if(mode.equals("Edit")){
            //Make it display suppliers details in dialog
            dialogController.displayProductSupplierDetails(productSupplier);
        }
        Stage dialogStage = new Stage();
        dialogStage.initModality(Modality.APPLICATION_MODAL); //make it modal
        if(mode.equals("Add")){
            dialogStage.setTitle("Add Product-Supplier");
        }
        else {
            dialogStage.setTitle("Edit Product-Supplier");
        }
        dialogStage.setScene(scene);
        dialogStage.showAndWait();

        //When dialog closes update table view and disable edit and delete buttons, and reset search bar
        displayProductSupplier();
        btnDelete.setDisable(true);
        btnEdit.setDisable(true);
        txtSearch.setText("");
    }

    private ProductSupplierDTO mapToProductSupplierDTO(ProductSupplierResponse response) {
        return new ProductSupplierDTO(
            response.getSupplierId().intValue(),
            response.getProductId().intValue(),
            response.getSupplierName(),
            response.getProductName(),
            response.getCost().doubleValue()
        );
    }

}
