import { useCallback, useEffect, useMemo, useRef, useState } from "react";

import { createComment, deleteComment, getReplies, getRootComments, updateComment } from "@/features/comments/api/comment-api";
import { CommentForm } from "@/features/comments/components/CommentForm";
import { CommentItem } from "@/features/comments/components/CommentItem";
import type { CommentPage, CommentRecord, CommentTarget } from "@/features/comments/model/types";
import { ApiError } from "@/shared/api/api-error";
import type { ApiClient } from "@/shared/api/client";
import { PageState } from "@/shared/ui/page-state";
import { ResourcePagination } from "@/shared/ui/resource-pagination";

type LoadState =
  | { readonly status: "loading" }
  | { readonly data: CommentPage; readonly status: "content" }
  | { readonly message: string; readonly status: "error" };

type ReplyEntry = {
  readonly page: number;
  readonly state: LoadState;
};

type ReplyState = Record<number, ReplyEntry>;

const pageSize = 20;

function getErrorMessage(error: unknown): string {
  return error instanceof ApiError ? error.message : "댓글을 처리하지 못했습니다.";
}

function canEdit(comment: CommentRecord, target: CommentTarget): boolean {
  return comment.writerId === target.creatorId;
}

export function CommentThread({ apiClient, canMutate = true, target }: { readonly apiClient: ApiClient; readonly canMutate?: boolean; readonly target: CommentTarget }) {
  const audioContentId = target.kind === "audio" ? target.contentId : "";
  const communityPostId = target.kind === "community" ? target.postId : "";
  const requestTarget = useMemo<CommentTarget>(() => target.kind === "audio"
    ? { kind: "audio", characterId: target.characterId, contentId: audioContentId, creatorId: target.creatorId }
    : { kind: "community", characterId: target.characterId, postId: communityPostId, creatorId: target.creatorId }, [audioContentId, communityPostId, target.characterId, target.creatorId, target.kind]);
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const [expandedRootIds, setExpandedRootIds] = useState<readonly number[]>([]);
  const [isSaving, setIsSaving] = useState(false);
  const [rootPage, setRootPage] = useState(0);
  const [rootReloadKey, setRootReloadKey] = useState(0);
  const [roots, setRoots] = useState<LoadState>({ status: "loading" });
  const [replies, setReplies] = useState<ReplyState>({});
  const isSavingRef = useRef(false);

  const loadReplies = useCallback(async function loadRepliesForPage(rootId: number, page = 0): Promise<void> {
    setReplies((current) => ({ ...current, [rootId]: { page, state: { status: "loading" } } }));
    try {
      const data = await getReplies(apiClient, requestTarget, { commentId: rootId, page, size: pageSize });
      if (page > 0 && data.items.length === 0 && data.totalCount <= page * pageSize) {
        await loadRepliesForPage(rootId, page - 1);
        return;
      }
      setReplies((current) => ({ ...current, [rootId]: { page, state: { data, status: "content" } } }));
    } catch (error: unknown) {
      setReplies((current) => ({ ...current, [rootId]: { page, state: { message: getErrorMessage(error), status: "error" } } }));
    }
  }, [apiClient, requestTarget]);

  useEffect(() => {
    let isCurrent = true;
    setRoots((current) => current.status === "content" ? current : { status: "loading" });
    void getRootComments(apiClient, requestTarget, { page: rootPage, size: pageSize })
      .then((data) => {
        if (isCurrent) {
          setRoots({ data, status: "content" });
        }
      })
      .catch((error: unknown) => {
        if (isCurrent) {
          setRoots({ message: getErrorMessage(error), status: "error" });
        }
      });

    return () => {
      isCurrent = false;
    };
  }, [apiClient, requestTarget, rootPage, rootReloadKey]);

  async function runMutation(work: () => Promise<null>, rootId?: number): Promise<boolean> {
    if (isSavingRef.current) {
      return false;
    }
    isSavingRef.current = true;
    setIsSaving(true);
    setErrorMessage(null);
    try {
      await work();
      setRootReloadKey((key) => key + 1);
      if (rootId !== undefined) {
        await loadReplies(rootId, replies[rootId]?.page ?? 0);
      }
      return true;
    } catch (error: unknown) {
      setErrorMessage(getErrorMessage(error));
      return false;
    } finally {
      isSavingRef.current = false;
      setIsSaving(false);
    }
  }

  async function createRoot(comment: string): Promise<boolean> {
    if (requestTarget.kind === "audio") {
      return runMutation(() => createComment(apiClient, requestTarget, { comment, parentId: null, isSecret: false, languageCode: null }));
    }
    return runMutation(() => createComment(apiClient, requestTarget, { comment, parentId: null, isSecret: false }));
  }

  async function createReply(rootId: number, comment: string): Promise<boolean> {
    if (requestTarget.kind === "audio") {
      return runMutation(() => createComment(apiClient, requestTarget, { comment, parentId: rootId, isSecret: false, languageCode: null }), rootId);
    }
    return runMutation(() => createComment(apiClient, requestTarget, { comment, parentId: rootId, isSecret: false }), rootId);
  }

  function toggleReplies(rootId: number) {
    setExpandedRootIds((current) => current.includes(rootId) ? current.filter((id) => id !== rootId) : [...current, rootId]);
    if (replies[rootId] === undefined) {
      void loadReplies(rootId, 0);
    }
  }

  function renderReplies(root: CommentRecord) {
    if (!expandedRootIds.includes(root.id)) {
      return null;
    }
    const entry = replies[root.id] ?? { page: 0, state: { status: "loading" } };
    const state = entry.state;
    if (state.status === "loading") {
      return <PageState state="loading" title="답글을 불러오는 중" />;
    }
    if (state.status === "error") {
      return <PageState description={state.message} onRetry={() => void loadReplies(root.id)} state="error" title="답글 조회 실패" />;
    }

    return (
      <section aria-label={`${root.comment} 답글`} className="ml-0 flex flex-col gap-3 border-l border-border pl-3 sm:ml-4">
        {canMutate ? <CommentForm errorId={`comment-reply-${root.id}-error`} isSaving={isSaving} label={`${root.comment}에 답글`} onSubmit={(comment) => createReply(root.id, comment)} submitLabel="답글 등록" /> : null}
        {state.data.items.map((reply) => (
          <CommentItem canDelete={canMutate} canEdit={canMutate && canEdit(reply, requestTarget)} comment={reply} isSaving={isSaving} key={reply.id} onDelete={() => void runMutation(() => deleteComment(apiClient, requestTarget, { commentId: reply.id }), root.id)} onEdit={(comment) => void runMutation(() => updateComment(apiClient, requestTarget, { commentId: reply.id, request: { comment } }), root.id)} />
        ))}
        {state.data.totalCount > pageSize ? <ResourcePagination data={{ totalCount: state.data.totalCount, page: entry.page, size: pageSize, hasNext: (entry.page + 1) * pageSize < state.data.totalCount, items: state.data.items }} onPageChange={(page) => void loadReplies(root.id, page)} onSizeChange={() => void loadReplies(root.id, 0)} sizeOptions={[20]} /> : null}
      </section>
    );
  }

  return (
    <section className="flex flex-col gap-4 rounded-lg border border-border bg-muted p-4" aria-labelledby="comment-thread-title">
      <div className="flex flex-col gap-1">
        <p className="text-xs font-semibold text-info">COMMENTS</p>
        <h2 className="text-xl font-semibold" id="comment-thread-title">댓글 관리</h2>
      </div>
      {canMutate ? <CommentForm errorId="comment-root-error" isSaving={isSaving} label="새 댓글" onSubmit={createRoot} submitLabel="댓글 등록" /> : null}
      {errorMessage === null ? null : <p className="rounded-md border border-destructive bg-card p-3 text-sm font-semibold text-destructive" role="alert">{errorMessage}</p>}
      {isSaving ? <p className="rounded-md border border-border bg-card p-3 text-sm font-semibold" role="status">댓글을 저장하는 중</p> : null}
      {roots.status === "loading" ? <PageState state="loading" title="댓글을 불러오는 중" /> : null}
      {roots.status === "error" ? <PageState description={roots.message} onRetry={() => setRootReloadKey((key) => key + 1)} state="error" title="댓글 조회 실패" /> : null}
      {roots.status === "content" ? (
        <>
          <p className="text-sm font-semibold text-muted-foreground">총 {roots.data.totalCount.toLocaleString("ko-KR")}개</p>
          {roots.data.totalCount === 0 ? <PageState description="새 댓글을 등록해 첫 대화를 시작할 수 있습니다." state="empty" title="댓글이 없습니다" /> : (
            <>
              <div className="flex flex-col gap-3">
                {roots.data.items.map((comment) => (
                  <div className="flex flex-col gap-3" key={comment.id}>
                    <CommentItem canDelete={canMutate} canEdit={canMutate && canEdit(comment, requestTarget)} comment={comment} isSaving={isSaving} onDelete={() => void runMutation(() => deleteComment(apiClient, requestTarget, { commentId: comment.id }))} onEdit={(nextComment) => void runMutation(() => updateComment(apiClient, requestTarget, { commentId: comment.id, request: { comment: nextComment } }))} onShowReplies={() => toggleReplies(comment.id)} replyActionLabel={comment.replyCount > 0 || expandedRootIds.includes(comment.id) ? "답글 보기" : requestTarget.kind === "audio" && canMutate ? "답글 작성" : undefined} />
                    {renderReplies(comment)}
                  </div>
                ))}
              </div>
              <ResourcePagination data={{ totalCount: roots.data.totalCount, page: rootPage, size: pageSize, hasNext: (rootPage + 1) * pageSize < roots.data.totalCount, items: roots.data.items }} onPageChange={setRootPage} onSizeChange={() => setRootPage(0)} sizeOptions={[20]} />
            </>
          )}
        </>
      ) : null}
    </section>
  );
}
