package org.to.telegramfinalproject.UI;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.geometry.Side;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Circle;
import javafx.stage.FileChooser;
import org.json.JSONArray;
import org.json.JSONObject;
import org.to.telegramfinalproject.Client.ActionHandler;
import org.to.telegramfinalproject.Client.AvatarLocalResolver;
import org.to.telegramfinalproject.Client.Session;
import org.to.telegramfinalproject.Models.ChatEntry;
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.File;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.UUID;

public class ChatPageController {

    // ===== messages area =====
    @FXML
    private VBox messageContainer;
    @FXML
    private ScrollPane messageScrollPane;

    // ===== input area =====
    @FXML
    private TextArea messageInput;
    @FXML
    private Button sendButton;

    @FXML
    private Button attachmentButton;
    @FXML
    private ImageView attachmentIcon;   // <ImageView> inside the attachment button

    // ===== header =====
    @FXML
    private ImageView userAvatar;       // 36x36 in the FXML
    @FXML
    private Label chatTitle;            // contact/group title
    @FXML
    private Label chatStatus;           // last seen / online

    @FXML
    private Button searchInChatButton;  // magnifier button
    @FXML
    private ImageView searchIcon;

    @FXML
    private Button moreButton;          // 3-dots button
    @FXML
    private ImageView moreIcon;
    @FXML
    private ContextMenu moreMenu;
    @FXML
    private MenuItem viewProfileItem;
    @FXML
    private MenuItem deleteChatItem;

    // ===== send icon =====
    @FXML
    private ImageView sendIcon;

    // ===== Time formatter for messages =====
    private static final DateTimeFormatter FMT_HHMM       = DateTimeFormatter.ofPattern("HH:mm");
    private static final DateTimeFormatter FMT_DATE_TIME  = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
    private static final String YESTERDAY_LABEL           = "Yesterday";

    // ===== state =====
    private String chatName;
    private final ThemeManager themeManager = ThemeManager.getInstance();
    private final java.util.Set<String> pendingClientMsgIds =
            java.util.Collections.newSetFromMap(new java.util.concurrent.ConcurrentHashMap<>());

    // Where your icons live
    private static final String ICON_BASE = "/org/to/telegramfinalproject/Icons/";
    private ChatEntry currentChat;
    private UUID me;

    // ----- helpers for safe strings -----
    private static String nz(String s) {
        return s == null ? "" : s.trim();
    }
    private static boolean hasVal(String s) {
        if (s == null) return false;
        String t = s.trim();
        return !t.isEmpty() && !"null".equalsIgnoreCase(t);
    }


    private void initCurrentUserId() {
        try {
            String meStr = org.to.telegramfinalproject.Client.Session
                    .currentUser.getString("internal_uuid");
            me = UUID.fromString(meStr);
        } catch (Exception ignore) {
            me = null;
        }
    }

    private static String str(org.json.JSONObject j, String k) {
        try { return (j.has(k) && !j.isNull(k)) ? j.getString(k) : ""; } catch (Exception e) { return ""; }
    }
    private static boolean bool(org.json.JSONObject j, String k) {
        try { return (j.has(k) && !j.isNull(k)) && j.getBoolean(k); } catch (Exception e) { return false; }
    }
    private static org.json.JSONArray arr(org.json.JSONObject j, String k) {
        try { return (j.has(k) && !j.isNull(k)) ? j.getJSONArray(k) : null; } catch (Exception e) { return null; }
    }


    private String formatWhen(LocalDateTime ts) {
        if (ts == null) return "";
        LocalDate today = LocalDate.now();
        LocalDate d = ts.toLocalDate();

        if (d.isEqual(today)) {
            return FMT_HHMM.format(ts);
        } else if (d.isEqual(today.minusDays(1))) {
            return YESTERDAY_LABEL + " " + FMT_HHMM.format(ts);
        } else {
            return FMT_DATE_TIME.format(ts);
        }
    }

    /** ISO → LocalDateTime (با پشتیبانی از Offset/Z) */
    private LocalDateTime parseWhen(String iso) {
        if (iso == null || iso.isEmpty()) return null;
        try {
            return OffsetDateTime.parse(iso).toLocalDateTime();
        } catch (Exception ignore) {
            try {
                return LocalDateTime.parse(iso);
            } catch (Exception e) {
                return null;
            }
        }
    }

