package org.example.petshopdesktop.controllers;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import org.example.petshopdesktop.api.dto.activity.ActivityLogResponse;
import org.example.petshopdesktop.api.endpoints.ActivityLogApi;
import org.example.petshopdesktop.util.ActivityLogger;
import org.example.petshopdesktop.util.TableViewSupport;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.TreeSet;

public class ActivityLogController {

    private static final String SEPARATOR_NEW = "  |  ";
    private static final String SEPARATOR_OLD = "  \u00b7  ";
    private static final DateTimeFormatter DISPLAY_FORMATTER = DateTimeFormatter.ofPattern("MMM d, HH:mm");
    private static final int DEFAULT_LIMIT = 2000;

    @FXML private TableView<ActivityLogResponse> tvActivityLogs;
    @FXML private TableColumn<ActivityLogResponse, Object> colTimestamp;
    @FXML private TableColumn<ActivityLogResponse, String> colUser;
    @FXML private TableColumn<ActivityLogResponse, String> colRole;
    @FXML private TableColumn<ActivityLogResponse, String> colStore;
    @FXML private TableColumn<ActivityLogResponse, String> colActivity;
    @FXML private Button btnRefresh;
    @FXML private DatePicker dpStart;
    @FXML private DatePicker dpEnd;
    @FXML private CheckBox chkHideViewOnly;
    @FXML private TextField tfSearch;
    @FXML private ComboBox<String> cbRoleFilter;
    @FXML private ComboBox<String> cbStoreFilter;
    @FXML private Label lblStatus;
    @FXML private Label lblError;

    private final ObservableList<ActivityLogResponse> activityLogs = FXCollections.observableArrayList();
    private FilteredList<ActivityLogResponse> filteredLogs;

    @FXML
    public void initialize() {
        colTimestamp.setCellValueFactory(data -> new javafx.beans.property.SimpleObjectProperty<>(data.getValue().getLogTimestamp()));
        colTimestamp.setCellFactory(col -> new TableCell<>() {
            @Override
            protected void updateItem(Object item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText(null);
                } else if (item instanceof java.time.LocalDateTime time) {
                    setText(time.format(DISPLAY_FORMATTER));
                } else {
                    setText(item.toString());
                }
            }
        });

