"use client";

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

import { api } from "@/lib/api/client";
import { useToast } from "@/components/Toast";
import type { AiMessageView } from "@/lib/api/types";
import type { AiKind } from "./aiConversation";

// AI 对话数据层（Workbench 级单实例）：懒加载本章 ∪ 项目级留痕 + 批量 append（乐观 + 正位对账）。
// 记录写入是 fail-soft 副作用——append 失败绝不 throw 进调用方、不阻塞生成/HITL（守不变量 #3 意图），
// 至多弹一次 toast。仿 useContextDrawerData：单飞哨兵 + reload nonce + 项目/章切换复位。

export type AiConversationStatus = "idle" | "loading" | "ready" | "error";

// 一条待落库 bubble（camelCase 入参，映射到 snake_case AiMessageInput）。
export interface AiTurnMessage {
  role: "author" | "ai";
  content: string;
  meta?: Record<string, unknown>;
}

// 一次已结束交换的一批 bubble（同 threadId 归组；批级字段提顶层，与后端 AppendRequest 对齐）。
export interface AiTurnInput {
  threadId: string;
  kind: AiKind;
  toolKey?: string | null;
  chapterNo: number | null;
  messages: AiTurnMessage[];
}

export interface UseAiConversationOptions {
  // ToolboxPage 无抽屉：静默直投——不 GET、不 toast、append fire-and-forget。
  silent?: boolean;
}

export interface UseAiConversation {
  status: AiConversationStatus;
  messages: AiMessageView[];
  // 首次抽屉打开 / 首次 append 触发懒加载；已加载或在途则空操作。silent 下为空操作。
  ensureLoaded: () => void;
  // 出错后重试拉取。
  reload: () => void;
  // 追加一批往复；乐观插入 + 成功正位对账 / 失败回滚。绝不 throw。
  appendMessages: (turn: AiTurnInput) => Promise<void>;
}

const LIST_PATH = "/projects/{project_id}/ai-messages" as const;

// 按 id 并集去重（base 在前，extra 仅补 base 未含的 id）——防加载/追加并发时行重复或被覆盖。
function mergeById(
  base: readonly AiMessageView[],
  extra: readonly AiMessageView[],
): AiMessageView[] {
  const ids = new Set(base.map((m) => m.id));
  return [...base, ...extra.filter((m) => !ids.has(m.id))];
}

export function useAiConversation(
  projectId: string,
  chapterNo: number,
  options: UseAiConversationOptions = {},
): UseAiConversation {
  const silent = options.silent ?? false;
  const currentKey = `${projectId}:${chapterNo}`;
  const [status, setStatus] = useState<AiConversationStatus>("idle");
  const [messages, setMessages] = useState<AiMessageView[]>([]);
  // 请求哪个 (project:chapter) 的记录；按 key 触发（而非布尔）——项目切换时残留请求不会误发到新项目。
  const [loadKey, setLoadKey] = useState<string | null>(null);
  const [reloadNonce, setReloadNonce] = useState(0);
  // 单飞哨兵：已发起过 GET 就不再重复（成功缓存 / 在途去重）；出错时复位以允许重试。
  const reqRef = useRef(false);
  const toast = useToast();

  // 项目/章切换 → 清缓存 + 复位（先于加载 effect 声明，保证同次提交先复位）。
  useEffect(() => {
    reqRef.current = false;
    setMessages([]);
    setStatus("idle");
    setLoadKey(null);
  }, [projectId, chapterNo]);

  useEffect(() => {
    if (silent || loadKey !== currentKey || reqRef.current) return;
    reqRef.current = true;
    let cancelled = false;
    setStatus("loading");
    void (async () => {
      try {
        const { data, error } = await api.GET(LIST_PATH, {
          params: { path: { project_id: projectId }, query: { chapter_no: chapterNo } },
        });
        if (cancelled) return;
        if (error || !data) {
          reqRef.current = false;
          setStatus("error");
          toast("AI 对话加载失败，请重试。", "error");
          return;
        }
        // 合并而非覆盖：不吞掉加载在途时已成功 append 的行。
        setMessages((prev) => mergeById(data.messages, prev));
        setStatus("ready");
      } catch {
        if (cancelled) return;
        reqRef.current = false;
        setStatus("error");
        toast("AI 对话加载异常，请检查网络。", "error");
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [projectId, chapterNo, currentKey, loadKey, reloadNonce, silent, toast]);

  const ensureLoaded = useCallback((): void => {
    if (silent) return;
    setLoadKey(currentKey);
  }, [silent, currentKey]);

  const reload = useCallback((): void => {
    reqRef.current = false;
    setStatus("idle");
    setLoadKey(currentKey);
    setReloadNonce((n) => n + 1);
  }, [currentKey]);

  const appendMessages = useCallback<UseAiConversation["appendMessages"]>(
    async (turn) => {
      ensureLoaded();
      const now = new Date().toISOString();
      // 乐观临时行（temp id + 本地 seq）；引用留存以便成功对账 / 失败回滚。
      const optimistic: AiMessageView[] = turn.messages.map((m, i) => ({
        id: `temp-${crypto.randomUUID()}`,
        project_id: projectId,
        chapter_no: turn.chapterNo,
        thread_id: turn.threadId,
        seq: i,
        kind: turn.kind,
        tool_key: turn.toolKey ?? null,
        role: m.role,
        content: m.content,
        meta: m.meta ?? {},
        created_at: now,
      }));
      if (!silent) {
        setMessages((prev) => [...prev, ...optimistic]);
      }
      const dropOptimistic = (prev: AiMessageView[]): AiMessageView[] =>
        prev.filter((m) => !optimistic.includes(m));
      try {
        const { data, error } = await api.POST(LIST_PATH, {
          params: { path: { project_id: projectId } },
          body: {
            thread_id: turn.threadId,
            chapter_no: turn.chapterNo,
            kind: turn.kind,
            tool_key: turn.toolKey ?? null,
            messages: turn.messages.map((m) => ({
              role: m.role,
              content: m.content,
              meta: m.meta ?? {},
            })),
          },
        });
        if (error || !data) {
          if (!silent) {
            setMessages(dropOptimistic);
            toast("对话记录保存失败（不影响本次生成）。", "error");
          }
          return;
        }
        // 正位对账：移除临时行，按服务器返回行去重合并（response 不回显 temp id，靠位置替换）。
        if (!silent) {
          setMessages((prev) => mergeById(dropOptimistic(prev), data.messages));
        }
      } catch {
        if (!silent) {
          setMessages(dropOptimistic);
          toast("对话记录保存异常（不影响本次生成）。", "error");
        }
      }
    },
    [ensureLoaded, projectId, silent, toast],
  );

  return { status, messages, ensureLoaded, reload, appendMessages };
}
