import { useEffect, useReducer, useRef } from "react";
import type { FormEvent } from "react";
import type { SurfaceScope } from "./apiBridge";
import type { Viewer } from "./types";
import {
  deleteRecordComment,
  fetchRecordComments,
  postRecordComment,
} from "./recordCommentsApi";
import type { RecordComment } from "./recordCommentsApi";
import {
  initialRecordCommentsState,
  recordCommentsReducer,
} from "./recordCommentsState";

function initials(name: string): string {
  return (
    name
      .trim()
      .split(/\s+/)
      .slice(0, 2)
      .map((part) => part[0]?.toUpperCase())
      .join("") || "?"
  );
}

function when(value: string): string {
  const date = new Date(value);
  if (!Number.isFinite(date.getTime())) return value;
  return new Intl.DateTimeFormat(undefined, {
    dateStyle: "medium",
    timeStyle: "short",
  }).format(date);
}

export default function RecordComments({
  scope,
  pid,
  viewer,
}: {
  /**
   * ⭐ WAVE 19 (owner item 12) — WHICH DATABASE THIS RECORD BELONGS TO. Required, with no
   * default, for the reason the Insights tab states at `RecordDetail.tsx`'s `scope` prop: a pid
   * is only meaningful inside one topic, and asking the customer endpoint about a product's
   * CRC32 hash either 403s on a screen showing a product (the reported bug) or — on a
   * collision — answers 200 and files the comment against somebody's customer.
   */
  scope: SurfaceScope;
  pid: number;
  viewer?: Viewer;
}) {
  const [state, dispatch] = useReducer(
    recordCommentsReducer,
    pid,
    initialRecordCommentsState
  );
  const { comments, draft, loading, posting, error } = state;
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  useEffect(() => {
    const controller = new AbortController();
    dispatch({ type: "record", pid });
    fetchRecordComments(scope, pid, controller.signal)
      .then((comments) => dispatch({ type: "loaded", pid, comments }))
      .catch((reason: unknown) => {
        if (reason instanceof DOMException && reason.name === "AbortError") return;
        dispatch({
          type: "load-failed",
          pid,
          error: reason instanceof Error ? reason.message : "Comments could not be loaded.",
        });
      });
    return () => controller.abort();
    // ⚠ `scope` is in the deps beside `pid`: the reducer's stale-completion guard keys on the
    // PID alone, so a surface switch that landed on the same pid would otherwise accept the old
    // topic's feed. The mount is also keyed by scope+pid (RecordDetail), which makes this belt
    // and braces — kept because the guard here is the one that survives a host that forgets.
  }, [scope, pid]);

  const post = async (event: FormEvent) => {
    event.preventDefault();
    const body = draft.trim();
    if (!body || posting) return;
    const requestPid = pid;
    dispatch({ type: "posting", pid: requestPid });
    try {
      const comment = await postRecordComment(scope, requestPid, body);
      dispatch({ type: "posted", pid: requestPid, comment });
      if (requestPid === pid) textareaRef.current?.focus();
    } catch (reason) {
      dispatch({
        type: "post-failed",
        pid: requestPid,
        error: reason instanceof Error ? reason.message : "That comment was not saved.",
      });
    }
  };

  const remove = async (comment: RecordComment) => {
    const requestPid = pid;
    try {
      await deleteRecordComment(scope, requestPid, comment.id);
      dispatch({ type: "deleted", pid: requestPid, commentId: comment.id });
    } catch (reason) {
      dispatch({
        type: "delete-failed",
        pid: requestPid,
        error: reason instanceof Error ? reason.message : "That comment was not deleted.",
      });
    }
  };

  return (
    <aside className="cg-record-comments" aria-label="Record comments">
      <div className="cg-record-comments-head">
        <h2>Comments</h2>
        <span>{comments.length.toLocaleString()}</span>
      </div>
      <div className="cg-record-comment-feed" aria-live="polite">
        {/* wave17 GRID — item 3 / R6. The feed is `aria-live="polite"`, so the spinner's
            `aria-label` is what it announces; dropping it would make the wait silent. */}
        {loading && (
          <div className="cg-record-comment-state">
            <span className="lp-spin" role="status" aria-label="Loading" />
          </div>
        )}
        {!loading && comments.length === 0 && !error && (
          <div className="cg-record-comment-state">
            No comments yet. Add context for the team here.
          </div>
        )}
        {comments.map((comment) => {
          const canDelete =
            viewer?.isAdmin === true ||
            viewer?.name?.trim().toLowerCase() === comment.authorKey.toLowerCase();
          return (
            <article className="cg-record-comment" key={comment.id}>
              <div className="cg-record-comment-avatar" aria-hidden>
                {initials(comment.author)}
              </div>
              <div className="cg-record-comment-content">
                <div className="cg-record-comment-meta">
                  <strong>{comment.author}</strong>
                  <time dateTime={comment.createdAt}>{when(comment.createdAt)}</time>
                  {canDelete && (
                    <button
                      type="button"
                      className="cg-record-comment-delete"
                      aria-label={`Delete comment by ${comment.author}`}
                      title="Delete comment"
                      onClick={() => void remove(comment)}
                    >
                      ×
                    </button>
                  )}
                </div>
                <p>{comment.body}</p>
              </div>
            </article>
          );
        })}
        {error && <div className="cg-record-comment-error">{error}</div>}
      </div>
      <form className="cg-record-comment-compose" onSubmit={(event) => void post(event)}>
        <label htmlFor={`cg-comment-${pid}`}>Add a comment</label>
        <textarea
          ref={textareaRef}
          id={`cg-comment-${pid}`}
          value={draft}
          maxLength={4000}
          placeholder="Share an update or leave context…"
          onChange={(event) =>
            dispatch({ type: "draft", pid, draft: event.target.value })
          }
          onKeyDown={(event) => {
            if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
              event.preventDefault();
              event.currentTarget.form?.requestSubmit();
            }
          }}
        />
        <div className="cg-record-comment-compose-foot">
          <span>Ctrl/⌘ + Enter to post</span>
          <button type="submit" disabled={posting || !draft.trim()}>
            {posting ? "Posting…" : "Comment"}
          </button>
        </div>
      </form>
    </aside>
  );
}
