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.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.ImagePattern;
import javafx.scene.shape.Circle;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import org.example.petshopdesktop.api.ChatRealtimeClient;
import org.example.petshopdesktop.api.dto.chat.ConversationResponse;
import org.example.petshopdesktop.api.dto.chat.MessageRequest;
import org.example.petshopdesktop.api.dto.chat.MessageResponse;
import org.example.petshopdesktop.api.dto.chat.UpdateConversationRequest;
import org.example.petshopdesktop.api.dto.common.DropdownOption;
import org.example.petshopdesktop.api.endpoints.ChatApi;
import org.example.petshopdesktop.api.endpoints.DropdownApi;
import org.example.petshopdesktop.api.ApiClient;
import org.example.petshopdesktop.auth.UserSession;
import org.example.petshopdesktop.util.ActivityLogger;

import java.io.File;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class ChatController {
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("MMM d, HH:mm");
    private static final int CHAT_CELL_HEIGHT = 80;

    @FXML
    private ListView<ConversationResponse> lvActiveConversations;

    @FXML
    private ListView<ConversationResponse> lvClosedConversations;

    @FXML
    private Button btnActiveHeader;

    @FXML
    private Button btnClosedHeader;

    @FXML
    private VBox vbMessages;

    @FXML
    private ScrollPane spMessages;

    @FXML
    private TextArea txtMessage;

    @FXML
    private Button btnSend;

    @FXML
    private Button btnRefresh;

    @FXML
    private Button btnAttachment;

    @FXML
    private Button btnClose;

    @FXML
    private Label lblConversationTitle;

    @FXML
    private Label lblChatStatus;

    private final ObservableList<ConversationResponse> conversations = FXCollections.observableArrayList();
    private final ObservableList<ConversationResponse> activeConversations = FXCollections.observableArrayList();
    private final ObservableList<ConversationResponse> closedConversations = FXCollections.observableArrayList();
    private final Map<Long, String> customerLabels = new HashMap<>();
    private final Set<Long> renderedMessageIds = new HashSet<>();
    private final ChatRealtimeClient realtimeClient = ChatRealtimeClient.getInstance();
    private ConversationResponse selectedConversation;
    private File selectedAttachmentFile;
    private boolean activeExpanded = true;
    private boolean closedExpanded = true;
    private boolean selectionChanging = false;

    @FXML
    public void initialize() {
        setupConversationListView(lvActiveConversations);
        setupConversationListView(lvClosedConversations);
        lvActiveConversations.setItems(activeConversations);
        lvClosedConversations.setItems(closedConversations);

        lvActiveConversations.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> {
            if (newVal != null && !selectionChanging) {
                selectionChanging = true;
                lvClosedConversations.getSelectionModel().clearSelection();
                selectionChanging = false;
                onConversationSelected(newVal);
            }
        });

        lvClosedConversations.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> {
            if (newVal != null && !selectionChanging) {
                selectionChanging = true;
                lvActiveConversations.getSelectionModel().clearSelection();
                selectionChanging = false;
                onConversationSelected(newVal);
            }
        });

        txtMessage.setOnKeyPressed(event -> {
            if (event.getCode() == KeyCode.ENTER) {
                if (event.isShiftDown()) {
                    txtMessage.appendText("\n");
                } else {
                    btnSendClicked();
                }
                event.consume();
            }
        });

        realtimeClient.setConversationListener(conversation -> Platform.runLater(() -> upsertConversation(conversation)));
        realtimeClient.setMessageListener(message -> Platform.runLater(() -> appendMessageIfSelected(message)));
        realtimeClient.setStatusListener(status -> Platform.runLater(() -> lblChatStatus.setText(status)));
        realtimeClient.subscribeToConversations();

        loadCustomers();
        loadConversations();
        updateChatState(null);
    }

    @FXML
    void toggleActiveSection() {
        activeExpanded = !activeExpanded;
        lvActiveConversations.setVisible(activeExpanded);
        lvActiveConversations.setManaged(activeExpanded);
        updateSectionHeaders();
    }

    @FXML
    void toggleClosedSection() {
        closedExpanded = !closedExpanded;
        lvClosedConversations.setVisible(closedExpanded);
        lvClosedConversations.setManaged(closedExpanded);
        updateSectionHeaders();
    }

    @FXML
    void btnRefreshClicked() {
        loadConversations();
        if (selectedConversation != null) {
            loadMessages(selectedConversation.getId());
        }
    }

    @FXML
    void btnSendClicked() {
        if (selectedConversation == null) {
            lblChatStatus.setText("Select a conversation");
            return;
        }

        String content = txtMessage.getText() == null ? "" : txtMessage.getText().trim();
        if (content.isEmpty() && selectedAttachmentFile == null) {
            return;
        }

        Long convId = selectedConversation.getId();
        File attachmentFile = selectedAttachmentFile;

        txtMessage.clear();
        btnSend.setDisable(true);
        lblChatStatus.setText("Sending message...");

        new Thread(() -> {
            try {
                MessageResponse response;
                if (attachmentFile != null) {
                    response = ChatApi.getInstance().sendMessageWithAttachment(convId, attachmentFile, content);
                } else {
                    response = ChatApi.getInstance().sendMessage(convId, new MessageRequest(content));
                }
                Platform.runLater(() -> {
                    btnSend.setDisable(false);
                    realtimeClient.markConversationReplied(convId, UserSession.getInstance().getUserId());
                    appendMessageIfSelected(response);
                    if (attachmentFile != null) {
                        clearLocalAttachment();
                    }
                    lblChatStatus.setText("Message sent");
                });
            } catch (OutOfMemoryError e) {
                Platform.runLater(() -> {
                    txtMessage.setText(content);
                    btnSend.setDisable(false);
                    lblChatStatus.setText("File too large to send");
                });
            } catch (Exception e) {
                Platform.runLater(() -> {
                    txtMessage.setText(content);
                    btnSend.setDisable(false);
                    String msg = e.getMessage();
                    lblChatStatus.setText(msg != null && !msg.isBlank() ? msg : "Chat send failed");
                    ActivityLogger.getInstance().logException(
                            "ChatController.sendMessage",
                            e,
                            "Sending chat message for conversation " + convId);
                });
            }
        }).start();
    }

    @FXML
    void btnAttachmentClicked(ActionEvent event) {
        File file = org.example.petshopdesktop.util.FilePickerSupport.pickAnyFile(btnAttachment.getScene().getWindow());
        if (file == null) return;

        long maxBytes = 5 * 1024 * 1024;
        if (file.length() > maxBytes) {
            javafx.scene.control.Alert alert = new javafx.scene.control.Alert(javafx.scene.control.Alert.AlertType.WARNING);
            alert.setTitle("File Too Large");
            alert.setHeaderText("The selected file exceeds the 5 MB limit.");
            alert.setContentText("Please choose a smaller file and try again.");
            alert.showAndWait();
            return;
        }

        selectedAttachmentFile = file;
        btnAttachment.setText("📎 " + file.getName());
        btnAttachment.setStyle("-fx-background-color: #dcfce7; -fx-background-radius: 12; -fx-text-fill: #166534; -fx-cursor: hand;");
        lblChatStatus.setText("Attachment selected");
    }

    @FXML
    void btnCloseClicked() {
        if (selectedConversation == null || "CLOSED".equals(selectedConversation.getStatus())) return;
        Long convId = selectedConversation.getId();
        btnClose.setDisable(true);
        lblChatStatus.setText("Closing...");
        new Thread(() -> {
            try {
                ConversationResponse updated = ChatApi.getInstance().updateConversation(convId, new UpdateConversationRequest("CLOSED"));
                Platform.runLater(() -> {
                    upsertConversation(updated);
                    updateChatState(updated);
                    lblChatStatus.setText("Conversation closed");
                });
            } catch (Exception e) {
                Platform.runLater(() -> {
                    btnClose.setDisable(false);
                    lblChatStatus.setText("Close failed");
                    ActivityLogger.getInstance().logException(
                            "ChatController.closeConversation",
                            e,
                            "Closing conversation " + convId);
                });
            }
        }).start();
    }

    private void onConversationSelected(ConversationResponse newValue) {
        boolean sameConversation = selectedConversation != null && selectedConversation.getId().equals(newValue.getId());
        selectedConversation = newValue;
        lblConversationTitle.setText(getConversationTitle(newValue));
        if (!sameConversation) {
            loadMessages(newValue.getId());
            realtimeClient.subscribeToConversation(newValue.getId());
        }
        updateChatState(newValue);
    }

    private void updateChatState(ConversationResponse conv) {
        boolean closed = conv == null || "CLOSED".equals(conv.getStatus());
        txtMessage.setDisable(closed);
        btnSend.setDisable(closed);
        btnAttachment.setDisable(closed);
        btnClose.setVisible(!closed);
        btnClose.setManaged(!closed);
        if (!closed) {
            btnClose.setDisable(false);
        }
    }

    private void clearLocalAttachment() {
        selectedAttachmentFile = null;
        btnAttachment.setText("📎");
        btnAttachment.setStyle("-fx-background-color: #e2e8f0; -fx-background-radius: 12; -fx-text-fill: #475569; -fx-cursor: hand;");
    }

    private void setupConversationListView(ListView<ConversationResponse> lv) {
        lv.setFixedCellSize(CHAT_CELL_HEIGHT);
        lv.setCellFactory(list -> new ListCell<>() {
            @Override
            protected void updateItem(ConversationResponse item, boolean empty) {
                super.updateItem(item, empty);
                if (empty || item == null) {
                    setText(null);
                    setGraphic(null);
                    return;
                }

                Label title = new Label(getConversationTitle(item));

                UserSession session = UserSession.getInstance();
                Long currentUserId = session.getUserId();
                boolean needsPickup = item.getHumanRequestedAt() != null && item.getStaffId() == null;
                boolean needsReply = currentUserId != null && currentUserId.equals(item.getStaffId())
                        && item.getLastSenderId() != null && !item.getLastSenderId().equals(currentUserId);

                if (needsPickup || needsReply) {
                    title.setStyle("-fx-font-weight: bold; -fx-text-fill: #FF6B6B;");
                } else {
                    title.setStyle("-fx-font-weight: bold; -fx-text-fill: #1f2937;");
                }
                Label preview = new Label(item.getLastMessage() == null ? "" : item.getLastMessage());
                preview.setStyle("-fx-text-fill: #64748b;");
                preview.setTextOverrun(javafx.scene.control.OverrunStyle.ELLIPSIS);
                preview.setMaxWidth(Double.MAX_VALUE);
                Label meta = new Label(buildConversationMeta(item));
                meta.setStyle("-fx-text-fill: #94a3b8; -fx-font-size: 11px;");
                VBox box = new VBox(4, title, preview, meta);
                box.setMaxWidth(Double.MAX_VALUE);
                setGraphic(box);
            }
        });
    }

    private void refreshSections() {
        activeConversations.setAll(conversations.stream()
                .filter(c -> !"CLOSED".equals(c.getStatus()))
                .collect(Collectors.toList()));
        closedConversations.setAll(conversations.stream()
                .filter(c -> "CLOSED".equals(c.getStatus()))
                .collect(Collectors.toList()));
        updateSectionHeaders();
        updateListViewHeights();
    }

    private void updateSectionHeaders() {
        btnActiveHeader.setText((activeExpanded ? "▼" : "▶") + "  Active Chats (" + activeConversations.size() + ")");
        btnClosedHeader.setText((closedExpanded ? "▼" : "▶") + "  Closed Chats (" + closedConversations.size() + ")");
    }

    private void updateListViewHeights() {
        if (activeExpanded) {
            lvActiveConversations.setPrefHeight(activeConversations.size() * CHAT_CELL_HEIGHT + 2);
        }
        if (closedExpanded) {
            lvClosedConversations.setPrefHeight(closedConversations.size() * CHAT_CELL_HEIGHT + 2);
        }
    }

    private void loadCustomers() {
        new Thread(() -> {
            try {
                List<DropdownOption> customers = DropdownApi.getInstance().getCustomers();
                Map<Long, String> labels = new HashMap<>();
                for (DropdownOption option : customers) {
                    labels.put(option.getId(), option.getLabel());
                }
                Platform.runLater(() -> {
                    customerLabels.clear();
                    customerLabels.putAll(labels);
                    lvActiveConversations.refresh();
                    lvClosedConversations.refresh();
                    if (selectedConversation != null) {
                        lblConversationTitle.setText(getConversationTitle(selectedConversation));
                    }
                });
            } catch (Exception e) {
                Platform.runLater(() -> ActivityLogger.getInstance().logException(
                        "ChatController.loadCustomers",
                        e,
                        "Loading customer labels for chat"));
            }
        }).start();
    }

    private void loadConversations() {
        new Thread(() -> {
            try {
                List<ConversationResponse> response = ChatApi.getInstance().listConversations();
                response.sort(Comparator.comparing(ChatController::conversationSortTime, Comparator.nullsLast(Comparator.reverseOrder())));
                Platform.runLater(() -> {
                    realtimeClient.initializeState(response);
                    conversations.setAll(response);
                    refreshSections();
                    restoreSelection();
                    if (selectedConversation == null && !activeConversations.isEmpty()) {
                        lvActiveConversations.getSelectionModel().selectFirst();
                    } else if (selectedConversation == null && !closedConversations.isEmpty()) {
                        lvClosedConversations.getSelectionModel().selectFirst();
                    }
                });
            } catch (Exception e) {
                Platform.runLater(() -> {
                    lblChatStatus.setText("Chat unavailable");
                    ActivityLogger.getInstance().logException(
                            "ChatController.loadConversations",
                            e,
                            "Loading conversations");
                });
            }
        }).start();
    }

    private void loadMessages(Long conversationId) {
        new Thread(() -> {
            try {
                List<MessageResponse> messages = ChatApi.getInstance().listMessages(conversationId);
                Platform.runLater(() -> renderMessages(messages));
            } catch (Exception e) {
                Platform.runLater(() -> ActivityLogger.getInstance().logException(
                        "ChatController.loadMessages",
                        e,
                        "Loading messages for conversation " + conversationId));
            }
        }).start();
    }

    private void renderMessages(List<MessageResponse> messages) {
        vbMessages.getChildren().clear();
        renderedMessageIds.clear();
        for (MessageResponse message : messages) {
            try {
                vbMessages.getChildren().add(createMessageBubble(message));
                if (message.getId() != null) renderedMessageIds.add(message.getId());
            } catch (Exception e) {
                ActivityLogger.getInstance().logException(
                        "ChatController.renderMessages",
                        e,
                        "Rendering chat message");
            }
        }
        scrollMessagesToBottom();
    }

    private void appendMessageIfSelected(MessageResponse message) {
        try {
            upsertConversationForMessage(message);
            if (selectedConversation != null && selectedConversation.getId().equals(message.getConversationId())) {
                if (message.getId() != null && renderedMessageIds.contains(message.getId())) {
                    return;
                }
                vbMessages.getChildren().add(createMessageBubble(message));
                if (message.getId() != null) renderedMessageIds.add(message.getId());
                scrollMessagesToBottom();
            }
        } catch (Exception e) {
            ActivityLogger.getInstance().logException(
                    "ChatController.appendMessageIfSelected",
                    e,
                    "Appending chat message");
        }
    }

    private void upsertConversation(ConversationResponse conversation) {
        Optional<ConversationResponse> existing = conversations.stream()
                .filter(item -> item.getId().equals(conversation.getId()))
                .findFirst();

        if (existing.isPresent()) {
            int index = conversations.indexOf(existing.get());
            conversations.set(index, conversation);
        } else {
            conversations.add(conversation);
        }

        conversations.sort(Comparator.comparing(ChatController::conversationSortTime, Comparator.nullsLast(Comparator.reverseOrder())));
        refreshSections();
        restoreSelection();
        if (selectedConversation != null && selectedConversation.getId().equals(conversation.getId())) {
            updateChatState(conversation);
        }
    }

    private void upsertConversationForMessage(MessageResponse message) {
        conversations.stream()
                .filter(conversation -> conversation.getId().equals(message.getConversationId()))
                .findFirst()
                .ifPresent(conversation -> {
                    conversation.setLastMessage(message.getContent());
                    conversation.setLastSenderId(message.getSenderId());
                    conversation.setUpdatedAt(message.getTimestamp());
                    conversations.sort(Comparator.comparing(ChatController::conversationSortTime, Comparator.nullsLast(Comparator.reverseOrder())));
                    refreshSections();
                });
    }

    private void restoreSelection() {
        if (selectedConversation == null) {
            return;
        }
        Long selId = selectedConversation.getId();
        activeConversations.stream()
                .filter(item -> item.getId().equals(selId))
                .findFirst()
                .ifPresent(match -> {
                    selectedConversation = match;
                    lvActiveConversations.getSelectionModel().select(match);
                });
        closedConversations.stream()
                .filter(item -> item.getId().equals(selId))
                .findFirst()
                .ifPresent(match -> {
                    selectedConversation = match;
                    lvClosedConversations.getSelectionModel().select(match);
                });
    }

    private HBox createMessageBubble(MessageResponse message) {
        boolean mine = message.getSenderId() != null && message.getSenderId().equals(UserSession.getInstance().getUserId());

        Circle avatar = new Circle(16);
        avatar.setFill(javafx.scene.paint.Color.web(mine ? "#0f766e" : "#cbd5e1"));
        if (message.getSenderAvatarUrl() != null && !message.getSenderAvatarUrl().isBlank()) {
            String relativeUrl = message.getSenderAvatarUrl();
            new Thread(() -> {
                try {
                    byte[] bytes = ApiClient.getInstance().getBytes(relativeUrl);
                    Image img = new Image(new java.io.ByteArrayInputStream(bytes));
                    if (!img.isError()) {
                        Platform.runLater(() -> avatar.setFill(new ImagePattern(img)));
                    }
                } catch (Exception ignored) {}
            }).start();
        }

        Label author = new Label(resolveAuthorLabel(message));
        author.setStyle("-fx-font-weight: bold; -fx-text-fill: " + (mine ? "#ffffff" : "#1f2937") + ";");

        String timestampText = message.getTimestamp() == null ? "" : TIME_FORMATTER.format(message.getTimestamp());
        Label timestamp = new Label(timestampText);
        timestamp.setStyle("-fx-text-fill: " + (mine ? "#dbeafe" : "#94a3b8") + "; -fx-font-size: 11px;");

        VBox bubble = new VBox(4, author);
        String contentText = message.getContent() == null ? "" : message.getContent();
        if (!contentText.isBlank()) {
            Label content = new Label(contentText);
            content.setWrapText(true);
            content.setStyle("-fx-text-fill: " + (mine ? "#ffffff" : "#1f2937") + ";");
            bubble.getChildren().add(content);
        }
        if (message.getAttachmentName() != null && !message.getAttachmentName().isBlank()) {
            String mimeType = message.getAttachmentMimeType();
            boolean isImage = mimeType != null && mimeType.startsWith("image/");
            if (isImage && message.getId() != null) {
                ImageView imageView = new ImageView();
                imageView.setFitWidth(280);
                imageView.setFitHeight(280);
                imageView.setPreserveRatio(true);
                imageView.setSmooth(true);
                bubble.getChildren().add(imageView);
                String attachmentPath = "/api/v1/chat/messages/" + message.getId() + "/attachment";
                new Thread(() -> {
                    try {
                        byte[] bytes = ApiClient.getInstance().getBytes(attachmentPath);
                        Image img = new Image(new java.io.ByteArrayInputStream(bytes));
                        if (!img.isError()) {
                            Platform.runLater(() -> imageView.setImage(img));
                        }
                    } catch (Exception ignored) {}
                }).start();
            } else {
                Label attachmentLabel = new Label("\uD83D\uDCCE " + message.getAttachmentName());
                attachmentLabel.setStyle("-fx-text-fill: " + (mine ? "#dbeafe" : "#475569") + "; -fx-font-size: 12px;");
                attachmentLabel.setWrapText(true);
                bubble.getChildren().add(attachmentLabel);
            }
        }

        bubble.getChildren().add(timestamp);
        bubble.setMaxWidth(420);
        bubble.setStyle(mine
                ? "-fx-background-color: #0f766e; -fx-background-radius: 14; -fx-padding: 12;"
                : "-fx-background-color: #e2e8f0; -fx-background-radius: 14; -fx-padding: 12;");

        Region spacer = new Region();
        HBox.setHgrow(spacer, Priority.ALWAYS);
        HBox container = new HBox(12);
        container.setAlignment(javafx.geometry.Pos.BOTTOM_LEFT);

        if (mine) {
            container.getChildren().addAll(spacer, bubble, avatar);
            container.setAlignment(javafx.geometry.Pos.BOTTOM_RIGHT);
        } else {
            container.getChildren().addAll(avatar, bubble, spacer);
        }
        return container;
    }

    private String resolveAuthorLabel(MessageResponse message) {
        if ("BOT".equalsIgnoreCase(message.getSenderRole())) {
            return message.getSenderDisplayName() != null && !message.getSenderDisplayName().isBlank()
                    ? message.getSenderDisplayName()
                    : "AI Bot";
        }
        Long currentUserId = UserSession.getInstance().getUserId();
        if (message.getSenderId() != null && message.getSenderId().equals(currentUserId)) {
            return "You";
        }
        if (selectedConversation != null && selectedConversation.getStaffId() != null && selectedConversation.getStaffId().equals(message.getSenderId())) {
            return "Staff";
        }
        return "Customer";
    }

    private String getConversationTitle(ConversationResponse conversation) {
        String customerLabel = customerLabels.get(conversation.getCustomerId());
        return customerLabel != null ? customerLabel : "Customer #" + conversation.getCustomerId();
    }

    private String buildConversationMeta(ConversationResponse conversation) {
        String updated = conversation.getUpdatedAt() == null ? "" : TIME_FORMATTER.format(conversation.getUpdatedAt());
        if ("CLOSED".equals(conversation.getStatus())) {
            return "Closed" + (updated.isBlank() ? "" : " · " + updated);
        }
        String assignee;
        if (conversation.getStaffId() != null) {
            assignee = "Assigned";
        } else if (conversation.getHumanRequestedAt() != null) {
            assignee = "Takeover requested";
        } else if ("AUTOMATED".equals(conversation.getMode())) {
            assignee = "Automated";
        } else {
            assignee = "Open";
        }
        return assignee + (updated.isBlank() ? "" : " · " + updated);
    }

    private static java.time.LocalDateTime conversationSortTime(ConversationResponse conversation) {
        return conversation.getUpdatedAt() != null ? conversation.getUpdatedAt() : conversation.getCreatedAt();
    }

    private void scrollMessagesToBottom() {
        Platform.runLater(() -> spMessages.setVvalue(1.0));
    }
}
