// ============================================================
// use-api.ts — 业务 API Hook 集合
//
// 基于 useAsyncData / useAsyncMutation 封装各模块 API，
// 组件只需调用 Hook 获取 { data, loading, error } 三元组，
// 不再需要在组件内手写 useEffect + cancelled flag + useState。
//
// 每个 Hook 自动处理：
//   - 登录状态门控（enabled: isLoggedIn）
//   - 竞态条件
//   - 错误日志
//   - 用户可读错误信息（errorMessage）
// ============================================================

import {useMemo, useState, useRef, useEffect, useCallback} from 'react';
import {useAppContext} from '@/app/providers/AppProvider';
import { TaskAPI, POLLING_STATUSES, type TaskListRequestParams, type TaskInfoItemResponseBody, type CreatTaskRequestBody, type TaskStatusResponseBody, type TaskStatusString } from '@/modules/home/controllers/task-api';
import { ModelAPI, MODEL_CATEGORY_LABELS, type ModelsListResponseBody, type ModelCategoryStringEnum } from '@/modules/home/controllers/model-api';
import { fetchBanners, type Banner } from '@/modules/home/controllers/banner-api';
import { AuthAPI, type UserInfoBody } from '@/modules/auth/controllers/auth-api';
import { VipAPI, type VipLevelsItem } from '@/modules/account/controllers/vip-api';
import { BillingAPI, type BalanceResponseBody, type LedgerItemResponseBody, type LedgerReqeustParam } from '@/modules/account/controllers/billing-api';
import {useAsyncData, UseAsyncDataReturn, useAsyncMutation} from './use-async';
import {getCachedModels, setCachedModels} from '@/shared/services/cache/model-cache';
import { upload } from '@/shared/services/http';

// ============================================================
// Banner
// ============================================================

/** 首页轮播图数据 Hook */
export function useBannerList() {
    const {isLoggedIn} = useAppContext();

    return useAsyncData<Banner[]>(
        (signal) => fetchBanners(signal),
        [],
        {enabled: isLoggedIn, label: 'banner-list'},
    );
}

// ============================================================
// 模型
// ============================================================

/** 模型列表数据 Hook（缓存优先 + 后台刷新）
 *
 *  策略：
 *    1. 启动时同步读取加密缓存 → 立即展示（避免每次打开都 loading）
 *    2. 异步拉取最新模型列表 → 覆盖缓存数据
 *    3. 拉取成功后将最新数据加密写入 localStorage
 *
 *  因后端 category 查询参数实际不可用（传参返回空列表），
 *  改为不带分类参数一次拉取全部，再由客户端根据响应中的
 *  category_name 字段手动分组。 */
export function useModelList() {
    const {isLoggedIn} = useAppContext();

    // 缓存即时数据（首次 render 同步读取，后续 API 返回后覆盖）
    const [instantData, setInstantData] = useState<ModelsListResponseBody[] | null>(() =>
        getCachedModels(),
    );

    const result: UseAsyncDataReturn<ModelsListResponseBody[]> = useAsyncData<ModelsListResponseBody[]>(
        async (signal) => {
            const models = await ModelAPI.fetchModels({page: 1, page_size: 200}, signal);
            // 异步写入加密缓存（best-effort，不影响数据流）
            setCachedModels(models).catch(() => {});
            setInstantData(models);
            return models;
        },
        [],
        {enabled: isLoggedIn, label: 'model-list'},
    );

    // 合并策略：API 数据 > 缓存即时数据
    const mergedData = result.data ?? instantData;

    // loading 仅在无任何数据时展示（缓存命中时不闪烁 loading）
    const loading = result.loading && mergedData === null;

    // 按 category_name 分组（后端返回的 category_name 为 slug 格式如 img2img）
    const groupedModels: Map<string, ModelsListResponseBody[]> = useMemo(() => {
        const groups = new Map<string, ModelsListResponseBody[]>();
        mergedData?.forEach((model: ModelsListResponseBody) => {
            const cat: ModelCategoryStringEnum = model.model_type;
            if (!groups.has(cat)) groups.set(cat, []);
            groups.get(cat)!.push(model);
        });
        return groups;
    }, [mergedData]);

    return {
        ...result,
        /** 合并后的模型数据（缓存优先） */
        data: mergedData,
        /** 仅在无缓存且正在加载时为 true */
        loading,
        /** 按 model_type 分组后的模型（key 为后端 slug） */
        groupedModels,
    };
}