    @FXML
    public void initialize() {
        initCurrentUserId();

        // Send button
        if (sendButton != null) {
            sendButton.setOnAction(e -> sendMessage());
        }

        // ENTER = send, SHIFT+ENTER = newline
        messageInput.addEventFilter(javafx.scene.input.KeyEvent.KEY_PRESSED, e -> {
            if (e.getCode() == javafx.scene.input.KeyCode.ENTER) {
                if (e.isShiftDown()) {
                    // let newline happen
                } else {
                    e.consume(); // block newline
                    sendMessage();
                }
            }
        });

        // Enable/disable send button by content (ignore spaces/newlines)
        messageInput.textProperty().addListener((obs, oldV, newV) -> {
            boolean hasRealText = newV != null && !newV.trim().isEmpty();
            sendButton.setDisable(!hasRealText);

            // toggle style class for color state
            var sc = sendButton.getStyleClass();
            sc.removeAll("send-empty", "send-ready");
            sc.add(hasRealText ? "send-ready" : "send-empty");
        });

        // Auto-resize input box like Telegram
        messageInput.textProperty().addListener((obs, oldText, newText) -> {
            Platform.runLater(() -> {
                var textNode = messageInput.lookup(".text");
                if (textNode != null) {
                    double textHeight = textNode.getBoundsInLocal().getHeight();
                    double padding = 20; // top + bottom padding
                    double newHeight = textHeight + padding;

                    if (newHeight < 40) newHeight = 40;   // min (1 row)
                    if (newHeight > 120) newHeight = 120; // max (~5 rows)

                    messageInput.setPrefHeight(newHeight);
                }
            });
        });

        // Start in "empty" state
        sendButton.getStyleClass().add("send-empty");

        // attach file
        if (attachmentButton != null) {
            attachmentButton.setOnAction(e -> openFileChooser());
        }

        // Hook "More" button → show menu under it
        if (moreButton != null) {
            moreButton.setOnAction(e -> {
                if (!moreMenu.isShowing()) {
                    moreMenu.show(moreButton, Side.BOTTOM, 0, 0);
                } else {
                    moreMenu.hide();
                }
            });
        }

        // Menu item actions
        viewProfileItem.setOnAction(e -> {
            System.out.println("Viewing profile of " + chatName);
            // TODO: open profile UI
        });

        deleteChatItem.setOnAction(e -> {
            System.out.println("Deleting chat with " + chatName);
            // TODO: delete logic
        });

        // Initial icon sync once the Scene is ready (stylesheet applied)
        Platform.runLater(this::syncIconsWithTheme);

        // Auto-Scroll
        messageContainer.heightProperty().addListener((obs, oldVal, newVal) -> {
            Platform.runLater(() -> {
                var timeline = new javafx.animation.Timeline();
                var kv = new javafx.animation.KeyValue(
                        messageScrollPane.vvalueProperty(), 1.0, javafx.animation.Interpolator.EASE_BOTH
                );
                var kf = new javafx.animation.KeyFrame(javafx.util.Duration.millis(200), kv);
                timeline.getKeyFrames().add(kf);
                timeline.play();
            });
        });

        // Smooth scroll feel
        messageScrollPane.getStylesheets().add(getClass().getResource("/org/to/telegramfinalproject/CSS/scrollpane.css").toExternalForm());
        messageScrollPane.setPannable(true);
        messageScrollPane.setFitToWidth(true);
        messageScrollPane.setFitToHeight(false);
        messageScrollPane.getContent().setOnScroll(event -> {
            double deltaY = event.getDeltaY() * 0.00003; // smaller = smoother
            messageScrollPane.setVvalue(messageScrollPane.getVvalue() - deltaY);
        });

        // React to theme changes everywhere
        themeManager.darkModeProperty().addListener((o, oldVal, isDark) -> syncIconsWithTheme());
    }

//    private void initCurrentUserId() {
//        try {
//            String meStr = org.to.telegramfinalproject.Client.Session
//                    .currentUser.getString("internal_uuid");
//            me = UUID.fromString(meStr);
//        } catch (Exception ignore) {
//            me = null;
//        }
//    }
//
//    private String formatWhen(LocalDateTime ts) {
//        if (ts == null) return "";
//        LocalDate today = LocalDate.now();
//        LocalDate d = ts.toLocalDate();
//
//        if (d.isEqual(today)) {
//            return FMT_HHMM.format(ts);
//        } else if (d.isEqual(today.minusDays(1))) {
//            return YESTERDAY_LABEL + " " + FMT_HHMM.format(ts);
//        } else {
//            return FMT_DATE_TIME.format(ts);
//        }
//    }
//
//    /** ISO → LocalDateTime (با پشتیبانی از Offset/Z) */
//    private LocalDateTime parseWhen(String iso) {
//        if (iso == null || iso.isEmpty()) return null;
//        try {
//            return OffsetDateTime.parse(iso).toLocalDateTime();
//        } catch (Exception ignore) {
//            try {
//                return LocalDateTime.parse(iso);
//            } catch (Exception e) {
//                return null;
//            }
//        }
//    }

