"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import Link from "next/link";
import { Smile, Clapperboard, Coins, X, Trash2, ArrowUpDown, ChevronDown, MoreHorizontal, Pencil, Sparkles, Clock, MessageSquare } from "lucide-react";
import { useClickOutside } from "@/lib/use-click-outside";
import { Avatar } from "./Avatar";
import { LikeButton } from "./LikeButton";
import { EmojiPickerPopover } from "./EmojiPickerPopover";
import { ThreadLines } from "./ThreadLines";
import { timeAgo } from "@/lib/utils";
import { useAuth } from "@/lib/auth-context";
import type { Comment } from "@/lib/types";
import clsx from "clsx";

export function CommentThread({
  postId,
  postAuthorUsername,
  onCommentPosted,
  onCommentDeleted,
  onClose,
}: {
  postId: string;
  onClose?: () => void;
  postAuthorUsername?: string;
  onCommentPosted?: () => void;
  onCommentDeleted?: (removedCount: number) => void;
}) {
  const { user } = useAuth();
  const [comments, setComments] = useState<Comment[]>([]);
  const [text, setText] = useState("");
    // Most relevant first, like Facebook: the conversation people are
  // actually having, rather than whatever arrived last.
  const [sort, setSort] = useState<"relevant" | "recent" | "top">("relevant");
  const [showEmoji, setShowEmoji] = useState(false);
  const [expandedReplies, setExpandedReplies] = useState<Set<string>>(new Set());
  const [showTip, setShowTip] = useState(false);
  const [tipAmount, setTipAmount] = useState("");
  const [tipping, setTipping] = useState(false);
  const [notice, setNotice] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const inputRef = useRef<HTMLInputElement>(null);

  const load = useCallback(() => {
    fetch(`/api/posts/${postId}/comments`)
      .then((r) => r.json())
      .then(setComments);
  }, [postId]);

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

  function insertEmoji(emoji: string) {
    setText((t) => t + emoji);
    setShowEmoji(false);
    inputRef.current?.focus();
  }

  function showNotYetWired(label: string) {
    setNotice(`${label} needs file storage — not wired up in this local build yet.`);
    setTimeout(() => setNotice(null), 2200);
  }

  async function sendTip() {
    if (!postAuthorUsername) return;
    const amt = Number(tipAmount);
    if (!Number.isFinite(amt) || amt <= 0) {
      setError("Enter a valid amount");
      return;
    }
    setTipping(true);
    setError(null);
    const res = await fetch("/api/wallet/tip", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ username: postAuthorUsername, amount: amt }),
    }).catch(() => null);
    setTipping(false);
    if (!res || !res.ok) {
      const data = res ? await res.json().catch(() => ({})) : {};
      setError(data.error || "Couldn't send that tip");
      return;
    }
    setShowTip(false);
    setTipAmount("");
    setNotice(`Sent $${amt} to @${postAuthorUsername}!`);
    setTimeout(() => setNotice(null), 2200);
  }

  // Posts a brand new top-level comment from the bottom composer.
  async function submit() {
    if (!text.trim()) return;
    const body = text;
    setError(null);
    setText("");
    setShowEmoji(false);
    const posted = await postComment(body, undefined);
    if (!posted) setText(body); // give the text back so nothing is lost
  }

  // Shared by the bottom composer (new top-level comments) and each
  // CommentRow's own inline reply box (parentId set).
  async function postComment(body: string, parentId: string | undefined): Promise<boolean> {
    const res = await fetch(`/api/posts/${postId}/comments`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ text: body, parentId }),
    }).catch(() => null);
    if (!res || !res.ok) {
      const data = res ? await res.json().catch(() => ({})) : {};
      setError(data.error || "Couldn't post that comment — try again.");
      return false;
    }
    // A reply you just posted should be visible immediately, not hidden
    // behind a collapsed "View replies" toggle.
    if (parentId) setExpandedReplies((s) => new Set(s).add(parentId));
    load();
    onCommentPosted?.();
    return true;
  }

  async function deleteComment(commentId: string) {
    // Count this comment plus any replies to it, since the server cascades
    // the delete — the parent post's visible count needs to drop by the
    // same amount.
    const removedCount = 1 + comments.filter((c) => c.parentId === commentId).length;
    const res = await fetch(`/api/posts/${postId}/comments/${commentId}`, {
      method: "DELETE",
    }).catch(() => null);
    if (res && res.ok) {
      load();
      onCommentDeleted?.(removedCount);
    }
  }

  async function editComment(commentId: string, newText: string) {
    const res = await fetch(`/api/posts/${postId}/comments/${commentId}`, {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ text: newText }),
    }).catch(() => null);
    if (res && res.ok) {
      load();
      return true;
    }
    return false;
  }

  async function toggleLike(commentId: string) {
    // Optimistic update so the pop animation feels instant, then reconcile
    // with whatever the server actually persisted.
    setComments((cs) =>
      cs.map((c) =>
        c.id === commentId ? { ...c, liked: !c.liked, likes: c.likes + (c.liked ? -1 : 1) } : c
      )
    );
    const res = await fetch(`/api/posts/${postId}/comments/${commentId}/like`, {
      method: "POST",
    }).catch(() => null);
    if (res && res.ok) {
      const updated = await res.json();
      setComments((cs) => cs.map((c) => (c.id === commentId ? { ...c, ...updated } : c)));
    }
  }

  const topLevel = comments.filter((c) => !c.parentId);
  const repliesOf = (id: string) => comments.filter((c) => c.parentId === id);

  const threadRef = useRef<HTMLDivElement>(null);
  // One connector per visible parent -> reply pair. Chained so a reply's own
  // reply links from the reply above it, matching the cascading look.
  // Memoised: a new array every render made the line-drawing callback new
  // every render too, and the two kept restarting each other.
  const threadLinks = useMemo(
    () =>
      topLevel.flatMap((c) => {
        const replies = expandedReplies.has(c.id)
          ? repliesOf(c.id)
          : repliesOf(c.id).slice(0, 1);
        return replies.map((r, i) => ({
          from: i === 0 ? c.id : replies[i - 1].id,
          to: r.id,
        }));
      }),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [comments, expandedReplies]
  );
  const sorted = [...topLevel].sort((a, b) => {
    // "Most relevant" weighs the conversation a comment started as well as
    // the likes it got, then prefers the newer of two equals — a reply is
    // a stronger signal of interest than a like.
    if (sort === "relevant") {
      const weight = (x: typeof a) =>
        x.likes + (comments.filter((r) => r.parentId === x.id).length * 3);
      return weight(b) - weight(a) || +new Date(b.createdAt) - +new Date(a.createdAt);
    }
    if (sort === "top") return b.likes - a.likes;
    return +new Date(b.createdAt) - +new Date(a.createdAt);
  });

  return (
    <div className="pb-24 md:pb-0">
      <div className="flex items-center justify-between px-1 py-3">
        <span className="ct-sorts">
          {(
            [
              ["relevant", "Most relevant", Sparkles],
              ["recent", "Newest", Clock],
              ["top", "All comments", MessageSquare],
            ] as const
          ).map(([id, label, Icon]) => (
            <button
              key={id}
              onClick={() => setSort(id)}
              className={clsx("ct-sort", sort === id && "on")}
            >
              <Icon size={13} />
              {label}
            </button>
          ))}
        </span>

        {onClose && (
          <button onClick={onClose} className="ct-hide" title="Hide comments">
            <ChevronDown size={16} className="rotate-180" />
          </button>
        )}
      </div>

      <div ref={threadRef} className="relative flex flex-col gap-4">
        <ThreadLines containerRef={threadRef} links={threadLinks} deps={[comments, expandedReplies]} />
        {sorted.map((c) => {
          const allReplies = repliesOf(c.id);
          const expanded = expandedReplies.has(c.id);
          const visibleReplies = expanded ? allReplies : allReplies.slice(0, 1);
          const hiddenCount = allReplies.length - visibleReplies.length;
          return (
            <div key={c.id} className="group/thread">
              <CommentRow
                comment={c}
                isOwn={user?.id === c.author.id}
                canReply={Boolean(user)}
                onSubmitReply={(text) => postComment(text, c.id)}
                onDelete={() => deleteComment(c.id)}
                onEdit={(text) => editComment(c.id, text)}
                onToggleLike={() => toggleLike(c.id)}
              />
              {visibleReplies.length > 0 && (
                <div className="relative">
                  {visibleReplies.map((r) => (
                    <div key={r.id} className="relative pl-11 pt-3">
                      <CommentRow
                        comment={r}
                        isOwn={user?.id === r.author.id}
                        canReply={Boolean(user)}
                        onSubmitReply={(text) => postComment(text, c.id)}
                        onEdit={(text) => editComment(r.id, text)}
                        onDelete={() => deleteComment(r.id)}
                        onToggleLike={() => toggleLike(r.id)}
                      />
                    </div>
                  ))}
                </div>
              )}
              {hiddenCount > 0 && (
                <button
                  onClick={() => setExpandedReplies((s) => new Set(s).add(c.id))}
                  className="ml-7 mt-1.5 flex items-center gap-1 text-xs font-semibold text-neutral-400 hover:text-neutral-600 transition-colors"
                >
                  <span className="w-6 h-px bg-neutral-300 dark:bg-neutral-700" />
                  View {hiddenCount} {hiddenCount === 1 ? "reply" : "replies"}
                </button>
              )}
              {expanded && allReplies.length > 1 && (
                <button
                  onClick={() =>
                    setExpandedReplies((s) => {
                      const next = new Set(s);
                      next.delete(c.id);
                      return next;
                    })
                  }
                  className="ml-7 mt-1.5 flex items-center gap-1 text-xs font-semibold text-neutral-400 hover:text-neutral-600 transition-colors"
                >
                  <span className="w-6 h-px bg-neutral-300 dark:bg-neutral-700" />
                  Hide replies
                </button>
              )}
            </div>
          );
        })}
        {sorted.length === 0 && (
          <p className="ct-empty">No comments yet — say something first.</p>
        )}
      </div>

      <div className="fixed bottom-0 left-0 right-0 md:relative md:mt-6 bg-white dark:bg-black border-t border-neutral-200 dark:border-neutral-800 px-4 py-3">
        {!user && (
          <p className="max-w-2xl mx-auto text-xs text-neutral-400 mb-2">
            Log in to comment.
          </p>
        )}
        {notice && (
          <p className="max-w-2xl mx-auto text-xs text-neutral-400 mb-2">{notice}</p>
        )}
        {error && (
          <p className="max-w-2xl mx-auto text-xs text-red-500 mb-2">{error}</p>
        )}
        {showEmoji && (
          <div className="absolute bottom-full left-0 right-0 mb-2 px-4 z-30">
            <div className="w-full rounded-2xl overflow-hidden shadow-2xl border border-neutral-200 dark:border-neutral-800">
              <EmojiPickerPopover
                onSelect={(emoji) => insertEmoji(emoji)}
                onClose={() => setShowEmoji(false)}
                height={320}
              />
            </div>
          </div>
        )}
        {showTip && (
          <div className="max-w-2xl mx-auto flex items-center gap-2 p-2 mb-2 border border-neutral-100 dark:border-neutral-800 rounded-xl">
            <Coins size={16} className="text-amber-500 shrink-0" />
            <input
              type="number"
              min={1}
              autoFocus
              value={tipAmount}
              onChange={(e) => setTipAmount(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && sendTip()}
              placeholder={`Tip @${postAuthorUsername} ($)`}
              className="flex-1 bg-transparent text-sm outline-none"
            />
            <button
              onClick={sendTip}
              disabled={tipping || !tipAmount}
              className="text-sm font-semibold text-amber-600 disabled:text-neutral-300 shrink-0"
            >
              {tipping ? "Sending…" : "Send"}
            </button>
          </div>
        )}
        <div className="max-w-2xl mx-auto flex items-center gap-2">
          <button
            onClick={() => setShowEmoji((v) => !v)}
            className={clsx(
              "shrink-0 transition-colors",
              showEmoji ? "text-black dark:text-white" : "text-neutral-400 hover:text-neutral-600"
            )}
          >
            <Smile size={20} />
          </button>
          <button
            onClick={() => showNotYetWired("GIF")}
            className="text-neutral-400 hover:text-neutral-600 shrink-0 transition-colors"
          >
            <Clapperboard size={20} />
          </button>
          <input
            ref={inputRef}
            disabled={!user}
            value={text}
            onChange={(e) => setText(e.target.value)}
            onKeyDown={(e) => e.key === "Enter" && submit()}
            placeholder="Write a comment..."
            maxLength={1000}
            className="flex-1 bg-neutral-100 dark:bg-neutral-900 rounded-full px-4 py-2 text-sm outline-none disabled:opacity-50"
          />
          {!postAuthorUsername ? null : postAuthorUsername === user?.username ? (
            <button
              onClick={() => {
                setNotice("You can't tip yourself.");
                setTimeout(() => setNotice(null), 2000);
              }}
              className="text-neutral-300 dark:text-neutral-700 shrink-0"
            >
              <Coins size={20} />
            </button>
          ) : (
            <button
              onClick={() => setShowTip((v) => !v)}
              className={clsx(
                "shrink-0 transition-colors",
                showTip ? "text-amber-600" : "text-amber-500 hover:text-amber-600"
              )}
              title={`Tip @${postAuthorUsername}`}
            >
              <Coins size={20} />
            </button>
          )}
          <button
            onClick={submit}
            disabled={!text.trim() || !user}
            className="text-sm font-semibold text-black dark:text-white disabled:text-neutral-300 shrink-0 transition-colors"
          >
            Post
          </button>
        </div>
      </div>
    </div>
  );
}

