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

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

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

import {
  deleteStandardizationTaskById,
  importStandardizedRows,
  summarizeCurrentStandardizedRows,
} from '@/services/standardizedService';

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 };
}

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

  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'] });
    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);
  });
});
