/* global React, PR */
const { useState, useRef, useEffect, useCallback } = React;
const { Avatar } = PR;

const fmtTime = (iso) => {
  if (!iso) return "";
  const d = new Date(iso);
  const diff = Math.floor((Date.now() - d.getTime()) / 60000);
  if (diff < 1) return "just now";
  if (diff < 60) return diff + "m ago";
  if (diff < 1440) return Math.floor(diff / 60) + "h ago";
  return Math.floor(diff / 1440) + "d ago";
};

const EMOJI_PICKER = ["👍","👎","🎯","🔥","💡","✅","🤔","👀","🚀","❤️"];

// ── Lightbox ──────────────────────────────────────────────────────────────────

function Lightbox({ src, onClose }) {
  useEffect(() => {
    const h = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", h);
    return () => document.removeEventListener("keydown", h);
  }, [onClose]);
  return (
    <div className="lightbox" onClick={onClose}>
      <button className="lightbox-close" onClick={onClose}>×</button>
      <img src={src} alt="" onClick={(e) => e.stopPropagation()} />
    </div>
  );
}

// ── Attachment display ────────────────────────────────────────────────────────

function AttachmentDisplay({ attachments }) {
  const [lightbox, setLightbox] = useState(null);

  const images = attachments.filter(a => a.kind === "image" && a.url);
  const files  = attachments.filter(a => a.kind !== "image" || !a.url);

  return (
    <>
      {images.length > 0 && (
        <div className="comment-img-grid">
          {images.map((a, i) => (
            <img
              key={i}
              className="comment-img-thumb"
              src={a.url}
              alt={a.name}
              onClick={() => setLightbox(a.url)}
            />
          ))}
        </div>
      )}
      {files.length > 0 && (
        <div className="comment-attachments">
          {files.map((a, i) => (
            <div key={i} className="attachment">
              <span className="attachment-icon">
                <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6"/>
                </svg>
              </span>
              <span className="attachment-name">{a.name}</span>
              <span className="attachment-size">{a.size}</span>
              {a.url && (
                <a href={a.url} download={a.name} className="attachment-download" onClick={(e) => e.stopPropagation()}>
                  <svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"><path d="M12 3v12M7 11l5 5 5-5M5 20h14"/></svg>
                  Download
                </a>
              )}
            </div>
          ))}
        </div>
      )}
      {lightbox && <Lightbox src={lightbox} onClose={() => setLightbox(null)} />}
    </>
  );
}

function renderBody(text, authorMap) {
  const parts = text.split(/(@\w+(?:\s+\w+)?)/g);
  return parts.map((p, i) => {
    if (p.startsWith("@")) {
      const id = p.slice(1).trim();
      const author = authorMap[id];
      const display = author ? author.name : id;
      return <span key={i} className="mention">@{display}</span>;
    }
    return <React.Fragment key={i}>{p}</React.Fragment>;
  });
}

function EmojiMenu({ onPick, onClose }) {
  const ref = useRef(null);
  useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) onClose(); };
    document.addEventListener("mousedown", h);
    return () => document.removeEventListener("mousedown", h);
  }, [onClose]);
  return (
    <div ref={ref} className="emoji-menu">
      {EMOJI_PICKER.map(e => (
        <button key={e} className="emoji-btn" onMouseDown={(ev) => { ev.preventDefault(); onPick(e); onClose(); }}>{e}</button>
      ))}
    </div>
  );
}

function Comment({ c, authorMap, hypothesisId, onReload, depth = 0, currentUserId }) {
  const [showReply, setShowReply] = useState(false);
  const [showEmoji, setShowEmoji] = useState(false);
  const author = authorMap[c.author];

  const handleReact = async (emoji) => {
    await fetch(`/api/hypotheses/${hypothesisId}/comments/${c.id}/react`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ emoji }),
    });
    onReload();
  };

  return (
    <div className="comment">
      <Avatar author={author || { name: "?", color: "#888" }} />
      <div className="comment-body-wrap">
        <div className="comment-head">
          <span className="comment-author">{author?.name || c.author}</span>
          <span className="comment-time">{fmtTime(c.time)}</span>
        </div>
        <div className="comment-body">{renderBody(c.body, authorMap)}</div>

        {c.attachments && c.attachments.length > 0 && (
          <AttachmentDisplay attachments={c.attachments} />
        )}

        <div className="reactions">
          {c.reactions.map((r, i) => (
            <span
              key={i}
              className={"reaction" + (r.users.includes(String(currentUserId)) ? " reacted" : "")}
              title={r.users.map(u => authorMap[u]?.name || u).join(", ")}
              onClick={() => handleReact(r.emoji)}
            >
              <span>{r.emoji}</span>
              <span className="reaction-count">{r.users.length}</span>
            </span>
          ))}
          <div style={{ position: "relative", display: "inline-flex" }}>
            <button className="reaction-add" title="Add reaction" onClick={() => setShowEmoji(v => !v)}>
              <svg viewBox="0 0 24 24" width="11" height="11" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"><path d="M12 6v12M6 12h12"/></svg>
            </button>
            {showEmoji && <EmojiMenu onPick={handleReact} onClose={() => setShowEmoji(false)} />}
          </div>
        </div>

        <div className="comment-actions">
          {depth === 0 && (
            <button onClick={() => setShowReply(s => !s)}>
              <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"><path d="M9 14l-5-5 5-5M4 9h11a5 5 0 0 1 0 10h-3"/></svg>
              Reply
            </button>
          )}
        </div>

        {c.replies && c.replies.length > 0 && (
          <div className="replies">
            {c.replies.map(r => (
              <Comment
                key={r.id}
                c={r}
                authorMap={authorMap}
                hypothesisId={hypothesisId}
                onReload={onReload}
                depth={depth + 1}
                currentUserId={currentUserId}
              />
            ))}
          </div>
        )}

        {showReply && depth === 0 && (
          <div style={{ marginTop: 12 }}>
            <Composer
              authorMap={authorMap}
              placeholder={`Reply to ${author?.name || ""}…`}
              compact
              hypothesisId={hypothesisId}
              parentId={Number(c.id)}
              onPosted={() => { setShowReply(false); onReload(); }}
              currentUserId={currentUserId}
            />
          </div>
        )}
      </div>
    </div>
  );
}