// ============================================================
// 任务列表
// ============================================================

export interface UseTaskListParams {
    /** 完整的 API 请求参数（由调用方根据 antd Table onChange 构建） */
    requestParams: TaskListRequestParams;
    /** 外部传入的刷新信号（任意变化触发重新请求） */
    refreshSignal?: number;
}

/**
 * 任务列表 Hook（分页 + 筛选 + 排序）
 *
 * 调用方负责构建 TaskListRequestParams（包括 edition-aware 的 user_ids 逻辑），
 * 本 Hook 仅负责数据获取、竞态控制、错误日志。
 */
export function useTaskList(params: UseTaskListParams) {
    const {isLoggedIn} = useAppContext();
    const {requestParams, refreshSignal = 0} = params;

    return useAsyncData(
        (signal) => TaskAPI.getTaskList(requestParams, signal),
        [requestParams, refreshSignal],
        {enabled: isLoggedIn, label: 'task-list'},
    );
}

// ============================================================
// 任务详情
// ============================================================

/** 任务详情 Hook（taskId 为 null 时不请求） */
export function useTaskDetail(taskId: string | null | undefined) {
    const {isLoggedIn} = useAppContext();

    return useAsyncData<TaskInfoItemResponseBody>(
        (signal) => TaskAPI.getTaskInfo(taskId!, signal),
        [taskId],
        {enabled: isLoggedIn && !!taskId, label: 'task-detail'},
    );
}

// ============================================================
// 账户信息（用户信息 + VIP 等级，并行请求）
// ============================================================

/** 账户信息页数据 Hook — 返回 [userInfo, vipsInfo] 元组 */
export function useAccountInfo() {
    const { isLoggedIn } = useAppContext();
    return useAsyncData<[UserInfoBody, VipLevelsItem[]]>(
        (signal) => Promise.all([
            AuthAPI.getUserInfo(signal),
            VipAPI.getVipLevels(signal),
        ]),
        [],
        { enabled: isLoggedIn, label: 'account-info' },
    );
}

// ============================================================
// 账单
// ============================================================

/** 账户余额 Hook */
export function useBillingBalance() {
    const { isLoggedIn } = useAppContext();
    return useAsyncData<BalanceResponseBody>(
        (signal) => BillingAPI.getBalance(signal),
        [],
        { enabled: isLoggedIn, label: 'billing-balance' },
    );
}

/** 账单明细 Hook（支持分页参数） */
export function useBillingLedger(params: LedgerReqeustParam) {
    const { isLoggedIn } = useAppContext();
    return useAsyncData<LedgerItemResponseBody[]>(
        (signal) => BillingAPI.getLedger(params, signal),
        [params],
        { enabled: isLoggedIn, label: 'billing-ledger' },
    );
}

// ============================================================
// 提交任务
// ============================================================

/** 提交任务 Mutation Hook（device_id 由 API 层自动注入，调用方无需传入） */
export function useSubmitTask(options?: {
    onSuccess?: (data: TaskInfoItemResponseBody) => void;
    onError?: (err: Error) => void;
}) {
    return useAsyncMutation<TaskInfoItemResponseBody, [Omit<CreatTaskRequestBody, 'device_id'>]>(
        (body) => TaskAPI.submitTask(body),
        {
            label: 'submit-task',
            onSuccess: options?.onSuccess,
            onError: options?.onError,
        },
    );
}

// ============================================================
// 任务状态轮询
// ============================================================

/**
 * 批量轮询非终态任务状态，返回合并后的任务列表。
 *
 * - 自动识别 tasks 中的非终态任务（pending / submitted / processing）
 * - 调用 batchTaskStatus 批量查询状态
 * - 使用服务端返回的 next_poll_ms（本地限制 2s ~ 30s）自适应间隔
 * - 递归 setTimeout 而非 setInterval（避免请求堆积）
 * - onTerminal 回调：当某个任务变为终态时触发（用于触发完整列表刷新）
 */
