import { beforeEach, describe, expect, it, vi } from 'vitest';

import { getSupabaseClient } from '@/lib/supabase';

vi.mock('@/lib/supabase', () => ({
  getSupabaseClient: vi.fn(),
}));

import {
  createManualStandardizedResultsFromFailures,
  deleteStandardizationTaskById,
  FAILURE_SUPPLEMENT_FIELDS,
  getRequiredSupplementFields,
  importStandardizedRows,
  mapFailureSupplementToStandardizedRow,
  summarizeCurrentStandardizedRows,
  type StandardizationFailureItem,
} from '@/services/standardizedService';
import { buildFailureSupplementPrintHtml } from '@/components/shared/FailureSupplementPrintView';

function createQuery(response: { data: unknown[] | null; error: { message: string } | null }) {
  const query = {
    in: vi.fn(() => query),
    select: vi.fn(() => query),
    then: (onFulfilled: (value: { data: unknown[] | null; error: { message: string } | null }) => unknown, onRejected?: (reason: unknown) => unknown) =>
      Promise.resolve(response).then(onFulfilled, onRejected),
    catch: (onRejected: (reason: unknown) => unknown) => Promise.resolve(response).catch(onRejected),
  };

  return query;
}

function createImportSupabaseMock(params: {
  procurementMaterials: unknown[];
  standardizedResults?: unknown[];
}) {
  const upsertCalls = {
    standardized_results: [] as unknown[][],
  };

  const client = {
    auth: {
      getUser: vi.fn(async () => ({ data: { user: { id: 'user-001' } }, error: null })),
    },
    from: vi.fn((table: string) => {
      if (table === 'procurement_materials') {
        return {
          select: vi.fn(() => createQuery({ data: params.procurementMaterials, error: null })),
        };
      }

      if (table === 'standardized_results') {
        return {
          select: vi.fn(() => createQuery({ data: params.standardizedResults ?? [], error: null })),
          upsert: vi.fn((rows: unknown[]) => {
            upsertCalls.standardized_results.push(rows);
            return {
              select: vi.fn(() => Promise.resolve({ data: rows, error: null })),
            };
          }),
        };
      }

      throw new Error(`Unexpected table: ${table}`);
    }),
  };

  return { client, upsertCalls };
}

function createDeleteTaskSupabaseMock(params: {
  taskStatus: 'pending' | 'processing' | 'stopping' | 'completed' | 'failed' | 'stopped';
  taskItems?: Array<{ id: string }>;
}) {
  const calls = {
    retryUnlinks: [] as Array<{ payload: Record<string, unknown>; field: string; values: string[] }>,
    resolvedUnlinks: [] as Array<{ payload: Record<string, unknown>; field: string; value: string }>,
    deletions: [] as Array<{ field: string; value: string }>,
  };

  const client = {
    from: vi.fn((table: string) => {
      if (table === 'standardization_tasks') {
        return {
          select: vi.fn((columns: string) => {
            if (columns !== 'id, status') {
              throw new Error(`Unexpected standardization_tasks select: ${columns}`);
            }

            return {
              eq: vi.fn((field: string, value: string) => {
                expect(field).toBe('id');
                expect(value).toBe('task-001');
                return {
                  maybeSingle: vi.fn(async () => ({
                    data: { id: value, status: params.taskStatus },
                    error: null,
                  })),
                };
              }),
            };
          }),
          delete: vi.fn(() => ({
            eq: vi.fn(async (field: string, value: string) => {
              calls.deletions.push({ field, value });
              return { error: null };
            }),
          })),
        };
      }

      if (table === 'standardization_task_items') {
        return {
          select: vi.fn((columns: string) => {
            if (columns !== 'id') {
              throw new Error(`Unexpected standardization_task_items select: ${columns}`);
            }

            return {
              eq: vi.fn(async (field: string, value: string) => {
                expect(field).toBe('task_id');
                expect(value).toBe('task-001');
                return {
                  data: params.taskItems ?? [],
                  error: null,
                };
              }),
            };
          }),
          update: vi.fn((payload: Record<string, unknown>) => ({
            in: vi.fn(async (field: string, values: string[]) => {
              calls.retryUnlinks.push({ payload, field, values });
              return { error: null };
            }),
            eq: vi.fn(async (field: string, value: string) => {
              calls.resolvedUnlinks.push({ payload, field, value });
              return { error: null };
            }),
          })),
        };
      }

      throw new Error(`Unexpected table: ${table}`);
    }),
  };

  return { client, calls };
}