    @FXML
    private void openSearchPanel() {
        MainController.getInstance().showSearchPanel();
    }

//    /**
//     * Called by main controller when opening a chat.
//     */
//    public void setChat(String chatName, String avatarPath) {
//        this.chatName = chatName;
//
//        // Header text (if present)
//        if (chatTitle != null) chatTitle.setText(chatName);
//        if (chatStatus != null) chatStatus.setText("last seen recently"); // or live status
//
//        // Load avatar if provided
//        if (userAvatar != null && avatarPath != null) {
//            try {
//                Image avatarImg = new Image(getClass().getResourceAsStream(avatarPath));
//                userAvatar.setImage(avatarImg);
//                Circle clip = new Circle(18, 18, 18); // x, y, radius
//                userAvatar.setClip(clip);
//            } catch (Exception e) {
//                System.err.println("Could not load avatar: " + avatarPath);
//            }
//        }
//
//        addSystemMessage("Chat with " + chatName + " opened.");
//        Platform.runLater(() -> messageInput.requestFocus());
//    }

    // ----- actions -----

//    private void sendMessage() {
//        String text = messageInput.getText() == null ? "" : messageInput.getText().trim();
//        if (!text.isEmpty()) {
//            addMessage("You", text);
//            messageInput.clear();
//            // TODO: send to server
//        }
//    }


    private void sendMessage() {
        // 0) Read and validate input
        String raw = messageInput.getText();
        String text = (raw == null) ? "" : raw.trim();
        if (text.isEmpty()) return;

        if (currentChat == null) {
            addSystemMessage("No chat is selected.");
            return;
        }

        // 1) Clear input immediately for good UX
        messageInput.clear();

        // 2) Snapshot chat info (must be final for lambdas)
        final UUID targetChatId   = currentChat.getId();
        final String targetType   = currentChat.getType();    // "private" | "group" | "channel"
        final String contentToSend = text;                    // effectively final

        // 3) Build the SAME JSON as your console method (for TEXT only)
        org.json.JSONObject req = new org.json.JSONObject();
        req.put("action", "send_message");
        req.put("receiver_type", targetType);
        req.put("receiver_id", targetChatId.toString());
        req.put("content", contentToSend);
        req.put("message_type", "TEXT");

        // 4) Send on a background thread
        new Thread(() -> {
            org.json.JSONObject resp;
            try {
                resp = org.to.telegramfinalproject.Client.ActionHandler.sendWithResponse(req);
            } catch (Exception ex) {
                ex.printStackTrace();
                Platform.runLater(() -> addSystemMessage("Send failed: " + ex.getMessage()));
                return;
            }

            // 5) Check status like your console method
            if (resp == null || !"success".equalsIgnoreCase(resp.optString("status"))) {
                String err = (resp != null) ? resp.optString("message", "No response") : "No response";
                Platform.runLater(() -> addSystemMessage("Send failed: " + err));
                return;
            }

            // 6) Extract fields (your console reads data.message_id; handle both shapes)
            org.json.JSONObject data = resp.optJSONObject("data");
            String messageId = null;
            String sendAtIso = null;
            if (data != null) {
                // If server returns { data: { message_id, send_at, ... } }
                messageId = data.optString("message_id", null);

                // Some servers nest: { data: { message: {...} } }
                if (messageId == null) {
                    org.json.JSONObject msgObj = data.optJSONObject("message");
                    if (msgObj != null) {
                        messageId = msgObj.optString("message_id", null);
                        sendAtIso = msgObj.optString("send_at", null);
                    }
                } else {
                    sendAtIso = data.optString("send_at", null);
                }
            }
            if (messageId == null) messageId = java.util.UUID.randomUUID().toString();

            final java.time.LocalDateTime ts =
                    (sendAtIso != null && !sendAtIso.isBlank()) ? parseWhen(sendAtIso)
                            : java.time.LocalDateTime.now();

            final String fMessageId = messageId;
            final java.time.LocalDateTime fTs = ts;

            // 7) Update UI on FX thread (render outgoing bubble + index for reply previews)
            Platform.runLater(() -> {
                // If user switched chats while sending, don’t render here
                if (currentChat == null || !currentChat.getId().equals(targetChatId)) return;

                addBubble(
                        true,                 // outgoing
                        "You",                // display name
                        "TEXT",               // message type
                        contentToSend,        // content
                        fTs,                  // timestamp
                        fMessageId,           // message_id
                        "", "", "",           // forwarded_from, forwarded_by, reply_to_id
                        false,                // edited
                        null                  // reactions
                );

                //Real time
                var mc = MainController.getInstance();
                if (mc != null) {
                    String preview = "You: " + (contentToSend.isBlank() ? "[Message]" : contentToSend);
                    mc.onChatUpdated(targetChatId, targetType, fTs, /*isIncoming*/ false, preview);
                }

                // Keep it in msgIndex for reply previews
                org.json.JSONObject idx = new org.json.JSONObject();
                idx.put("message_id",   fMessageId);
                idx.put("message_type", "TEXT");
                idx.put("content",      contentToSend);
                idx.put("sender_name",  "You");
                idx.put("sender_id",    (me != null) ? me.toString() : "");
                idx.put("send_at",      fTs.toString());
                msgIndex.put(fMessageId, idx);
            });
        }).start();
    }

