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

import { getSupabaseClient, sourceFilesBucket } from '@/lib/supabase';
import {
  updateSourceFileExtractionState,
  uploadSourceFile,
} from '@/services/sourceFileService';

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

function makeUploadSupabaseMock() {
  const single = vi.fn().mockResolvedValue({
    data: {
      id: 'source-001',
      batch_id: 'CG-2026-001',
      file_name: '采购清单.pdf',
      storage_path: 'source-files/CG-2026-001/采购清单.pdf',
      extraction_status: 'uploaded',
    },
    error: null,
  });
  const select = vi.fn(() => ({ single }));
  const insert = vi.fn(() => ({ select }));
  const from = vi.fn(() => ({ insert }));
  const upload = vi.fn().mockResolvedValue({ error: null });
  const remove = vi.fn().mockResolvedValue({ error: null });
  const storageFrom = vi.fn(() => ({ upload, remove }));

  return {
    client: {
      from,
      storage: { from: storageFrom },
    },
    from,
    insert,
    storageFrom,
    upload,
  };
}

describe('sourceFileService extraction state', () => {
  afterEach(() => {
    vi.clearAllMocks();
  });

  it('creates source_files rows with uploaded extraction status', async () => {
    const supabase = makeUploadSupabaseMock();
    vi.mocked(getSupabaseClient).mockReturnValue(supabase.client as unknown as ReturnType<typeof getSupabaseClient>);

    const file = new File(['pdf'], '采购清单.pdf', { type: 'application/pdf' });
    await uploadSourceFile({ batchId: 'CG-2026-001', file, uploadedBy: '测试用户' });

    expect(supabase.storageFrom).toHaveBeenCalledWith(sourceFilesBucket);
    expect(supabase.insert).toHaveBeenCalledWith(expect.objectContaining({
      batch_id: 'CG-2026-001',
      file_name: '采购清单.pdf',
      extraction_status: 'uploaded',
    }));
  });

  it('updates source file extraction state and stamps extracted_at for terminal states', async () => {
    const maybeSingle = vi.fn().mockResolvedValue({
      data: {
        id: 'source-001',
        extraction_status: 'llm_succeeded',
        extracted_at: '2026-04-26T10:00:00.000Z',
      },
      error: null,
    });
    const select = vi.fn(() => ({ maybeSingle }));
    const eq = vi.fn(() => ({ select }));
    const update = vi.fn(() => ({ eq }));
    const from = vi.fn(() => ({ update }));
    vi.mocked(getSupabaseClient).mockReturnValue({ from } as unknown as ReturnType<typeof getSupabaseClient>);

    await updateSourceFileExtractionState('source-001', {
      extraction_status: 'llm_succeeded',
      extraction_model: 'glm-5.1',
      extraction_summary: '提取 2 项',
      extracted_material_count: 2,
    });

    expect(from).toHaveBeenCalledWith('source_files');
    expect(update).toHaveBeenCalledWith(expect.objectContaining({
      extraction_status: 'llm_succeeded',
      extraction_model: 'glm-5.1',
      extraction_summary: '提取 2 项',
      extracted_material_count: 2,
      extracted_at: expect.any(String),
    }));
    expect(eq).toHaveBeenCalledWith('id', 'source-001');
  });
});
