import { useState, useEffect, useCallback, useRef } from "react";
import { CheckCircle, Trash2, Send, MessageCircle } from "lucide-react";
import type { Editor } from "@tiptap/core";
import type { CommentStore, CommentData } from "../editor/comments";

interface Props {
  editor: Editor | null;
  commentStore: CommentStore | null;
  user: { name: string; color: string; avatarUrl?: string };
}

interface CommentPosition {
  id: string;
  top: number;
}

function buildMarkPositions(editor: Editor): Map<string, number> {
  const map = new Map<string, number>();
  editor.state.doc.descendants((node, pos) => {
    for (const mark of node.marks) {
      if (mark.type.name === "comment" && !map.has(mark.attrs.commentId)) {
        map.set(mark.attrs.commentId, pos);
      }
    }
  });
  return map;
}

export function CommentMarginIcons({ editor, commentStore, user }: Props) {
  const [positions, setPositions] = useState<CommentPosition[]>([]);
  const [activeId, setActiveId] = useState<string | null>(null);
  const [activeComment, setActiveComment] = useState<CommentData | null>(null);
  const [replyText, setReplyText] = useState("");
  const popoverRef = useRef<HTMLDivElement>(null);
  const replyInputRef = useRef<HTMLTextAreaElement>(null);

  const activeIdRef = useRef<string | null>(null);
  activeIdRef.current = activeId;

  const updatePositions = useCallback(() => {
    if (!editor || !commentStore) return;
    if (activeIdRef.current) return; // freeze while popover open
    const comments = commentStore.getAll().filter((c) => !c.resolved);
    if (comments.length === 0) {
      setPositions([]);
      return;
    }

    const scrollParent = editor.view.dom.closest(".editor-scroll");
    if (!scrollParent) return;
    const scrollTop = scrollParent.scrollTop;
    const scrollRect = scrollParent.getBoundingClientRect();

    const markPos = buildMarkPositions(editor);
    const result: CommentPosition[] = [];

    for (const c of comments) {
      const pos = markPos.get(c.id);
      if (pos === undefined) continue;
      try {
        const coords = editor.view.coordsAtPos(pos);
        const top = coords.top - scrollRect.top + scrollTop;
        result.push({ id: c.id, top });
      } catch { /* not mapped */ }
    }

    result.sort((a, b) => a.top - b.top);
    const MIN_GAP = 32;
    for (let i = 1; i < result.length; i++) {
      if (result[i].top < result[i - 1].top + MIN_GAP) {
        result[i].top = result[i - 1].top + MIN_GAP;
      }
    }
    setPositions(result);
  }, [editor, commentStore]);

  useEffect(() => {
    updatePositions();
    if (!commentStore) return;
    return commentStore.observe(updatePositions);
  }, [commentStore, updatePositions]);

  useEffect(() => {
    if (!editor) return;
    const scrollParent = editor.view.dom.closest(".editor-scroll");
    if (!scrollParent) return;

    let timer = 0;
    const debounced = () => {
      clearTimeout(timer);
      timer = window.setTimeout(updatePositions, 120);
    };
    scrollParent.addEventListener("scroll", debounced, { passive: true });
    editor.on("update", debounced);
    return () => {
      scrollParent.removeEventListener("scroll", debounced);
      editor.off("update", debounced);
      clearTimeout(timer);
    };
  }, [editor, updatePositions]);

  // Also open popover when clicking on a comment-mark in the text
  useEffect(() => {
    if (!editor || !commentStore) return;
    const handleSelection = () => {
      const { from, to } = editor.state.selection;
      if (from !== to) return;
      const resolved = editor.state.doc.resolve(from);
      for (const mark of resolved.marks()) {
        if (mark.type.name === "comment" && mark.attrs.commentId) {
          const id = mark.attrs.commentId as string;
          const comment = commentStore.get(id);
          if (comment && !comment.resolved) {
            setActiveId(id);
            setActiveComment(comment);
            setReplyText("");
            return;
          }
        }
      }
    };
    editor.on("selectionUpdate", handleSelection);
    return () => { editor.off("selectionUpdate", handleSelection); };
  }, [editor, commentStore]);

  // Refresh active comment data from store
  useEffect(() => {
    if (!activeId || !commentStore) return;
    const refresh = () => {
      const updated = commentStore.get(activeId);
      if (updated) setActiveComment(updated);
      else { setActiveComment(null); setActiveId(null); }
    };
    return commentStore.observe(refresh);
  }, [activeId, commentStore]);

  // Recalculate positions when popover closes (unfreeze)
  const prevActiveId = useRef<string | null>(null);
  useEffect(() => {
    if (prevActiveId.current && !activeId) {
      setTimeout(updatePositions, 50);
    }
    prevActiveId.current = activeId;
  }, [activeId, updatePositions]);

  // Close on outside click
  useEffect(() => {
    if (!activeId) return;
    const handler = (e: MouseEvent) => {
      if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) {
        const target = e.target as HTMLElement;
        if (target.closest(".comment-margin-icon")) return;
        setActiveId(null);
        setActiveComment(null);
      }
    };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [activeId]);

  const handleIconClick = (id: string) => {
    if (!commentStore) return;
    if (activeId === id) {
      setActiveId(null);
      setActiveComment(null);
      return;
    }
    const comment = commentStore.get(id);
    if (!comment) return;
    setActiveId(id);
    setActiveComment(comment);
    setReplyText("");
  };

  const handleReply = () => {
    if (!replyText.trim() || !commentStore || !activeId) return;
    commentStore.addReply(activeId, {
      id: `r_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
      author: user.name,
      authorColor: user.color,
      text: replyText.trim(),
      createdAt: Date.now(),
    });
    setReplyText("");
    setTimeout(() => replyInputRef.current?.focus(), 50);
  };

  const removeMarkAndData = useCallback((id: string) => {
    if (!commentStore || !editor) return;
    commentStore.remove(id);
    const { doc, tr } = editor.state;
    const markType = editor.schema.marks.comment;
    doc.descendants((node, pos) => {
      node.marks.forEach((mark) => {
        if (mark.type === markType && mark.attrs.commentId === id) {
          tr.removeMark(pos, pos + node.nodeSize, markType);
        }
      });
    });
    editor.view.dispatch(tr);
    setActiveId(null);
    setActiveComment(null);
  }, [commentStore, editor]);

  const activePos = positions.find((p) => p.id === activeId);

  const formatTime = (ts: number) =>
    new Date(ts).toLocaleString(undefined, {
      month: "short", day: "numeric", hour: "2-digit", minute: "2-digit",
    });

  return (
    <div className="comment-margin-strip">
      {positions.map((p) => (
        <div
          key={p.id}
          className={`comment-margin-icon ${activeId === p.id ? "comment-margin-icon--active" : ""}`}
          style={{ top: p.top }}
          onClick={() => handleIconClick(p.id)}
        >
          <MessageCircle size={14} />
        </div>
      ))}

      {activeComment && activePos && (
        <div
          ref={popoverRef}
          className="comment-popover"
          style={{ top: activePos.top }}
        >
          <div className="comment-popover__thread">
            <div className="comment-popover__message">
              <div className="comment-popover__header">
                <span className="comment-popover__author" style={{ color: activeComment.authorColor }}>
                  {activeComment.author}
                </span>
                <span className="comment-popover__time">{formatTime(activeComment.createdAt)}</span>
              </div>
              <div className="comment-popover__text">{activeComment.text}</div>
            </div>

            {activeComment.replies.map((reply) => (
              <div key={reply.id} className="comment-popover__message comment-popover__reply">
                <div className="comment-popover__header">
                  <span className="comment-popover__author" style={{ color: reply.authorColor }}>
                    {reply.author}
                  </span>
                  <span className="comment-popover__time">{formatTime(reply.createdAt)}</span>
                </div>
                <div className="comment-popover__text">{reply.text}</div>
              </div>
            ))}
          </div>

          <div className="comment-popover__input-row">
            <textarea
              ref={replyInputRef}
              className="comment-popover__input"
              rows={1}
              placeholder="Reply..."
              value={replyText}
              onChange={(e) => setReplyText(e.target.value)}
              onKeyDown={(e) => {
                if (e.key === "Enter" && !e.shiftKey) {
                  e.preventDefault();
                  handleReply();
                }
              }}
            />
            <button
              className="comment-popover__send"
              onClick={handleReply}
              disabled={!replyText.trim()}
              aria-label="Send reply"
            >
              <Send size={14} />
            </button>
          </div>

          <div className="comment-popover__actions">
            <button className="comment-popover__resolve" onClick={() => removeMarkAndData(activeComment.id)}>
              <CheckCircle size={14} />
              Resolve
            </button>
            <button className="comment-popover__delete" onClick={() => removeMarkAndData(activeComment.id)}>
              <Trash2 size={14} />
            </button>
          </div>
        </div>
      )}
    </div>
  );
}