    private void openFileChooser() {
        FileChooser fc = new FileChooser();
        fc.setTitle("Select a file to send");
        File file = fc.showOpenDialog(attachmentButton.getScene().getWindow());
        if (file != null) {
            System.out.println("Selected file: " + file.getAbsolutePath());
            // TODO: actually send file
            addSystemMessage("Attached file: " + file.getName());
        }
    }

    // ----- UI helpers -----

//    /**
//     * Add a normal message bubble (very simple for now).
//     */
//    public void addMessage(String sender, String content) {
//        Label msg = new Label(sender + ": " + content);
//        msg.setWrapText(true);
//
//        boolean dark = themeManager.isDarkMode();
//        String bubbleColor = dark ? "#20405a" : "#4fa8f0";
//        String textColor = dark ? "#e8f1f8" : "#0f141a";
//        msg.setStyle(
//                "-fx-background-color: " + bubbleColor + ";" +
//                        "-fx-text-fill: " + textColor + ";" +
//                        "-fx-padding: 6 10; -fx-background-radius: 10;"
//        );
//
//        messageContainer.getChildren().add(msg);
//    }

    private void addSystemMessage(String content) {
        Label sys = new Label(content);
        sys.setStyle("-fx-text-fill: gray; -fx-font-size: 11;");
        messageContainer.getChildren().add(sys);
        messageScrollPane.layout();
        messageScrollPane.setVvalue(1.0);
    }

    /**
     * Update all header/footer icons according to current theme.
     */
    private void syncIconsWithTheme() {
        boolean dark = themeManager.isDarkMode();
        // We use “_light” icons on dark backgrounds, and “_dark” on light backgrounds.
        String suffix = dark ? "_light.png" : "_dark.png";

        // attachment
        if (attachmentIcon != null) {
            attachmentIcon.setImage(loadIcon("attachment" + suffix));
        }
        // send
        if (sendIcon != null) {
            sendIcon.setImage(loadIcon("send_cyan2.png"));
        }
        // header icons
        if (searchIcon != null) {
            searchIcon.setImage(loadIcon("search" + suffix));
        }
        if (moreIcon != null) {
            moreIcon.setImage(loadIcon("more" + suffix));
        }

        // header text tint (if you’re not fully relying on CSS)
        if (chatTitle != null) chatTitle.setStyle(dark ? "-fx-text-fill:#e8f1f8;" : "-fx-text-fill:#0f141a;");
        if (chatStatus != null) chatStatus.setStyle(dark ? "-fx-text-fill:#8ea1b2;" : "-fx-text-fill:#7e8a97;");

        // View profile icon in more button
        ((ImageView) viewProfileItem.getGraphic())
                .setImage(loadIcon("view_profile" + suffix));
    }

    private Image loadIcon(String filename) {
        var url = getClass().getResource(ICON_BASE + filename);
        if (url == null) {
            System.err.println("Icon not found: " + ICON_BASE + filename);
            return null;
        }
        return new Image(url.toExternalForm());
    }

    public void showChat(ChatEntry entry) {
        this.currentChat = entry;

        chatTitle.setText(entry.getName());

        // آواتار پیش‌فرض بر اساس نوع
        // ChatPageController.showChat(...)
        if (entry.getImageUrl() != null && !entry.getImageUrl().isEmpty()) {
            Image img = AvatarLocalResolver.load(entry.getImageUrl());
            if (img != null) {
                userAvatar.setImage(img);
            } else {
                // ⬇️ فال‌بک بر اساس نوع
                setDefaultHeaderAvatarByType(entry.getType());
            }
        } else {
            setDefaultHeaderAvatarByType(entry.getType());
        }
//        userAvatar.setClip(new Circle(20, 20, 20));
        AvatarFX.circleClip(userAvatar, 36);



        fetchAndRenderHeader(entry);

        // پیام‌ها
        messageContainer.getChildren().clear();
        loadMessages(entry);
        markAsRead(entry);

        Platform.runLater(() -> messageInput.requestFocus());
    }