function createManualCreateSupabaseMock(params: {
  failures: StandardizationFailureItem[];
  existingStandardizedResults?: Array<{ batch_id: string; material_id: string; created_by: string | null }>;
  upsertFailuresByFailureId?: Record<string, string>;
  resolveFailuresByFailureId?: Record<string, string>;
  resolveEmptyByFailureId?: string[];
}) {
  const calls = {
    upserts: [] as Array<{ row: Record<string, unknown>; options: Record<string, unknown> }>,
    resolutions: [] as Array<{ id: string; payload: Record<string, unknown> }>,
  };
  const upsertFailureByMaterialId = new Map(
    Object.entries(params.upsertFailuresByFailureId ?? {}).map(([failureId, message]) => {
      const failure = params.failures.find((item) => item.id === failureId);
      return [failure?.source_material_id, message];
    }),
  );

  const client = {
    auth: {
      getUser: vi.fn(async () => ({ data: { user: { id: 'user-002' } }, error: null })),
    },
    from: vi.fn((table: string) => {
      if (table === 'standardization_task_items') {
        return {
          select: vi.fn(() => ({
            eq: vi.fn((_field: string, value: string) => ({
              in: vi.fn(async (_inField: string, ids: string[]) => ({
                data: value === 'failed' ? params.failures.filter((failure) => ids.includes(failure.id)).map((failure) => ({
                  id: failure.id,
                  task_id: failure.task_id,
                  batch_id: failure.batch_id,
                  source_material_id: failure.source_material_id,
                  source_snapshot: failure.source_snapshot,
                  standardized_name: failure.standardized_name,
                  can_search_1688: failure.can_search_1688,
                  missing_required_fields: failure.missing_required_fields,
                  failure_reason: failure.failure_reason,
                  human_fix_hint: failure.human_fix_hint,
                  llm_model: failure.llm_model,
                  resolved_by_task_id: failure.resolved_by_task_id,
                  resolved_at: failure.resolved_at,
                  created_at: failure.created_at,
                  updated_at: failure.updated_at,
                })) : [],
                error: null,
              })),
            })),
          })),
          update: vi.fn((payload: Record<string, unknown>) => {
            let selectedId = '';

            const query = {
              eq: vi.fn((field: string, value: string) => {
                if (field === 'id') {
                  selectedId = value;
                }

                return query;
              }),
              select: vi.fn(async () => {
                calls.resolutions.push({ id: selectedId, payload });
                const resolveFailure = params.resolveFailuresByFailureId?.[selectedId];
                if (resolveFailure) {
                  return { data: null, error: { message: resolveFailure } };
                }

                if (params.resolveEmptyByFailureId?.includes(selectedId)) {
                  return { data: [], error: null };
                }

                return {
                  data: [{
                    id: selectedId,
                    resolved_at: payload.resolved_at,
                    updated_at: payload.updated_at,
                  }],
                  error: null,
                };
              }),
            };

            return query;
          }),
        };
      }

      if (table === 'standardized_results') {
        return {
          select: vi.fn(() => ({
            in: vi.fn(async (_field: string, values: string[]) => ({
              data: (params.existingStandardizedResults ?? []).filter((row) => values.includes(row.material_id)),
              error: null,
            })),
          })),
          upsert: vi.fn((rows: Array<Record<string, unknown>>, options: Record<string, unknown>) => {
            calls.upserts.push({ row: rows[0], options });
            const message = upsertFailureByMaterialId.get(String(rows[0].material_id));
            return {
              select: vi.fn(() => ({
                single: vi.fn(async () => ({
                  data: message ? null : { id: 'std-001' },
                  error: message ? { message } : null,
                })),
              })),
            };
          }),
        };
      }

      throw new Error(`Unexpected table: ${table}`);
    }),
  };

  return { client, calls };
}