function Composer({ authorMap, placeholder = "Write a comment… use @ to mention", compact, hypothesisId, parentId, onPosted, currentUserId }) {
  const [text, setText] = useState("");
  const [focused, setFocused] = useState(false);
  const [attached, setAttached] = useState([]);
  const [mentionState, setMentionState] = useState(null);
  const [activeIdx, setActiveIdx] = useState(0);
  const [sending, setSending] = useState(false);
  const taRef = useRef(null);
  const fileRef = useRef(null);

  const authors = Object.values(authorMap).filter(a => a.status !== "deactivated");
  const matches = mentionState
    ? authors.filter(a => a.name.toLowerCase().includes(mentionState.query.toLowerCase())).slice(0, 6)
    : [];

  const onChange = (e) => {
    const v = e.target.value;
    setText(v);
    const cur = e.target.selectionStart;
    const before = v.slice(0, cur);
    const m = before.match(/@(\w*)$/);
    if (m) { setMentionState({ query: m[1], pos: cur }); setActiveIdx(0); }
    else setMentionState(null);
  };

  const insertMention = (a) => {
    if (!taRef.current) return;
    const cur = taRef.current.selectionStart;
    const before = text.slice(0, cur);
    const after = text.slice(cur);
    const replaced = before.replace(/@\w*$/, `@${a.id} `);
    setText(replaced + after);
    setMentionState(null);
    setTimeout(() => taRef.current?.focus(), 0);
  };

  const onKeyDown = (e) => {
    if (mentionState && matches.length > 0) {
      if (e.key === "ArrowDown") { e.preventDefault(); setActiveIdx((activeIdx + 1) % matches.length); return; }
      if (e.key === "ArrowUp")   { e.preventDefault(); setActiveIdx((activeIdx - 1 + matches.length) % matches.length); return; }
      if (e.key === "Enter" || e.key === "Tab") { e.preventDefault(); insertMention(matches[activeIdx]); return; }
      if (e.key === "Escape") { setMentionState(null); return; }
    }
    if ((e.metaKey || e.ctrlKey) && e.key === "Enter") { e.preventDefault(); handleSend(); }
  };

  const onPickFile = async (e) => {
    const files = Array.from(e.target.files || []);
    e.target.value = "";
    for (const f of files) {
      const isImage = f.type.startsWith("image/");
      const sizeFmt = f.size < 1048576 ? `${Math.round(f.size / 1024)} KB` : `${(f.size / 1048576).toFixed(1)} MB`;
      const kind = isImage ? "image" : f.name.endsWith(".pdf") ? "pdf" : "file";

      // Upload to server so we have a permanent URL
      try {
        const form = new FormData();
        form.append("file", f);
        const resp = await fetch("/api/upload/attachment", { method: "POST", body: form });
        if (resp.ok) {
          const { url } = await resp.json();
          setAttached(a => [...a, { name: f.name, size: sizeFmt, kind, url }]);
        } else {
          // Fallback: store metadata only (no download/preview)
          setAttached(a => [...a, { name: f.name, size: sizeFmt, kind }]);
        }
      } catch {
        setAttached(a => [...a, { name: f.name, size: sizeFmt, kind }]);
      }
    }
  };

  const handleSend = async () => {
    if (!text.trim() && attached.length === 0) return;
    setSending(true);
    try {
      await fetch(`/api/hypotheses/${hypothesisId}/comments`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ body: text.trim(), parent_id: parentId || null, attachments: attached }),
      });
      setText("");
      setAttached([]);
      onPosted && onPosted();
    } finally {
      setSending(false);
    }
  };

  const currentAuthor = authorMap[String(currentUserId)];

  return (
    <div style={{ position: "relative" }}>
      <div className={"composer" + (focused ? " focused" : "")}>
        <Avatar author={currentAuthor || { name: "?", color: "#888" }} />
        <div className="composer-main">
          {attached.length > 0 && (
            <div className="composer-attached">
              {attached.map((a, i) => (
                <span key={i} className="attachment" style={{ paddingRight: 6 }}>
                  {a.kind === "image" && a.url
                    ? <img src={a.url} alt={a.name} style={{ width: 28, height: 22, objectFit: "cover", borderRadius: 3, verticalAlign: "middle" }} />
                    : <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6"/></svg>
                  }
                  <span className="attachment-name">{a.name}</span>
                  <span className="attachment-size">{a.size}</span>
                  <button onClick={() => setAttached(at => at.filter((_, j) => j !== i))}
                    style={{ border: 0, background: "transparent", color: "var(--text-3)", cursor: "pointer", padding: "0 2px" }}>×</button>
                </span>
              ))}
            </div>
          )}
          <textarea
            ref={taRef}
            className="composer-input"
            placeholder={placeholder}
            value={text}
            onChange={onChange}
            onKeyDown={onKeyDown}
            onFocus={() => setFocused(true)}
            onBlur={() => setTimeout(() => setFocused(false), 150)}
            rows={compact ? 2 : 3}
          />
          <div className="composer-toolbar">
            <button className="tb-btn" title="Mention" onClick={() => { setText(t => t + "@"); taRef.current?.focus(); }}>
              <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.9 7.9"/></svg>
            </button>
            <button className="tb-btn" title="Attach file" onClick={() => fileRef.current?.click()}>
              <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"><path d="M21 11l-9 9a5 5 0 0 1-7-7l9-9a3.5 3.5 0 0 1 5 5l-9 9a2 2 0 0 1-3-3l8-8"/></svg>
            </button>
            <input ref={fileRef} type="file" multiple style={{ display: "none" }} onChange={onPickFile} />
            <div className="composer-spacer"></div>
            <span className="composer-hint">⌘↵ to send</span>
            <button className="composer-send" disabled={(!text.trim() && attached.length === 0) || sending} onClick={handleSend}>
              {sending ? "…" : "Comment"}
            </button>
          </div>
        </div>
      </div>
      {mentionState && matches.length > 0 && (
        <div className="mention-pop" style={{ left: 56, bottom: -4, position: "absolute", transform: "translateY(100%)" }}>
          {matches.map((a, i) => (
            <div
              key={a.id}
              className={"mention-row" + (i === activeIdx ? " active" : "")}
              onMouseDown={(e) => { e.preventDefault(); insertMention(a); }}
            >
              <Avatar author={a} />
              <div>
                <div>{a.name}</div>
                <div className="em">{a.email || a.role}</div>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function CommentsPanel({ data, hypothesisId }) {
  const [comments, setComments] = useState([]);
  const [loading, setLoading] = useState(true);
  const currentUserId = data._meta?.currentUserId;

  const authorMap = Object.fromEntries(data.authors.map(a => [a.id, a]));

  const loadComments = useCallback(async () => {
    const resp = await fetch(`/api/hypotheses/${hypothesisId}/comments`);
    if (resp.ok) setComments(await resp.json());
    setLoading(false);
  }, [hypothesisId]);

  useEffect(() => { loadComments(); }, [loadComments]);

  const total = comments.reduce((s, c) => s + 1 + (c.replies?.length || 0), 0);

  if (loading) return <div style={{ padding: "20px 0", color: "var(--text-3)", fontSize: 13 }}>Loading comments…</div>;

  return (
    <div className="comments-section">
      <div className="section-title" style={{ marginBottom: 16, display: "flex", alignItems: "center", gap: 8 }}>
        <span>Comments</span>
        {total > 0 && <span style={{ fontFamily: "Geist Mono, monospace", color: "var(--text-3)", fontWeight: 400 }}>{total}</span>}
      </div>

      {comments.length === 0 ? (
        <div style={{ padding: "20px 0", color: "var(--text-3)", fontSize: 13 }}>
          No comments yet. Start the discussion.
        </div>
      ) : (
        <div className="comments-list">
          {comments.map(c => (
            <Comment
              key={c.id}
              c={c}
              authorMap={authorMap}
              hypothesisId={hypothesisId}
              onReload={loadComments}
              currentUserId={currentUserId}
            />
          ))}
        </div>
      )}

      <Composer
        authorMap={authorMap}
        hypothesisId={hypothesisId}
        onPosted={loadComments}
        currentUserId={currentUserId}
      />
    </div>
  );
}

window.PRComments = { CommentsPanel };
