"use client";

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

import { api } from "@/lib/api/client";
import {
  currentOverride,
  withExcluded,
  withPinToggled,
  withRecentN,
  withRestored,
  type InjectionEntityRef,
  type InjectionOverrideRequest,
  type InjectionResponse,
} from "./injection";

export interface UseInjection {
  data: InjectionResponse | null;
  loading: boolean;
  saving: boolean;
  // 首次加载失败（满屏错误框）与每次保存失败（悬浮提示）独立追踪，互不覆盖。
  loadError: string | null;
  saveError: string | null;
  clearSaveError: () => void;
  reload: () => void;
  retrySave: () => Promise<void>;
  togglePin: (ref: InjectionEntityRef) => Promise<void>;
  exclude: (ref: InjectionEntityRef) => Promise<void>;
  restore: (ref: InjectionEntityRef) => Promise<void>;
  setRecentN: (n: number) => Promise<void>;
}

const INJECTION_PATH =
  "/projects/{project_id}/chapters/{chapter_no}/injection" as const;

// 本章注入透明（B0 可控版）：读确定性 SelectionTrace + 作者覆盖；
// pin/排除/recent_n 经 PUT 覆盖 → 服务端回放新的确定结果（看到的=写章用的）。
// 写失败给可读文案、不改本地状态（天然回滚），不抛。
export function useInjection(projectId: string, chapterNo: number): UseInjection {
  const [data, setData] = useState<InjectionResponse | null>(null);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [loadError, setLoadError] = useState<string | null>(null);
  const [saveError, setSaveError] = useState<string | null>(null);
  // 重载计数：loadError「重试」自增 → 触发 effect 重新拉取（不需要把 loader 提到 effect 外）。
  const [reloadNonce, setReloadNonce] = useState(0);
  // 并发锁：保存在途时 ref 同步置位，拦下连点/快速切换造成的乱序覆盖（state 异步不可靠）。
  const savingRef = useRef(false);
  // 记下最近一次提交的覆盖，供 saveError「重试」重放（不重复构造 override）。
  const lastOverrideRef = useRef<InjectionOverrideRequest | null>(null);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    setLoadError(null);
    setSaveError(null);

    void (async () => {
      try {
        const { data: body, error: err } = await api.GET(INJECTION_PATH, {
          params: { path: { project_id: projectId, chapter_no: chapterNo } },
        });
        if (cancelled) return;
        if (err || !body) {
          setLoadError("注入信息暂不可用");
          setData(null);
        } else {
          setData(body);
        }
      } catch {
        // 网络层失败（后端不可达/CORS）：openapi-fetch 直接抛而非返回 error 信封。
        if (cancelled) return;
        setLoadError("注入信息暂不可用");
        setData(null);
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();

    return () => {
      cancelled = true;
    };
  }, [projectId, chapterNo, reloadNonce]);

  const save = useCallback(
    async (override: InjectionOverrideRequest) => {
      // 已有保存在途 → 早返回（避免并发 PUT 互相覆盖、回放结果乱序）。
      if (savingRef.current) return;
      lastOverrideRef.current = override;
      savingRef.current = true;
      setSaving(true);
      setSaveError(null);
      try {
        const { data: body, error: err } = await api.PUT(INJECTION_PATH, {
          params: { path: { project_id: projectId, chapter_no: chapterNo } },
          body: override,
        });
        if (err || !body) {
          // 保存失败：保留已加载的 data，仅设保存态错误（天然回滚 + 可重试）。
          setSaveError("保存注入设置失败，请重试");
        } else {
          setData(body); // 以服务端确定结果为准（不变量 #6）。
        }
      } catch {
        // 网络层失败：本地状态不动（天然回滚），给可读文案、不抛。
        setSaveError("保存注入设置失败，请重试");
      } finally {
        savingRef.current = false;
        setSaving(false);
      }
    },
    [projectId, chapterNo],
  );

  const mutate = useCallback(
    (fn: (o: InjectionOverrideRequest) => InjectionOverrideRequest) =>
      async (): Promise<void> => {
        if (!data) return;
        await save(fn(currentOverride(data)));
      },
    [data, save],
  );

  const togglePin = useCallback(
    (ref: InjectionEntityRef) => mutate((o) => withPinToggled(o, ref))(),
    [mutate],
  );
  const exclude = useCallback(
    (ref: InjectionEntityRef) => mutate((o) => withExcluded(o, ref))(),
    [mutate],
  );
  const restore = useCallback(
    (ref: InjectionEntityRef) => mutate((o) => withRestored(o, ref))(),
    [mutate],
  );
  const setRecentN = useCallback(
    (n: number) => mutate((o) => withRecentN(o, n))(),
    [mutate],
  );
  const clearSaveError = useCallback(() => setSaveError(null), []);
  const reload = useCallback(() => setReloadNonce((n) => n + 1), []);
  const retrySave = useCallback(async () => {
    const override = lastOverrideRef.current;
    if (!override) return;
    await save(override);
  }, [save]);

  return {
    data,
    loading,
    saving,
    loadError,
    saveError,
    clearSaveError,
    reload,
    retrySave,
    togglePin,
    exclude,
    restore,
    setRecentN,
  };
}
