import { useMutation, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
import type { Dispatch, SetStateAction } from 'react';

import { parseMaterialsFromExcel } from '@/lib/materialImportParser';
import {
  createMaterials,
  deleteMaterialById,
  deleteMaterialsByIds,
  updateMaterial,
} from '@/services/materialService';
import { extractMaterialsFromDocument } from '@/services/documentExtractionService';
import {
  deleteSourceFileById,
  uploadSourceFile,
} from '@/services/sourceFileService';
import type {
  AuthUser,
  ProcurementMaterial,
  SourceFileRecord,
} from '@/types';

import { UPLOAD_SUCCESS_TOAST_OPTIONS } from '../lib/constants';
import {
  buildUploadedMaterialInput,
  isLocalDraftMaterial,
  isSpreadsheetFile,
  matchesMaterialDeletionTarget,
  renderUploadError,
} from '../lib/materialHelpers';

export interface UploadMaterialMutationResult {
  batchId: string;
  records: SourceFileRecord[];
  materials: ProcurementMaterial[];
  spreadsheetFileCount: number;
  attachmentCount: number;
}

export interface UseMaterialMutationsOptions {
  /** 当前选择的归属批次 ID（来自 uploadBatchId state） */
  getUploadBatchId: () => string;
  /** 当前登录用户（用于填充 created_by / uploaded_by 字段） */
  getUser: () => AuthUser | null;
  /** 本地 draft 列表 + setter；删除流程会读写它 */
  uploadedMaterials: ProcurementMaterial[];
  setUploadedMaterials: Dispatch<SetStateAction<ProcurementMaterial[]>>;
  /** 所有可见物料（用于 batch delete 区分 draft vs 落表） */
  getMaterials: () => ProcurementMaterial[];
  /** 删除/批删后清理展开行 */
  setExpandedIds: Dispatch<SetStateAction<Set<string>>>;
  /** 删除/上传/批删后清理选中行 */
  setSelectedKeys: Dispatch<SetStateAction<Set<string>>>;
  /** 编辑保存成功后关闭弹窗 */
  setEditingMaterial: Dispatch<SetStateAction<ProcurementMaterial | null>>;
  /** 上传成功后由 page 决定如何重置筛选/分页等 */
  onUploadSuccess?: (batchId: string) => void;
}

/**
 * 把 Materials 页的 4 个 mutation（上传 / 单删 / 批删 / 编辑保存）封装成一个 hook。
 *
 * 设计取舍：
 *   - mutation 需要读 page 层的若干 state（uploadBatchId / user / materials），
 *     用 getter 函数传入而不是直传值，避免 hook 内部 stale closure。
 *   - 副作用按归属切分：通用副作用（query 缓存失效、toast）留在 hook；
 *     页面状态副作用（搜索框重置、关闭编辑弹窗）通过 setter / 回调暴露给调用方。
 *   - upload 失败时尝试回滚：删除已上传 source_file 与已写入 materials；
 *     这部分行为不变，封装在 mutationFn 里。
 */
export function useMaterialMutations(opts: UseMaterialMutationsOptions) {
  const queryClient = useQueryClient();
  const {
    getUploadBatchId,
    getUser,
    uploadedMaterials,
    setUploadedMaterials,
    getMaterials,
    setExpandedIds,
    setSelectedKeys,
    setEditingMaterial,
    onUploadSuccess,
  } = opts;

  const uploadMutation = useMutation<UploadMaterialMutationResult, Error, File[]>({
    mutationFn: async (files: File[]) => {
      const batchId = getUploadBatchId();
      if (!batchId) {
        throw new Error('请先选择归属批次');
      }
      const user = getUser();
      const uploadedBy =
        user?.displayName?.trim() || user?.username?.trim() || user?.email?.trim() || '当前用户';
      const createdByUserId = user?.id?.trim() || undefined;

      const records: SourceFileRecord[] = [];
      const materials: ProcurementMaterial[] = [];
      let spreadsheetFileCount = 0;
      try {
        for (const file of files) {
          const record = await uploadSourceFile({ batchId, file, uploadedBy });
          records.push(record);
          if (isSpreadsheetFile(file.name)) {
            spreadsheetFileCount += 1;
            const parsedRows = await parseMaterialsFromExcel(file);
            const materialInputs = parsedRows.map(parsedRow =>
              buildUploadedMaterialInput(record, createdByUserId, parsedRow),
            );

            if (materialInputs.length > 0) {
              const insertedMaterials = await createMaterials(materialInputs);
              materials.push(...insertedMaterials);
            }
          } else {
            // ── 非 Excel 文件：走 markitdown → LLM 提取物料流程 ──
            try {
              const extractionResult = await extractMaterialsFromDocument(file, batchId);
              if (extractionResult.items.length > 0) {
                const materialInputs = extractionResult.items.map(parsedRow =>
                  buildUploadedMaterialInput(record, createdByUserId, parsedRow),
                );
                const insertedMaterials = await createMaterials(materialInputs);
                materials.push(...insertedMaterials);
                spreadsheetFileCount += 1; // 复用计数以触发"导入 N 条材料"的 toast
              } else {
                toast.info(
                  `文件 ${file.name} 未识别到采购物料，已作为附件上传。提取摘要: ${extractionResult.summary}`,
                );
              }
            } catch (extractError) {
              // 提取失败不阻断整体上传，文件已作为附件保留
              const errMsg = extractError instanceof Error ? extractError.message : String(extractError);
              console.error(`[文档提取] ${file.name} 失败:`, errMsg);
              toast.error(
                `文件 ${file.name} 物料提取失败: ${errMsg}\n文件已作为附件保留，请检查后端日志。`,
              );
            }
          }
        }
      } catch (error) {
        try {
          await deleteMaterialsByIds(materials.map(material => material.id));
        } catch {
          // Best-effort rollback only.
        }

        for (const record of [...records].reverse()) {
          try {
            await deleteSourceFileById(record.id);
          } catch {
            // Best-effort rollback only.
          }
        }

        throw error instanceof Error ? error : new Error('上传后写入采购清单失败。');
      }

      return {
        batchId,
        records,
        materials,
        spreadsheetFileCount,
        attachmentCount: records.length - spreadsheetFileCount,
      };
    },
    onSuccess: ({ batchId, materials }) => {
      if (materials.length > 0) {
        queryClient.setQueryData<ProcurementMaterial[]>(['materials-supabase'], prev => [
          ...(prev ?? []),
          ...materials,
        ]);
      }
      setSelectedKeys(new Set());
      queryClient.invalidateQueries({ queryKey: ['materials-source-files'] });
      queryClient.invalidateQueries({ queryKey: ['materials-supabase'] });
      onUploadSuccess?.(batchId);
      toast.success('解析文件成功', UPLOAD_SUCCESS_TOAST_OPTIONS);
    },
    onError: error => {
      toast.error(renderUploadError(error));
    },
  });

  const deleteMutation = useMutation<
    ProcurementMaterial,
    Error,
    ProcurementMaterial,
    {
      previousUploadedMaterials: ProcurementMaterial[];
      previousSupabaseMaterials?: ProcurementMaterial[];
      affectedLocalCount: number;
    }
  >({
    onMutate: async row => {
      const previousUploadedMaterials = uploadedMaterials;
      const previousSupabaseMaterials = queryClient.getQueryData<ProcurementMaterial[]>([
        'materials-supabase',
      ]);
      const isUploadedDraft = isLocalDraftMaterial(row);
      const affectedLocalCount = previousUploadedMaterials.filter(item =>
        matchesMaterialDeletionTarget(item, row),
      ).length;
      const affectedIds = new Set([
        ...previousUploadedMaterials
          .filter(item => matchesMaterialDeletionTarget(item, row))
          .map(item => item.id),
        ...(previousSupabaseMaterials ?? [])
          .filter(item => matchesMaterialDeletionTarget(item, row))
          .map(item => item.id),
      ]);

      await queryClient.cancelQueries({ queryKey: ['materials-supabase'] });

      if (isUploadedDraft) {
        setUploadedMaterials(prev =>
          prev.filter(item => !matchesMaterialDeletionTarget(item, row)),
        );
      } else {
        queryClient.setQueryData<ProcurementMaterial[]>(['materials-supabase'], prev => {
          if (!prev) {
            return prev;
          }
          return prev.filter(item => !matchesMaterialDeletionTarget(item, row));
        });
      }

      setExpandedIds(prev => {
        const next = new Set(prev);
        affectedIds.forEach(id => next.delete(id));
        return next;
      });
      setSelectedKeys(prev => {
        const next = new Set(prev);
        affectedIds.forEach(id => next.delete(id));
        return next;
      });

      return { previousUploadedMaterials, previousSupabaseMaterials, affectedLocalCount };
    },
    mutationFn: async row => {
      const isUploadedDraft = isLocalDraftMaterial(row);
      if (!isUploadedDraft) {
        await deleteMaterialById(row.id);
      }
      return row;
    },
    onSuccess: (_deletedRow, row, context) => {
      queryClient.invalidateQueries({ queryKey: ['materials-supabase'] });
      const removedCount = context?.affectedLocalCount ?? 0;
      toast.success(
        removedCount > 0 ? `已移除 ${removedCount} 条本地材料记录。` : `已删除材料：${row.material_name}`,
      );
    },
    onError: (error, _row, context) => {
      if (context?.previousUploadedMaterials) {
        setUploadedMaterials(context.previousUploadedMaterials);
      }
      if (context?.previousSupabaseMaterials !== undefined) {
        queryClient.setQueryData(['materials-supabase'], context.previousSupabaseMaterials);
      }
      toast.error(renderUploadError(error));
    },
  });

  const batchDeleteMutation = useMutation<void, Error, string[]>({
    mutationFn: async (ids: string[]) => {
      const allMaterials = getMaterials();
      const localDraftIds = ids.filter(id => {
        const row = allMaterials.find(m => m.id === id);
        return row && isLocalDraftMaterial(row);
      });
      const persistedIds = ids.filter(id => !localDraftIds.includes(id));

      if (localDraftIds.length > 0) {
        setUploadedMaterials(prev => prev.filter(item => !localDraftIds.includes(item.id)));
      }
      if (persistedIds.length > 0) {
        await deleteMaterialsByIds(persistedIds);
      }
    },
    onSuccess: (_data, ids) => {
      setSelectedKeys(prev => {
        const next = new Set(prev);
        ids.forEach(id => next.delete(id));
        return next;
      });
      setExpandedIds(prev => {
        const next = new Set(prev);
        ids.forEach(id => next.delete(id));
        return next;
      });
      queryClient.invalidateQueries({ queryKey: ['materials-supabase'] });
      toast.success(`已批量删除 ${ids.length} 条材料。`);
    },
    onError: error => {
      queryClient.invalidateQueries({ queryKey: ['materials-supabase'] });
      toast.error(renderUploadError(error));
    },
  });

  const updateMutation = useMutation({
    mutationFn: async (params: {
      id: string;
      updates: Partial<Omit<ProcurementMaterial, 'id' | 'created_at'>>;
    }) => {
      return updateMaterial(params.id, params.updates);
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['materials-supabase'] });
      toast.success('材料已更新');
      setEditingMaterial(null);
    },
    onError: error => {
      toast.error(`更新失败：${error.message}`);
    },
  });

  return {
    uploadMutation,
    deleteMutation,
    batchDeleteMutation,
    updateMutation,
  };
}
