"use client";

import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { X } from "lucide-react";
import { Avatar } from "./Avatar";
import { CommentThread } from "./CommentThread";
import type { Post } from "@/lib/types";

/**
 * Comments in their own window, the way Facebook does it.
 *
 * A hundred comments inline pushes the rest of the feed off the screen and
 * leaves you scrolling past someone else's conversation to reach the next
 * post. In a window they scroll on their own, the post stays in view at the
 * top, and the box to write in is pinned to the bottom where it's always
 * reachable.
 */
export function CommentsModal({
  post,
  onClose,
  onCountChange,
}: {
  post: Post;
  onClose: () => void;
  onCountChange?: (n: number) => void;
}) {
  /** Comments added or removed while this window has been open. */
  const [posted, setPosted] = useState(0);

  const report = useRef(onCountChange);
  report.current = onCountChange;

  // The count when this window opened, captured once.
  //
  // Depending on post.commentCount was the loop: reporting a new count
  // changed it, which re-ran this, which reported again. Putting the
  // callback in a ref didn't help, because the count was the cycle rather
  // than the function.
  const baseCount = useRef(post.commentCount);

  useEffect(() => {
    if (posted !== 0) {
      report.current?.(Math.max(0, baseCount.current + posted));
    }
    // Only how many have been added or removed here.
  }, [posted]);

  // Escape closes it, and the page behind doesn't scroll while it's open.
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") onClose();
    };
    const previous = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    window.addEventListener("keydown", onKey);

    return () => {
      document.body.style.overflow = previous;
      window.removeEventListener("keydown", onKey);
    };
  }, [onClose]);

  // Rendered on <body>, not where it sits in the tree.
  //
  // A post card lifts itself on hover (transform: translateY(-4px) under
  // the Golden theme). A transformed element becomes the containing block
  // for position:fixed descendants, so the window was being trapped inside
  // the card it opened from — the size of the card, with the card's own
  // content painting over it. On body it can't happen.
  const [onBody, setOnBody] = useState(false);
  useEffect(() => setOnBody(true), []);
  if (!onBody) return null;

  return createPortal(
    <div className="cm-back" onClick={onClose}>
      <div className="cm-shell" onClick={(e) => e.stopPropagation()}>
        <div className="cm-bar">
          <span>{post.author.name}&apos;s post</span>
          <button onClick={onClose} className="cm-x" aria-label="Close">
            <X size={19} />
          </button>
        </div>

        <div className="cm-scroll">
          {/* A line of context, not the whole post again — the comments
              are what someone opened this for. */}
          {post.text && (
            <div className="cm-context">
              <Avatar user={post.author} size={20} />
              <b>{post.author.name}</b>
              <span>{post.text}</span>
            </div>
          )}

          <CommentThread
            postId={post.id}
            postAuthorUsername={post.author.username}
            onCommentPosted={() => setPosted((n) => n + 1)}
            onCommentDeleted={(gone) => setPosted((n) => n - gone)}
          />
        </div>
      </div>
    </div>,
    document.body,
  );
}