    private void loadMessages(ChatEntry entry) {
        JSONObject req = new JSONObject();
        req.put("action", "get_messages_UI");
        req.put("receiver_id", String.valueOf(entry.getId()));
        req.put("receiver_type", entry.getType());
        req.put("limit", 50);

        new Thread(() -> {
            JSONObject resp;
            try {
                resp = org.to.telegramfinalproject.Client.ActionHandler.sendWithResponse(req);
            } catch (Exception ex) {
                ex.printStackTrace();
                return;
            }
            if (resp == null) return;

            String status = "";
            try {
                status = resp.getString("status");
            } catch (Exception ignore) {
            }
            if (!"success".equals(status)) return;

            JSONObject data = null;
            try {
                data = resp.getJSONObject("data");
            } catch (Exception ignore) {
            }
            if (data == null) return;

            JSONArray arr = null;
            try {
                arr = data.getJSONArray("messages");
            } catch (Exception ignore) {
            }
            if (arr == null) return;

            JSONArray finalArr = arr;
            Platform.runLater(() -> renderMessages(finalArr));
        }).start();
    }


//    private void renderMessages(org.json.JSONArray arr) {
//        messageContainer.getChildren().clear();
//
//        String myId = Session.currentUser != null
//                ? Session.currentUser.optString("internal_uuid", "")
//                : "";
//
//        for (int i = 0; i < arr.length(); i++) {
//            org.json.JSONObject m = arr.getJSONObject(i);
//
//            String senderId    = m.has("sender_id")    && !m.isNull("sender_id")    ? m.getString("sender_id")    : "";
//            String senderName  = m.has("sender_name")  && !m.isNull("sender_name")  ? m.getString("sender_name")  : "";
//            String type        = m.has("message_type") && !m.isNull("message_type") ? m.getString("message_type") : "TEXT";
//            String content     = m.has("content")      && !m.isNull("content")      ? m.getString("content")      : "";
//            String whenStr     = m.has("send_at")      && !m.isNull("send_at")      ? m.getString("send_at")      : null;
//
//            boolean outgoing = senderId.equalsIgnoreCase(myId);
//
//            String display = outgoing ? "You"
//                    : (!senderName.isEmpty() ? senderName
//                    : (senderId.isEmpty() ? "Unknown"
//                    : senderId.substring(0, Math.min(8, senderId.length()))));
//
//            String text;
//            switch (type.toUpperCase()) {
//                case "IMAGE": text = "[Image]"; break;
//                case "AUDIO": text = "[Audio]"; break;
//                case "VIDEO": text = "[Video]"; break;
//                case "FILE":  text = "[File]";  break;
//                default:      text = content;   break;
//            }
//
//            LocalDateTime ts = parseWhen(whenStr);
//            addBubble(outgoing, display, text, ts);
//        }
//
//        messageScrollPane.layout();
//        messageScrollPane.setVvalue(1.0);
//    }


    private final java.util.Map<String, org.json.JSONObject> msgIndex = new java.util.HashMap<>();

    private void renderMessages(org.json.JSONArray list) {
        messageContainer.getChildren().clear();

        // برای reply-preview: ایندکس کردن پیام‌ها با message_id
        msgIndex.clear();
        for (int i = 0; i < list.length(); i++) {
            org.json.JSONObject m = list.getJSONObject(i);
            String mid = str(m, "message_id");
            if (!mid.isEmpty()) msgIndex.put(mid, m);
        }

        String myId = (Session.currentUser != null && Session.currentUser.has("internal_uuid"))
                ? Session.currentUser.getString("internal_uuid") : "";

        for (int i = 0; i < list.length(); i++) {
            org.json.JSONObject m = list.getJSONObject(i);

            String senderId    = str(m, "sender_id");
            String senderName  = str(m, "sender_name");
            String type        = str(m, "message_type");
            String content     = str(m, "content");
            String whenStr     = str(m, "send_at");
            String msgId       = str(m, "message_id");

            // فوروارد / ریپلای / ادیت / ری‌اکشن
            String fwdFrom     = nz(str(m, "forwarded_from"));
            String fwdBy       = nz(str(m, "forwarded_by"));
            String replyToId   = nz(str(m, "reply_to_id"));
            boolean edited     = bool(m, "is_edited");
            org.json.JSONArray reactions = arr(m, "reactions");

            boolean outgoing = senderId.equalsIgnoreCase(myId);
            if (senderName == null || senderName.isBlank()) {
                senderName = outgoing ? "You"
                        : (senderId == null || senderId.isBlank()
                        ? "Unknown"
                        : senderId.substring(0, Math.min(8, senderId.length())));
            }

            java.time.LocalDateTime ts = parseWhen(whenStr);

            // نمایش
            addBubble(outgoing, senderName, type, content, ts, msgId,
                    fwdFrom, fwdBy, replyToId, edited, reactions);
        }

        // کمی فاصله بین پیام‌ها
        messageContainer.setSpacing(8);
        messageScrollPane.layout();
        messageScrollPane.setVvalue(1.0);
    }

    private String shortId(String id){
        return (id==null||id.isEmpty()) ? "Unknown" : id.substring(0, Math.min(8,id.length()));
    }