function CommentRow({
  comment,
  isOwn,
  canReply,
  onSubmitReply,
  onDelete,
  onEdit,
  onToggleLike,
}: {
  comment: Comment;
  isOwn: boolean;
  canReply: boolean;
  onSubmitReply: (text: string) => Promise<boolean>;
  onDelete: () => void;
  onEdit: (text: string) => Promise<boolean>;
  onToggleLike: () => void;
}) {
  /** The translated text, while it's being shown. */
  const [showTranslation, setShowTranslation] = useState<string | null>(null);

  const [confirming, setConfirming] = useState(false);
  const [editing, setEditing] = useState(false);
  const [editText, setEditText] = useState(comment.text);
  const [saving, setSaving] = useState(false);
  const [replying, setReplying] = useState(false);
  const [cmMenu, setCmMenu] = useState(false);
  const cmMenuRef = useClickOutside<HTMLDivElement>(cmMenu, () => setCmMenu(false));
  const [replyText, setReplyText] = useState("");
  const [replyEmoji, setReplyEmoji] = useState(false);
  const [sendingReply, setSendingReply] = useState(false);
  const replyInputRef = useRef<HTMLInputElement>(null);

  async function save() {
    if (!editText.trim()) return;
    setSaving(true);
    const ok = await onEdit(editText);
    setSaving(false);
    if (ok) setEditing(false);
  }

  async function sendReply() {
    if (!replyText.trim()) return;
    setSendingReply(true);
    const ok = await onSubmitReply(replyText);
    setSendingReply(false);
    if (ok) {
      setReplyText("");
      setReplying(false);
      setReplyEmoji(false);
    }
  }

  return (
    <div className="flex items-start gap-2.5 px-1 group">
      <Link href={`/${comment.author.username}`} data-thread-avatar={comment.id}>
        <Avatar user={comment.author} size={32} />
      </Link>
      <div className="flex-1 min-w-0">
        {editing ? (
          <div className="max-w-md">
            <input
              autoFocus
              value={editText}
              onChange={(e) => setEditText(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && save()}
              maxLength={1000}
              className="w-full bg-neutral-100 dark:bg-neutral-900 rounded-full px-3.5 py-2 text-sm outline-none"
            />
            <div className="flex items-center gap-3 mt-1 px-1 text-xs">
              <button
                onClick={save}
                disabled={saving || !editText.trim()}
                className="font-semibold text-black dark:text-white disabled:text-neutral-300 transition-colors"
              >
                {saving ? "Saving…" : "Save"}
              </button>
              <button
                onClick={() => {
                  setEditText(comment.text);
                  setEditing(false);
                }}
                className="text-neutral-400 hover:text-neutral-600 transition-colors"
              >
                Cancel
              </button>
            </div>
          </div>
        ) : (
          <>
            <div className="flex items-start justify-between gap-3">
              <p className="text-sm leading-relaxed pt-0.5">
                <Link
                  href={`/${comment.author.username}`}
                  className="font-semibold mr-1.5 hover:underline"
                >
                  {comment.author.name}
                </Link>
                <span className="text-neutral-800 dark:text-neutral-200">{comment.text}</span>
              </p>
              <span
                className={clsx(
                  "flex flex-col items-center shrink-0 pt-0.5 transition-opacity",
                  comment.liked ? "opacity-100" : "opacity-60 group-hover:opacity-100"
                )}
              >
                <LikeButton liked={Boolean(comment.liked)} size={13} onToggle={onToggleLike} />
                {comment.likes > 0 && (
                  <span className="text-[10px] text-neutral-400 mt-0.5">{comment.likes}</span>
                )}
              </span>
            </div>
            

            

            <div className="flex items-center gap-3 mt-1 px-1 text-xs text-neutral-400">
              <span>
                {timeAgo(comment.createdAt)}
                {comment.editedAt && " · edited"}
              </span>
              {canReply && (
                <button
                  onClick={() => {
                    setReplying((v) => {
                      const next = !v;
                      if (next && !replyText) {
                        setReplyText(`@${comment.author.username} `);
                      }
                      return next;
                    });
                    setTimeout(() => replyInputRef.current?.focus(), 0);
                  }}
                  className={clsx(
                    "font-medium transition-colors",
                    replying ? "text-black dark:text-white" : "hover:text-neutral-600"
                  )}
                >
                  Reply
                </button>
              )}
              {isOwn && (
                <div className="relative" ref={cmMenuRef}>
                  <button
                    onClick={() => setCmMenu((v) => !v)}
                    className="flex items-center px-1 text-neutral-400 hover:text-neutral-600 transition-colors"
                    title="More"
                  >
                    <MoreHorizontal size={14} />
                  </button>
                  {cmMenu && (
                    <div className="absolute left-0 top-full mt-1 w-32 bg-white dark:bg-neutral-900 shadow-xl rounded-xl border border-neutral-100 dark:border-neutral-800 py-1 z-30 text-sm">
                      <button
                        onClick={() => {
                          setCmMenu(false);
                          setEditing(true);
                        }}
                        className="w-full flex items-center gap-2 text-left px-3 py-2 hover:bg-neutral-50 dark:hover:bg-neutral-800"
                      >
                        <Pencil size={13} /> Edit
                      </button>
                      <button
                        onClick={() => {
                          setCmMenu(false);
                          onDelete();
                        }}
                        className="w-full flex items-center gap-2 text-left px-3 py-2 text-red-500 hover:bg-neutral-50 dark:hover:bg-neutral-800"
                      >
                        <Trash2 size={13} /> Delete
                      </button>
                    </div>
                  )}
                </div>
              )}
            </div>

            {/* Inline reply box — appears directly under THIS comment/reply,
                not in a single shared composer at the bottom of the thread. */}
            {replying && (
              <div className="mt-2 animate-[slideDown_.15s_ease-out]">
                {replyEmoji && (
                  <div className="mb-1.5 relative z-10 max-w-xs">
                    <EmojiPickerPopover
                      onSelect={(e) => {
                        setReplyText((t) => t + e);
                        setReplyEmoji(false);
                      }}
                      onClose={() => setReplyEmoji(false)}
                    />
                  </div>
                )}
                <div className="flex items-center gap-2">
                  <button
                    onClick={() => setReplyEmoji((v) => !v)}
                    className="text-neutral-400 hover:text-neutral-600 shrink-0 transition-colors"
                  >
                    <Smile size={16} />
                  </button>
                  <input
                    ref={replyInputRef}
                    value={replyText}
                    onChange={(e) => setReplyText(e.target.value)}
                    onKeyDown={(e) => e.key === "Enter" && sendReply()}
                    placeholder={`Reply to ${comment.author.name}...`}
                    maxLength={1000}
                    className="flex-1 bg-neutral-100 dark:bg-neutral-900 rounded-full px-3.5 py-1.5 text-sm outline-none"
                  />
                  <button
                    onClick={sendReply}
                    disabled={!replyText.trim() || sendingReply}
                    className="text-xs font-semibold text-black dark:text-white disabled:text-neutral-300 shrink-0 transition-colors"
                  >
                    {sendingReply ? "…" : "Post"}
                  </button>
                </div>
              </div>
            )}
          </>
        )}
      </div>
    </div>
  );
}