        colUser.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(displayUser(data.getValue())));
        colRole.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(formatRole(displayRole(data.getValue()))));
        colStore.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(displayStore(data.getValue())));

        colActivity.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(nullToBlank(data.getValue().getActivity())));
        colActivity.setCellFactory(col -> new TableCell<>() {
            @Override
            protected void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null || item.isBlank()) {
                    setGraphic(null);
                    setText(null);
                    return;
                }
                int sep = item.indexOf(SEPARATOR_NEW);
                int sepLen = SEPARATOR_NEW.length();
                if (sep < 0) {
                    sep = item.indexOf(SEPARATOR_OLD);
                    sepLen = SEPARATOR_OLD.length();
                }
                if (sep >= 0) {
                    String description = item.substring(0, sep).trim();
                    String technical = item.substring(sep + sepLen).trim();
                    Label lblDesc = new Label(description);
                    lblDesc.setStyle("-fx-font-weight: bold; -fx-text-fill: #1f2937;");
                    lblDesc.setWrapText(true);
                    Label lblTech = new Label(technical);
                    lblTech.setStyle("-fx-font-size: 11px; -fx-text-fill: #94a3b8; -fx-font-family: monospace;");
                    lblTech.setWrapText(true);
                    VBox box = new VBox(2, lblDesc, lblTech);
                    setGraphic(box);
                    setText(null);
                } else {
                    setGraphic(null);
                    setText(item);
                }
            }
        });

        tvActivityLogs.setFixedCellSize(52);

        cbRoleFilter.setItems(FXCollections.observableArrayList("All Roles", "ADMIN", "STAFF", "CUSTOMER"));
        cbRoleFilter.getSelectionModel().selectFirst();
        cbRoleFilter.setOnAction(e -> applyFilters());

        cbStoreFilter.setItems(FXCollections.observableArrayList("All Stores"));
        cbStoreFilter.getSelectionModel().selectFirst();
        cbStoreFilter.setOnAction(e -> applyFilters());

        tfSearch.textProperty().addListener((obs, oldVal, newVal) -> applyFilters());

        filteredLogs = new FilteredList<>(activityLogs, a -> true);
        TableViewSupport.bindSortedItems(tvActivityLogs, filteredLogs);

        dpStart.setValue(LocalDate.now().minusDays(30));
        dpEnd.setValue(LocalDate.now());
        loadLogs();
    }

    @FXML
    void btnRefreshClicked(ActionEvent event) {
        loadLogs();
    }

    @FXML
    void onDateChanged(ActionEvent event) {
        loadLogs();
    }

    @FXML
    void onFilterChanged(ActionEvent event) {
        applyFilters();
    }

    private void applyFilters() {
        String search = tfSearch != null && tfSearch.getText() != null ? tfSearch.getText().trim().toLowerCase() : "";
        String role = cbRoleFilter != null ? cbRoleFilter.getValue() : null;
        String store = cbStoreFilter != null ? cbStoreFilter.getValue() : null;
        boolean hideViewOnly = chkHideViewOnly != null && chkHideViewOnly.isSelected();

        if (filteredLogs == null) return;

        filteredLogs.setPredicate(log -> {
            if (hideViewOnly) {
                String act = log.getActivity();
                if (act != null && act.stripLeading().startsWith("View")) return false;
            }

            if (role != null && !role.equals("All Roles")) {
                String logRole = displayRole(log);
                if (!role.equalsIgnoreCase(logRole)) return false;
            }

            if (store != null && !store.equals("All Stores")) {
                if (!store.equals(displayStore(log))) return false;
            }

            if (!search.isEmpty()) {
                String activity = nullToBlank(log.getActivity()).toLowerCase();
                String user = displayUser(log).toLowerCase();
                String username = nullToBlank(log.getUsername()).toLowerCase();
                String usernameSnap = nullToBlank(log.getUsernameSnapshot()).toLowerCase();
                if (!activity.contains(search) && !user.contains(search)
                        && !username.contains(search) && !usernameSnap.contains(search)) {
                    return false;
                }
            }

            return true;
        });
    }

    private void populateStoreFilter(List<ActivityLogResponse> logs) {
        TreeSet<String> stores = new TreeSet<>();
        for (ActivityLogResponse log : logs) {
            String s = displayStore(log);
            if (!s.isBlank()) stores.add(s);
        }
        String current = cbStoreFilter.getValue();
        ObservableList<String> options = FXCollections.observableArrayList("All Stores");
        options.addAll(stores);
        cbStoreFilter.setItems(options);
        cbStoreFilter.setValue(options.contains(current) ? current : "All Stores");
    }

    private void loadLogs() {
        lblError.setText("");
        tvActivityLogs.setDisable(true);
        btnRefresh.setDisable(true);

        LocalDate startDate = dpStart != null ? dpStart.getValue() : null;
        LocalDate endDate = dpEnd != null ? dpEnd.getValue() : null;

        new Thread(() -> {
            try {
                List<ActivityLogResponse> content = ActivityLogApi.getInstance().getActivityLogs(DEFAULT_LIMIT, startDate, endDate);
                List<ActivityLogResponse> safeContent = content != null ? content : List.of();

                Platform.runLater(() -> {
                    activityLogs.setAll(safeContent);
                    populateStoreFilter(safeContent);
                    applyFilters();
                    tvActivityLogs.setDisable(false);
                    btnRefresh.setDisable(false);
                    TableViewSupport.flashStatus(lblStatus, "Refreshed");
                });
            } catch (Exception e) {
                ActivityLogger.getInstance().logException("ActivityLogController.loadLogs", e, "Loading activity logs");
                Platform.runLater(() -> {
                    lblError.setText(e.getMessage() == null || e.getMessage().isBlank()
                            ? "Could not load activity logs."
                            : "Could not load activity logs: " + e.getMessage());
                    tvActivityLogs.setDisable(false);
                    btnRefresh.setDisable(false);
                });
            }
        }).start();
    }

    private String displayUser(ActivityLogResponse log) {
        if (log == null) return "";
        if (log.getFullNameSnapshot() != null && !log.getFullNameSnapshot().isBlank()) return log.getFullNameSnapshot();
        if (log.getFullName() != null && !log.getFullName().isBlank()) return log.getFullName();
        if (log.getUsernameSnapshot() != null && !log.getUsernameSnapshot().isBlank()) return log.getUsernameSnapshot();
        if (log.getUsername() != null && !log.getUsername().isBlank()) return log.getUsername();
        return log.getUserId() != null ? String.valueOf(log.getUserId()) : "";
    }

    private String displayRole(ActivityLogResponse log) {
        if (log == null) return "";
        if (log.getRoleSnapshot() != null && !log.getRoleSnapshot().isBlank()) return log.getRoleSnapshot();
        return nullToBlank(log.getRole());
    }

    private String formatRole(String role) {
        if (role == null) return "";
        return switch (role.toUpperCase()) {
            case "ADMIN"    -> "Admin";
            case "STAFF"    -> "Staff";
            case "CUSTOMER" -> "Customer";
            default         -> role;
        };
    }

    private String displayStore(ActivityLogResponse log) {
        if (log == null) return "";
        if (log.getStoreNameSnapshot() != null && !log.getStoreNameSnapshot().isBlank()) return log.getStoreNameSnapshot();
        return nullToBlank(log.getStoreName());
    }

    private String nullToBlank(String value) {
        return value == null ? "" : value;
    }
}