    private void markAsRead(ChatEntry entry) {
        JSONObject readReq = new JSONObject();
        readReq.put("action", "mark_as_read");
        readReq.put("receiver_id", entry.getId().toString()); // internal_id
        readReq.put("receiver_type", entry.getType());
        ActionHandler.sendWithResponse(readReq);
    }

//    private void addBubble(boolean outgoing, String displayName, String content, LocalDateTime sentAt) {
//
//        Label meta = new Label(displayName + " • " + formatWhen(sentAt));
//        meta.setStyle("-fx-font-size: 11; -fx-text-fill: #7e8a97;");
//        meta.setWrapText(true);
//
//        Label msg = new Label(content);
//        msg.setWrapText(true);
//
//        boolean dark = themeManager.isDarkMode();
//        String mine   = dark ? "#2b7cff" : "#d8ecff";
//        String theirs = dark ? "#2c333a" : "#ffffff";
//        String bg     = outgoing ? mine : theirs;
//
//        msg.setStyle(
//                "-fx-background-color:" + bg + ";" +
//                        "-fx-padding:8 12;" +
//                        "-fx-background-radius:12;" +
//                        "-fx-max-width: 520;"
//        );
//        msg.setMinHeight(Region.USE_PREF_SIZE);
//
//        VBox bubble = new VBox(4, meta, msg);
//
//        javafx.scene.layout.HBox row = new javafx.scene.layout.HBox(bubble);
//        row.setFillHeight(true);
//        row.setSpacing(6);
//        row.setAlignment(outgoing
//                ? javafx.geometry.Pos.CENTER_RIGHT
//                : javafx.geometry.Pos.CENTER_LEFT);
//
//        messageContainer.getChildren().add(row);
//    }

    private void addBubble(
            boolean outgoing,
            String displayName,
            String type,
            String content,
            java.time.LocalDateTime sentAt,
            String messageId,
            String forwardedFrom,
            String forwardedBy,
            String replyToId,
            boolean edited,
            org.json.JSONArray reactions
    ) {
        String metaText = (displayName == null ? "" : displayName) + " • " + formatWhen(sentAt);
        if (edited) metaText += " (edited)";
        Label meta = new Label(metaText);
        meta.setStyle("-fx-font-size: 11; -fx-text-fill: #7e8a97;");
        meta.setWrapText(true);

        String t = type == null ? "" : type.trim().toUpperCase();
        boolean isText = t.isEmpty() ? (content != null && !content.isBlank()) : "TEXT".equals(t);
        String bodyText = isText ? (content == null ? "" : content) : bracketLabel(t);

        Label msg = new Label(bodyText);
        msg.setWrapText(true);

        boolean dark = themeManager.isDarkMode();
        String mine   = dark ? "#2b7cff" : "#d8ecff";
        String theirs = dark ? "#2c333a" : "#ffffff";
        String bg     = outgoing ? mine : theirs;

        msg.setStyle(
                "-fx-background-color:" + bg + ";" +
                        "-fx-padding:8 12;" +
                        "-fx-background-radius:12;" +
                        "-fx-max-width: 520;"
        );
        msg.setMinHeight(Region.USE_PREF_SIZE);

        // بدنه حباب
        VBox bubble = new VBox(4); // spacing عمودی داخل حباب
        bubble.getChildren().add(meta);

        // Forward header (اختیاری)
        if (hasVal(forwardedFrom) || hasVal(forwardedBy)) {
            bubble.getChildren().add(buildForwardHeader(forwardedFrom, forwardedBy));
        }

        // Reply preview (اختیاری)
        if (hasVal(replyToId)) {
            bubble.getChildren().add(buildReplyBoxFromIndex(replyToId));
        }

        // متن اصلی
        bubble.getChildren().add(msg);

        // Reactions (اختیاری)
        if (reactions != null && reactions.length() > 0) {
            bubble.getChildren().add(buildReactionsBarFromJson(reactions, dark));
        }

        // چیدمان راست/چپ
        javafx.scene.layout.HBox row = new javafx.scene.layout.HBox(bubble);
        row.setFillHeight(true);
        row.setSpacing(4);
        row.setAlignment(outgoing ? javafx.geometry.Pos.CENTER_RIGHT
                : javafx.geometry.Pos.CENTER_LEFT);

        row.setPadding(new javafx.geometry.Insets(2, 6, 2, 6));

        messageContainer.getChildren().add(row);
    }


    private String bracketLabel(String t) {
        String tt = (t == null) ? "" : t.trim().toUpperCase();
        switch (tt) {
            case "IMAGE": return "[Image]";
            case "AUDIO": return "[Audio]";
            case "VIDEO": return "[Video]";
            case "FILE":  return "[File]";
            default:      return "[Message]";
        }
    }


    private javafx.scene.Node buildForwardHeader(String forwardedFrom, String forwardedBy) {
        String from = hasVal(forwardedFrom) ? forwardedFrom.trim() : null;
        String by   = hasVal(forwardedBy)   ? forwardedBy.trim()   : null;

        String txt = (from != null && by != null) ? ("Forwarded from " + from + " by " + by)
                : (from != null) ? ("Forwarded from " + from)
                : (by   != null) ? ("Forwarded by "   + by)
                : "Forwarded";

        Label l = new Label(txt);
        l.setStyle("-fx-font-size: 11; -fx-text-fill: #5b8bb1;");
        l.setWrapText(true);
        return l;
    }