function createFailureItem(overrides: Partial<StandardizationFailureItem> = {}): StandardizationFailureItem {
  return {
    id: 'failure-001',
    task_id: 'task-001',
    batch_id: 'BATCH-001',
    source_material_id: 'mat-001',
    original_name: '球墨铸铁井盖',
    original_spec: 'DN700',
    quantity: 12,
    unit: '套',
    invoice_type: '专票',
    standardized_name: '球墨铸铁井盖',
    can_search_1688: false,
    missing_required_fields: ['规格型号', '材质'],
    failure_reason: '缺少规格型号和材质，无法准确搜索',
    human_fix_hint: '请补充规格型号、材质',
    llm_model: 'glm-5.1',
    resolved_by_task_id: null,
    resolved_at: null,
    created_at: '2026-04-20T10:00:00.000Z',
    updated_at: '2026-04-20T10:00:00.000Z',
    source_snapshot: {
      id: 'mat-001',
      batch_id: 'BATCH-001',
      material_name: '球墨铸铁井盖',
      specification: 'DN700',
      quantity: 12,
      unit: '套',
      invoice_type: '专票',
      brand: '源品牌',
      remarks: '道路工程使用',
      delivery_address: '济南仓',
      technical_standard: 'GB/T 23858',
      material_requirement: '球墨铸铁',
      technical_requirement: '',
      applicable_conditions: '市政道路',
      test_requirement: '',
      acceptance_note: '',
      warranty_period: '',
      deadline: '',
      source_file: 'source.xlsx',
      created_at: '2026-04-20T09:00:00.000Z',
      created_by: 'user-001',
    },
    ...overrides,
  };
}