export function useTaskPolling(
    tasks: TaskInfoItemResponseBody[],
    onTerminal?: () => void,
): TaskInfoItemResponseBody[] {
    const [merged, setMerged] = useState<TaskInfoItemResponseBody[]>(tasks);
    const mergedRef = useRef(merged);
    const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
    const mountedRef = useRef(true);

    // 保持 ref 与 state 同步，避免轮询闭包读到过期数据
    mergedRef.current = merged;

    // 源数据变化时同步（服务端拉取的新数据覆盖本地合并结果）
    useEffect(() => {
        setMerged(tasks);
    }, [tasks]);

    // 用 ref 稳定化 onTerminal 回调，避免 useEffect 因回调引用变化而重启
    const onTerminalRef = useRef(onTerminal);
    onTerminalRef.current = onTerminal;

    // 轮询循环：当 tasks（源数据）变化时重启
    useEffect(() => {
        mountedRef.current = true;

        // 检查源数据中是否有非终态任务
        const hasNonTerminal = tasks.some(
            t => POLLING_STATUSES.includes(t.status as TaskStatusString),
        );
        if (!hasNonTerminal) return;

        const poll = async () => {
            if (!mountedRef.current) return;

            // 从最新 merged 中取非终态 ID（而非闭包捕获的旧值）
            const currentMerged = mergedRef.current;
            const ids = currentMerged
                .filter(t => POLLING_STATUSES.includes(t.status as TaskStatusString))
                .map(t => t.id);

            if (ids.length === 0) return;

            try {
                const result = await TaskAPI.batchTaskStatus({ task_ids: ids });
                if (!mountedRef.current) return;

                const statusMap = new Map(
                    result.items
                        .filter((s): s is TaskStatusResponseBody => s !== null && s !== undefined)
                        .map(s => [s.task_id, s]),
                );

                let hasTerminal = false;

                setMerged(prev => prev.map(task => {
                    const st = statusMap.get(task.id);
                    if (!st || st.status === task.status) return task;

                    const isNowTerminal = !POLLING_STATUSES.includes(st.status as TaskStatusString);
                    if (isNowTerminal) hasTerminal = true;

                    return {
                        ...task,
                        status: st.status,
                        cost_cent: st.cost_cent ?? task.cost_cent,
                        error_code: st.error_code ?? task.error_code,
                        error_message: st.error_message ?? task.error_message,
                        finished_at: st.finished_at ?? task.finished_at,
                    };
                }));

                if (hasTerminal) onTerminalRef.current?.();

                // 检查合并后是否还有非终态任务（从 ref 读取最新值）
                const stillPending = mergedRef.current.some(
                    t => POLLING_STATUSES.includes(t.status as TaskStatusString),
                );

                if (stillPending && mountedRef.current) {
                    const delay = Math.max(2000, Math.min(30000, result.next_poll_ms || 5000));
                    timerRef.current = setTimeout(poll, delay);
                }
            } catch {
                if (mountedRef.current) {
                    // 出错后 10s 重试
                    timerRef.current = setTimeout(poll, 10000);
                }
            }
        };

        // 首次轮询延迟 2s（给服务端缓冲时间）
        timerRef.current = setTimeout(poll, 2000);

        return () => {
            mountedRef.current = false;
            if (timerRef.current) {
                clearTimeout(timerRef.current);
                timerRef.current = null;
            }
        };
    }, [tasks]);

    return merged;
}

// ============================================================
// 文件上传
// ============================================================

/** 上传接口路径（与后端 /api/v1/upload 对齐） */
const UPLOAD_ACTION = '/api/v1/upload';

/**
 * 文件上传 Hook — 返回 antd Upload 组件兼容的 customRequest。
 *
 * 设计要点：
 *   - 复用 request.ts 的 upload() 函数（自动携带 Token、超时、错误处理）
 *   - 通过 customRequest 集成到 antd Upload，而非直接设 action（避免 Token 缺失）
 *   - 上传成功后 antd 自动将 response 写入 file.response，供表单提交时提取 URL
 */
export function useFileUpload() {
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   const customRequest = useCallback((options: any) => {
      const file = options.file as File;
      const formData = new FormData();
      formData.append('file', file, options.filename || file.name);

      upload<{ url: string }>(UPLOAD_ACTION, formData, (percent) => {
         options.onProgress?.({ percent });
      })
         .then((result) => {
            options.onSuccess?.(result);
         })
         .catch((err) => {
            options.onError?.(err instanceof Error ? err : new Error(String(err)));
         });
   }, []);

   return { customRequest, uploadAction: UPLOAD_ACTION } as const;
}

// ---------- 重新导出类型和工具 ----------

export {MODEL_CATEGORY_LABELS};
export type {ModelCategoryStringEnum, ModelsListResponseBody, TaskInfoItemResponseBody, Banner};