    private javafx.scene.Node buildReplyBoxFromIndex(String replyToId) {
        org.json.JSONObject r = msgIndex.get(replyToId);
        String from = "Unknown";
        String preview;

        if (r != null) {
            String rType    = r.optString("message_type", "TEXT");
            String rContent = r.optString("content", "");
            String rSender  = r.optString("sender_name", "");
            if (hasVal(rSender)) from = rSender;

            preview = "TEXT".equalsIgnoreCase(rType) ? rContent : bracketLabel(rType);
        } else {
            preview = "[Quoted message]";
        }

        Label t = new Label("Reply to " + from);
        t.setStyle("-fx-font-size: 11; -fx-text-fill: #5b8bb1; -fx-font-weight: bold;");

        Label p = new Label(ellipsize(preview == null ? "" : preview, 140));
        p.setWrapText(true);
        p.setStyle("-fx-font-size: 12; -fx-text-fill: #6b7c8a;");

        VBox box = new VBox(2, t, p);
        box.setStyle(
                "-fx-background-color: rgba(125,150,175,0.10);" +
                        "-fx-padding:6 8; -fx-background-radius:8;" +
                        "-fx-border-color:#7da0b8; -fx-border-width:0 0 0 2;"
        );
        return box;
    }


    private javafx.scene.Node buildReactionsBarFromJson(org.json.JSONArray arr, boolean dark) {
        javafx.scene.layout.HBox bar = new javafx.scene.layout.HBox(6);
        bar.setAlignment(javafx.geometry.Pos.CENTER_LEFT);
        for (int i = 0; i < arr.length(); i++) {
            org.json.JSONObject ro = arr.optJSONObject(i);
            if (ro == null) continue;
            String emoji = ro.optString("emoji", "👍");
            int count    = ro.optInt("count", 1);
            boolean byMe = ro.optBoolean("by_me", false);

            String chipBg = byMe ? (dark ? "#215a9f" : "#d1e8ff")
                    : (dark ? "#2f3942" : "#eef3f7");
            String text   = emoji + (count > 0 ? (" " + count) : "");
            Label chip = new Label(text);
            chip.setStyle(
                    "-fx-font-size: 12;" +
                            "-fx-background-radius: 12;" +
                            "-fx-padding: 2 8;" +
                            "-fx-background-color: " + chipBg + ";"
            );
            bar.getChildren().add(chip);
        }
        return bar;
    }


    private static String ellipsize(String s, int max) { return s.length() > max ? s.substring(0, max) + "…" : s; }

    public boolean isSameChat(UUID chatId, String type) {
        return currentChat != null
                && currentChat.getId().equals(chatId)
                && currentChat.getType().equalsIgnoreCase(type);
    }