describe('standardizedService', () => {
  beforeEach(() => {
    vi.resetAllMocks();
  });

  it('keeps one shared supplement field order for UI and printable cards', () => {
    expect(FAILURE_SUPPLEMENT_FIELDS.map((field) => field.label)).toEqual([
      '规格型号',
      '品牌',
      '材质',
      '用途/场景',
      '执行标准',
      '补充说明',
    ]);
  });

  it('infers required supplement fields and appends stars to display labels', () => {
    const fields = getRequiredSupplementFields({
      missing_required_fields: ['规格型号', '材质要求'],
      failure_reason: '缺少使用场景，无法判断适配对象',
      human_fix_hint: '请补充执行标准或标准号',
    });

    expect(fields.map((field) => [field.label, field.required, field.displayLabel])).toEqual([
      ['规格型号', true, '规格型号 *'],
      ['品牌', false, '品牌'],
      ['材质', true, '材质 *'],
      ['用途/场景', true, '用途/场景 *'],
      ['执行标准', true, '执行标准 *'],
      ['补充说明', false, '补充说明'],
    ]);
  });

  it('requires spec model and supplement notes when required fields cannot be inferred exactly', () => {
    const fields = getRequiredSupplementFields({
      missing_required_fields: [],
      failure_reason: '信息不完整，无法判断采购对象',
      human_fix_hint: '',
    });

    expect(fields.filter((field) => field.required).map((field) => field.label)).toEqual([
      '规格型号',
      '补充说明',
    ]);
  });

  it('builds printable failure supplement cards without system identifiers or failure details', () => {
    const html = buildFailureSupplementPrintHtml([
      createFailureItem({
        human_fix_hint: '请补充规格型号、材质',
        failure_reason: '系统内部失败详情不应出现在补充卡',
      }),
      createFailureItem({
        id: 'failure-002',
        task_id: 'task-002',
        batch_id: 'BATCH-002',
        source_material_id: 'mat-002',
        human_fix_hint: '',
        missing_required_fields: ['品牌'],
      }),
    ]);

    expect(html).toContain('球墨铸铁井盖');
    expect(html).toContain('请补充规格型号、材质');
    expect(html).toContain('请补充标 * 的字段');
    expect(html).toContain('规格型号');
    expect(html).toContain('品牌');
    expect(html).toContain('材质');
    expect(html).toContain('用途/场景');
    expect(html).toContain('执行标准');
    expect(html).toContain('补充说明');
    expect(html).toContain('负责人（签字）：');
    expect(html).toContain('盖                章：');
    expect(html).toContain('page-break-before: always');
    expect(html).toContain('class="required">*</span>');

    expect(html).not.toContain('DN700');
    expect(html).not.toContain('12 套');
    expect(html).not.toContain('济南仓');
    expect(html).not.toContain('GB/T 23858');
    expect(html).not.toContain('道路工程使用');
    expect(html).not.toContain('BATCH-001');
    expect(html).not.toContain('BATCH-002');
    expect(html).not.toContain('task-001');
    expect(html).not.toContain('task-002');
    expect(html).not.toContain('mat-001');
    expect(html).not.toContain('mat-002');
    expect(html).not.toContain('系统内部失败详情');
    expect(html).not.toContain('失败原因');
    expect(html).not.toContain('处理状态');
  });

  it('maps a failure record and human supplement into a standardized_results upsert row', () => {
    const row = mapFailureSupplementToStandardizedRow({
      failure: createFailureItem(),
      supplement: {
        spec_model: '重型 DN700',
        brand: '晋钢',
        material: '球墨铸铁 QT500-7',
        usage_scene: '市政道路检查井',
        execution_standard: 'GB/T 23858-2009',
        supplement_notes: '承重 D400',
      },
      currentUserId: 'user-002',
    });

    expect(row).toEqual(expect.objectContaining({
      batch_id: 'BATCH-001',
      material_id: 'mat-001',
      source_type: 'manual',
      invoice_type: '专票',
      original_name: '球墨铸铁井盖',
      original_spec: 'DN700',
      standardized_name: '球墨铸铁井盖',
      standardized_spec: '重型 DN700',
      standardized_unit: '套',
      quantity: 12,
      brand: '晋钢',
      delivery_address: '济南仓',
      primary_search_term: '球墨铸铁井盖 重型 DN700 晋钢',
      product_type: '市政道路检查井',
      ai_reasoning: '人工补充入成功池；执行标准：GB/T 23858-2009',
      created_by: 'user-002',
      updated_by: 'user-002',
    }));
    expect(row.notes).toContain('材质：球墨铸铁 QT500-7');
    expect(row.notes).toContain('用途/场景：市政道路检查井');
    expect(row.notes).toContain('执行标准：GB/T 23858-2009');
    expect(row.alt_search_terms.length).toBeGreaterThan(0);
  });

  it('falls back to original values for direct manual success insertion', () => {
    const row = mapFailureSupplementToStandardizedRow({
      failure: createFailureItem({
        standardized_name: '',
        llm_model: '',
        source_snapshot: {
          ...createFailureItem().source_snapshot,
          specification: '',
          brand: '',
          remarks: '',
          material_requirement: '',
          applicable_conditions: '',
          technical_standard: '',
        },
      }),
      supplement: {},
      currentUserId: null,
    });

    expect(row).toEqual(expect.objectContaining({
      standardized_name: '球墨铸铁井盖',
      standardized_spec: '',
      brand: '',
      primary_search_term: '球墨铸铁井盖',
      notes: '',
      llm_model: '',
      ai_reasoning: '人工补充入成功池',
      created_by: null,
      updated_by: null,
    }));
  });

  it('creates manual standardized results and marks only successful failures resolved', async () => {
    const { client, calls } = createManualCreateSupabaseMock({
      failures: [
        createFailureItem({ id: 'failure-001', source_material_id: 'mat-001' }),
        createFailureItem({ id: 'failure-002', source_material_id: 'mat-002' }),
      ],
      upsertFailuresByFailureId: {
        'failure-002': 'duplicate key violation',
      },
    });

    vi.mocked(getSupabaseClient).mockReturnValue(client as unknown as ReturnType<typeof getSupabaseClient>);

    const result = await createManualStandardizedResultsFromFailures([
      {
        failureId: 'failure-001',
        supplement: {
          spec_model: 'D400 DN700',
          brand: '晋钢',
          material: '球墨铸铁',
        },
      },
      {
        failureId: 'failure-002',
        supplement: { spec_model: '失败项' },
      },
      {
        failureId: 'missing-failure',
        supplement: {},
      },
    ]);

    expect(result.successCount).toBe(1);
    expect(result.failedCount).toBe(2);
    expect(result.successes).toEqual([
      expect.objectContaining({
        failureId: 'failure-001',
        materialId: 'mat-001',
        batchId: 'BATCH-001',
      }),
    ]);
    expect(result.failures).toEqual([
      { failureId: 'failure-002', error: '写入标准化成功数据失败：duplicate key violation' },
      { failureId: 'missing-failure', error: '未找到可处理的标准化失败记录' },
    ]);

    expect(calls.upserts).toHaveLength(2);
    expect(calls.upserts[0]).toEqual(expect.objectContaining({
      options: { onConflict: 'batch_id,material_id' },
      row: expect.objectContaining({
        material_id: 'mat-001',
        source_type: 'manual',
        standardized_spec: 'D400 DN700',
        brand: '晋钢',
        created_by: 'user-002',
        updated_by: 'user-002',
      }),
    }));
    expect(calls.resolutions).toHaveLength(1);
    expect(calls.resolutions[0]).toEqual(expect.objectContaining({
      id: 'failure-001',
      payload: expect.objectContaining({
        resolved_by_task_id: null,
      }),
    }));
    expect(calls.resolutions[0].payload.resolved_at).toEqual(expect.any(String));
  });

  it('reports resolution update failure after a successful manual upsert', async () => {
    const { client, calls } = createManualCreateSupabaseMock({
      failures: [createFailureItem({ id: 'failure-001', source_material_id: 'mat-001' })],
      resolveFailuresByFailureId: {
        'failure-001': 'permission denied',
      },
    });

    vi.mocked(getSupabaseClient).mockReturnValue(client as unknown as ReturnType<typeof getSupabaseClient>);

    const result = await createManualStandardizedResultsFromFailures([
      { failureId: 'failure-001', supplement: { spec_model: 'D400 DN700' } },
    ]);

    expect(result).toEqual({
      successCount: 0,
      failedCount: 1,
      successes: [],
      failures: [{ failureId: 'failure-001', error: '更新失败记录处理状态失败：permission denied' }],
    });
    expect(calls.upserts).toHaveLength(1);
    expect(calls.resolutions).toHaveLength(1);
  });

  it('reports failure when resolving a manual upsert updates no rows', async () => {
    const { client, calls } = createManualCreateSupabaseMock({
      failures: [createFailureItem({ id: 'failure-001', source_material_id: 'mat-001' })],
      resolveEmptyByFailureId: ['failure-001'],
    });

    vi.mocked(getSupabaseClient).mockReturnValue(client as unknown as ReturnType<typeof getSupabaseClient>);

    const result = await createManualStandardizedResultsFromFailures([
      { failureId: 'failure-001', supplement: { spec_model: 'D400 DN700' } },
    ]);

    expect(result).toEqual({
      successCount: 0,
      failedCount: 1,
      successes: [],
      failures: [{ failureId: 'failure-001', error: '更新失败记录处理状态失败：未找到可更新的失败记录' }],
    });
    expect(calls.upserts).toHaveLength(1);
    expect(calls.resolutions).toHaveLength(1);
  });

  it('preserves created_by when a manual success upsert updates an existing standardized result', async () => {
    const { client, calls } = createManualCreateSupabaseMock({
      failures: [createFailureItem({ id: 'failure-001', source_material_id: 'mat-001' })],
      existingStandardizedResults: [{
        batch_id: 'BATCH-001',
        material_id: 'mat-001',
        created_by: 'original-user',
      }],
    });

    vi.mocked(getSupabaseClient).mockReturnValue(client as unknown as ReturnType<typeof getSupabaseClient>);

    const result = await createManualStandardizedResultsFromFailures([
      { failureId: 'failure-001', supplement: { spec_model: 'D400 DN700' } },
    ]);

    expect(result.successCount).toBe(1);
    expect(calls.upserts).toHaveLength(1);
    expect(calls.upserts[0].row).toEqual(expect.objectContaining({
      created_by: 'original-user',
      updated_by: 'user-002',
    }));
  });

  it('keeps only rows whose latest task item is success and includes rows without task items', () => {
    const summary = summarizeCurrentStandardizedRows([
      { batch_id: 'BATCH-001', material_id: 'material-1' },
      { batch_id: 'BATCH-001', material_id: 'material-2' },
      { batch_id: 'BATCH-001', material_id: 'material-3' },
      { batch_id: 'BATCH-002', material_id: 'material-8' },
    ], [
      {
        batch_id: 'BATCH-001',
        source_material_id: 'material-1',
        status: 'success',
        created_at: '2026-04-10T10:00:00.000Z',
      },
      {
        batch_id: 'BATCH-001',
        source_material_id: 'material-1',
        status: 'failed',
        created_at: '2026-04-11T10:00:00.000Z',
      },
      {
        batch_id: 'BATCH-001',
        source_material_id: 'material-2',
        status: 'failed',
        created_at: '2026-04-09T10:00:00.000Z',
      },
      {
        batch_id: 'BATCH-001',
        source_material_id: 'material-2',
        status: 'success',
        created_at: '2026-04-12T10:00:00.000Z',
      },
      {
        batch_id: 'BATCH-002',
        source_material_id: 'material-8',
        status: 'success',
        created_at: '2026-04-12T12:00:00.000Z',
      },
    ]);

    expect(summary).toEqual({
      totalCount: 3,
      countByBatchId: {
        'BATCH-001': 2,
        'BATCH-002': 1,
      },
    });
  });

  it('matches existing procurement materials by material_id and preserves unspecified fields', async () => {
    const { client, upsertCalls } = createImportSupabaseMock({
      procurementMaterials: [{
        id: 'mat-001',
        batch_id: 'BATCH-001',
        material_name: '球墨铸铁井盖',
        specification: 'DN700',
        quantity: 12,
        unit: '套',
        invoice_type: '专票',
        brand: '旧品牌',
        remarks: 'existing note',
        delivery_address: '济南仓',
        created_at: '2026-04-16T09:00:00.000Z',
        created_by: 'user-001',
      }],
      standardizedResults: [{
        id: 'std-001',
        batch_id: 'BATCH-001',
        material_id: 'mat-001',
        source_type: 'ai',
        invoice_type: '专票',
        original_name: '球墨铸铁井盖',
        original_spec: 'DN700',
        standardized_name: '球墨铸铁井盖',
        category: '井盖',
        brand: '旧品牌',
        notes: 'existing note',
        delivery_address: '济南仓',
        primary_search_term: '球墨铸铁井盖',
        alt_search_terms: [],
        standardized_spec: '重型DN700',
        standardized_unit: '套',
        quantity: 12,
        product_type: '',
        llm_model: 'glm',
        ai_reasoning: '',
        created_by: 'user-001',
        updated_by: 'user-001',
        created_at: '2026-04-16T09:00:00.000Z',
        updated_at: '2026-04-16T09:00:00.000Z',
      }],
    });

    vi.mocked(getSupabaseClient).mockReturnValue(client as unknown as ReturnType<typeof getSupabaseClient>);

    const result = await importStandardizedRows([{
      batch_id: 'BATCH-001',
      material_id: 'mat-001',
      invoice_type: '',
      original_name: '球墨铸铁井盖',
      standardized_name: '球墨铸铁井盖重型',
      original_spec: 'DN700',
      standardized_spec: '',
      brand: '',
      category: '',
      quantity: 0,
      standardized_unit: '',
      notes: '',
      delivery_address: '',
      primary_search_term: '',
      alt_search_terms: [],
      product_type: '',
      llm_model: '',
      ai_reasoning: '',
      provided_fields: ['batch_id', 'material_id', 'original_name', 'standardized_name', 'original_spec'],
    }]);

    expect(result).toEqual({ importedCount: 1, batchIds: ['BATCH-001'], materialIds: ['mat-001'] });
    expect(upsertCalls.standardized_results).toHaveLength(1);
    expect(upsertCalls.standardized_results[0]).toEqual([
      expect.objectContaining({
        batch_id: 'BATCH-001',
        material_id: 'mat-001',
        standardized_name: '球墨铸铁井盖重型',
        original_spec: 'DN700',
        quantity: 12,
        standardized_unit: '套',
        notes: 'existing note',
        delivery_address: '济南仓',
        source_type: 'manual',
      }),
    ]);
  });

  it('fails fast when multiple procurement materials match a row without material_id', async () => {
    const { client, upsertCalls } = createImportSupabaseMock({
      procurementMaterials: [
        {
          id: 'mat-003',
          batch_id: 'BATCH-002',
          material_name: '莱卡全站仪专用电池',
          specification: 'GEB371',
          quantity: 1,
          unit: '块',
          invoice_type: '',
          brand: '',
          remarks: '',
          delivery_address: '',
          created_at: '2026-04-16T09:00:00.000Z',
          created_by: 'user-001',
        },
        {
          id: 'mat-008',
          batch_id: 'BATCH-002',
          material_name: '莱卡全站仪专用电池',
          specification: 'GEB371',
          quantity: 1,
          unit: '块',
          invoice_type: '',
          brand: '',
          remarks: '',
          delivery_address: '',
          created_at: '2026-04-16T09:05:00.000Z',
          created_by: 'user-001',
        },
      ],
    });

    vi.mocked(getSupabaseClient).mockReturnValue(client as unknown as ReturnType<typeof getSupabaseClient>);

    await expect(importStandardizedRows([{
      batch_id: 'BATCH-002',
      material_id: '',
      invoice_type: '',
      original_name: '莱卡全站仪专用电池',
      standardized_name: '莱卡全站仪专用电池',
      original_spec: 'GEB371',
      standardized_spec: '',
      brand: '',
      category: '',
      quantity: 0,
      standardized_unit: '',
      notes: '',
      delivery_address: '',
      primary_search_term: '',
      alt_search_terms: [],
      product_type: '',
      llm_model: '',
      ai_reasoning: '',
      provided_fields: ['batch_id', 'original_name', 'standardized_name', 'original_spec'],
    }])).rejects.toThrow('请使用最新导出模板并保留“物料ID(material_id)”列');

    expect(upsertCalls.standardized_results).toHaveLength(0);
  });

  it('deletes a terminal standardization task after unlinking dependent references', async () => {
    const { client, calls } = createDeleteTaskSupabaseMock({
      taskStatus: 'failed',
      taskItems: [{ id: 'item-001' }, { id: 'item-002' }],
    });

    vi.mocked(getSupabaseClient).mockReturnValue(client as unknown as ReturnType<typeof getSupabaseClient>);

    await deleteStandardizationTaskById('task-001');

    expect(calls.retryUnlinks).toEqual([
      expect.objectContaining({
        field: 'retry_of_item_id',
        values: ['item-001', 'item-002'],
        payload: expect.objectContaining({ retry_of_item_id: null }),
      }),
    ]);
    expect(calls.resolvedUnlinks).toEqual([
      expect.objectContaining({
        field: 'resolved_by_task_id',
        value: 'task-001',
        payload: expect.objectContaining({ resolved_by_task_id: null }),
      }),
    ]);
    expect(calls.deletions).toEqual([{ field: 'id', value: 'task-001' }]);
  });

  it('rejects deleting a non-terminal standardization task', async () => {
    const { client, calls } = createDeleteTaskSupabaseMock({
      taskStatus: 'processing',
      taskItems: [{ id: 'item-001' }],
    });

    vi.mocked(getSupabaseClient).mockReturnValue(client as unknown as ReturnType<typeof getSupabaseClient>);

    await expect(deleteStandardizationTaskById('task-001')).rejects.toThrow('仅支持删除已结束的标准化任务状态记录');
    expect(calls.retryUnlinks).toHaveLength(0);
    expect(calls.resolvedUnlinks).toHaveLength(0);
    expect(calls.deletions).toHaveLength(0);
  });
});