    public void onRealTimeNewMessage(JSONObject m) {
        try {
            String chatIdStr = str(m,"receiver_id");
            String chatType  = str(m,"receiver_type");
            if (chatIdStr.isEmpty() || chatType.isEmpty()) return;

            UUID chatId = UUID.fromString(chatIdStr);
            if (!isSameChat(chatId, chatType)) {
                System.out.println("[UI] RT msg for another chat: " + chatId);
                return;
            }

            // id → message_id fallback
            if (!m.has("message_id") && m.has("id")) {
                m.put("message_id", m.getString("id"));
            }

            String senderName = hasVal(str(m,"sender_name")) ? str(m,"sender_name")
                    : (hasVal(str(m,"sender_id")) ? shortId(str(m,"sender_id")) : "Unknown");

            String type    = hasVal(str(m,"message_type")) ? str(m,"message_type") : "TEXT";
            String content = str(m,"content");
            String whenIso = str(m,"send_at");
            String msgId   = str(m,"message_id");

            LocalDateTime ts = parseWhen(whenIso);
            if (ts == null) ts = LocalDateTime.now();

            addBubble(false, senderName, type, content, ts, msgId,
                    str(m,"forwarded_from"), str(m,"forwarded_by"), str(m,"reply_to_id"),
                    bool(m,"is_edited"), arr(m,"reactions"));

            if (hasVal(msgId)) msgIndex.put(msgId, m);

            if (currentChat != null) markAsRead(currentChat);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private void fetchAndRenderHeader(ChatEntry entry) {
        JSONObject req = new JSONObject();
        req.put("action", "get_header_info");
        req.put("receiver_id", entry.getId().toString());
        req.put("receiver_type", entry.getType()); // باید "private" باشه

        // 👇 اضافه کن: آی‌دی کاربر فعلی (current user)
        UUID viewerId = UUID.fromString(Session.getUserUUID()); // هر جایی که نگه می‌داری
        if ("private".equalsIgnoreCase(entry.getType()) && viewerId != null) {
            req.put("viewer_id", viewerId.toString());
        }

        new Thread(() -> {
            JSONObject resp;
            try {
                resp = org.to.telegramfinalproject.Client.ActionHandler.sendWithResponse(req);
            } catch (Exception ex) { ex.printStackTrace(); return; }

            if (resp == null || !"success".equalsIgnoreCase(resp.optString("status"))) {
                System.err.println("get_header_info failed: " + (resp != null ? resp.optString("message") : "null resp"));
                return;
            }
            JSONObject data = resp.optJSONObject("data");
            if (data == null) return;

            Platform.runLater(() -> renderHeaderFromData(entry, data));
        }).start();
    }

    private void renderHeaderFromData(ChatEntry entry, JSONObject data) {
        String t = entry.getType() == null ? "" : entry.getType().toLowerCase();
        switch (t) {
            case "private" -> updatePrivateHeader(entry, data);
            case "group"   -> updateGroupHeader(entry, data);
            case "channel" -> updateChannelHeader(entry, data);
            default        -> chatStatus.setText("");
        }
    }

    private void updatePrivateHeader(ChatEntry entry, JSONObject data) {
        String name = nz(data.optString("profile_name", entry.getName()));
        chatTitle.setText(name);

        // other_user_id برای ریل‌تایم status
        String other = data.optString("other_user_id", "");
        if (!other.isBlank()) {
            try { entry.setOtherUserId(java.util.UUID.fromString(other)); } catch (Exception ignore) {}
        }

        // تصویر
        String img = data.optString("image_url", "");
        if (hasVal(img)) {
            try {
                Image im = AvatarLocalResolver.load(img);  // ⬅️
                if (im != null) userAvatar.setImage(im);
                userAvatar.setClip(new Circle(20, 20, 20));
            } catch (Exception ignore) {}
        }

        chatStatus.setText(userStatusText(
                data.optBoolean("online", false),
                data.optString("last_seen", null)
        ));

    }

    private void updateGroupHeader(ChatEntry entry, JSONObject data) {
        chatTitle.setText(nz(data.optString("group_name", entry.getName())));

        String img = data.optString("image_url", "");
        if (hasVal(img)) {
            try {
                Image im = AvatarLocalResolver.load(img);  // ⬅️
                if (im != null) userAvatar.setImage(im);
                userAvatar.setClip(new Circle(20, 20, 20));
            } catch (Exception ignore) {}
        }


        int members = data.optInt("member_count", 0);
        int online  = data.optInt("online_count", -1);
        chatStatus.setText(online >= 0 ? (members + " members, " + online + " online")
                : (members + " members"));
    }

    private void updateChannelHeader(ChatEntry entry, JSONObject data) {
        chatTitle.setText(nz(data.optString("channel_name", entry.getName())));

        String img = data.optString("image_url", "");
        if (hasVal(img)) {
            try {
                Image im = AvatarLocalResolver.load(img);  // ⬅️
                if (im != null) userAvatar.setImage(im);
                userAvatar.setClip(new Circle(20, 20, 20));
            } catch (Exception ignore) {}
        }


        int subs = data.optInt("member_count", 0);
        chatStatus.setText(subs + " subscribers");
    }

    public void onUserStatusChanged(String userUuid, String status, String lastSeenIso) {
        if (currentChat == null || !"private".equalsIgnoreCase(currentChat.getType())) return;
        var other = currentChat.getOtherUserId();
        if (other == null || !other.toString().equalsIgnoreCase(userUuid)) return;

        if ("online".equalsIgnoreCase(status)) {
            chatStatus.setText("online");
        } else {
            if (hasVal(lastSeenIso)) {
                var ts = parseWhen(lastSeenIso);
                chatStatus.setText(ts != null ? ("last seen " + formatWhen(ts)) : "last seen recently");
            } else {
                chatStatus.setText("last seen recently");
            }
        }
    }



    private String userStatusText(boolean online, String lastSeenIso) {
        if (online) return "online";

        LocalDateTime ts = parseWhen(lastSeenIso);
        if (ts == null) return "Last seen recently";

        LocalDate today = LocalDate.now();
        LocalDate d = ts.toLocalDate();

        if (d.isEqual(today)) {
            return FMT_HHMM.format(ts);
        }

        long days = ChronoUnit.DAYS.between(d, today);
        if (days > 30) {
            return "Last seen long time ago";
        }
        return "recently";
    }

    private void setDefaultHeaderAvatarByType(String type){
        String path = switch (type == null ? "" : type.toLowerCase()) {
            case "channel" -> "/org/to/telegramfinalproject/Avatars/default_channel_profile.png";
            case "group"   -> "/org/to/telegramfinalproject/Avatars/default_group_profile.png";
            default        -> "/org/to/telegramfinalproject/Avatars/default_user_profile.png";
        };
        userAvatar.setImage(new Image(
                java.util.Objects.requireNonNull(getClass().getResourceAsStream(path))
        ));
    }
}
