import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { StandardizationQueueProvider, useStandardizationQueue } from '@/contexts/StandardizationQueueContext';
import { triggerStandardizeBatch } from '@/services/automationService';
import { fetchStandardizationTasks, type StandardizationTaskRecord } from '@/services/standardizedService';

vi.mock('@/services/automationService', async () => {
  const actual = await vi.importActual<typeof import('@/services/automationService')>('@/services/automationService');
  return {
    ...actual,
    triggerStandardizeBatch: vi.fn(),
  };
});

vi.mock('@/services/standardizedService', async () => {
  const actual = await vi.importActual<typeof import('@/services/standardizedService')>('@/services/standardizedService');
  return {
    ...actual,
    fetchStandardizationTasks: vi.fn(),
  };
});

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

vi.mock('sonner', () => ({
  toast: {
    success: vi.fn(),
    error: vi.fn(),
  },
}));

function createTask(overrides: Partial<StandardizationTaskRecord>): StandardizationTaskRecord {
  return {
    id: overrides.id ?? crypto.randomUUID(),
    batch_id: overrides.batch_id ?? 'CG-TEST-001',
    status: overrides.status ?? 'processing',
    total_rows: overrides.total_rows ?? 1,
    processed_rows: overrides.processed_rows ?? 0,
    success_rows: overrides.success_rows ?? 0,
    failed_rows: overrides.failed_rows ?? 0,
    current_item: overrides.current_item ?? '处理中',
    current_phase: overrides.current_phase ?? '处理中',
    error_message: overrides.error_message ?? '',
    created_at: overrides.created_at ?? '2026-04-12T09:00:00.000Z',
    updated_at: overrides.updated_at ?? '2026-04-12T09:00:00.000Z',
    started_at: overrides.started_at ?? '2026-04-12T09:00:00.000Z',
    finished_at: overrides.finished_at ?? null,
    execution_mode: overrides.execution_mode ?? 'online',
    provider_status: overrides.provider_status ?? '',
    progress_current: overrides.progress_current ?? 0,
    progress_total: overrides.progress_total ?? overrides.total_rows ?? 1,
    progress_percent: overrides.progress_percent ?? 0,
  };
}

function QueueProbe() {
  const { enqueueOnlineTask, queueItems } = useStandardizationQueue();

  return (
    <>
      <div data-testid="queue-count">{queueItems.length}</div>
      <button
        type="button"
        onClick={() => enqueueOnlineTask({
          batchId: 'CG-QUEUE-001',
          totalRows: 2,
          materials: [
            {
              id: 'mat-1',
              material_name: '物料1',
              specification: '规格1',
              quantity: 1,
              unit: '个',
              invoice_type: '专票',
              brand: '品牌A',
              remarks: '',
              delivery_address: '杭州',
            },
          ],
        })}
      >
        enqueue
      </button>
    </>
  );
}

describe('StandardizationQueueProvider', () => {
  let queryClient: QueryClient;
  let taskRecords: StandardizationTaskRecord[];

  const triggerStandardizeBatchMock = vi.mocked(triggerStandardizeBatch);
  const fetchStandardizationTasksMock = vi.mocked(fetchStandardizationTasks);

  beforeEach(() => {
    const storage = new Map<string, string>();
    Object.defineProperty(window, 'localStorage', {
      configurable: true,
      value: {
        getItem: (key: string) => storage.get(key) ?? null,
        setItem: (key: string, value: string) => {
          storage.set(key, value);
        },
        removeItem: (key: string) => {
          storage.delete(key);
        },
        clear: () => {
          storage.clear();
        },
      },
    });

    queryClient = new QueryClient({
      defaultOptions: {
        queries: {
          retry: false,
          gcTime: 0,
        },
      },
    });

    taskRecords = [];
    window.localStorage.clear();
    fetchStandardizationTasksMock.mockImplementation(async () => taskRecords);
    triggerStandardizeBatchMock.mockResolvedValue({
      message: '开始标准化处理，共 2 条物料',
      totalRows: 2,
      taskId: 'task-online-1',
      executionMode: 'online',
      taskStatus: 'processing',
    });
  });

  afterEach(() => {
    queryClient.clear();
    vi.clearAllMocks();
  });

  function renderQueue() {
    return render(
      <QueryClientProvider client={queryClient}>
        <StandardizationQueueProvider>
          <QueueProbe />
        </StandardizationQueueProvider>
      </QueryClientProvider>,
    );
  }

  it('dispatches the first online task even when offline tasks are processing', async () => {
    taskRecords = [
      createTask({
        batch_id: 'CG-OFFLINE-001',
        execution_mode: 'offline',
        status: 'processing',
      }),
    ];

    renderQueue();

    await waitFor(() => expect(fetchStandardizationTasksMock).toHaveBeenCalled());
    fireEvent.click(screen.getByRole('button', { name: 'enqueue' }));

    await waitFor(() => expect(triggerStandardizeBatchMock).toHaveBeenCalledTimes(1));
    expect(triggerStandardizeBatchMock).toHaveBeenCalledWith(expect.objectContaining({
      batchId: 'CG-QUEUE-001',
      materials: [expect.objectContaining({
        id: 'mat-1',
      })],
    }));
    await waitFor(() => expect(screen.getByTestId('queue-count')).toHaveTextContent('0'));
  });

  it('keeps later online tasks queued until the online slot becomes free', async () => {
    taskRecords = [
      createTask({
        batch_id: 'CG-ONLINE-001',
        execution_mode: 'online',
        status: 'processing',
      }),
      createTask({
        batch_id: 'CG-OFFLINE-001',
        execution_mode: 'offline',
        status: 'processing',
      }),
    ];

    renderQueue();

    await waitFor(() => expect(fetchStandardizationTasksMock).toHaveBeenCalled());
    fireEvent.click(screen.getByRole('button', { name: 'enqueue' }));

    await waitFor(() => expect(screen.getByTestId('queue-count')).toHaveTextContent('1'));
    expect(triggerStandardizeBatchMock).not.toHaveBeenCalled();

    taskRecords = [
      createTask({
        batch_id: 'CG-OFFLINE-001',
        execution_mode: 'offline',
        status: 'processing',
      }),
    ];

    await act(async () => {
      await queryClient.invalidateQueries({ queryKey: ['standardization', 'monitor', 'tasks'] });
    });

    await waitFor(() => expect(triggerStandardizeBatchMock).toHaveBeenCalledTimes(1));
  });

  it('removes stale queued items when the source materials no longer exist', async () => {
    window.localStorage.setItem('autoprocurement.standardization.online-queue', JSON.stringify([{
      id: 'queue-stale-1',
      fingerprint: 'CG-QUEUE-404::mat-1',
      batchId: 'CG-QUEUE-404',
      materialIds: ['mat-1'],
      totalRows: 1,
      createdAt: '2026-04-13T10:00:00.000Z',
      status: 'queued',
      errorMessage: '',
      blockedUntil: null,
      materials: [{
        id: 'mat-1',
        material_name: '旧测试物料',
        specification: '旧测试规格',
        quantity: 1,
        unit: '件',
        invoice_type: '专票',
        brand: '旧品牌',
        remarks: '',
        delivery_address: '杭州',
      }],
    }]));
    triggerStandardizeBatchMock.mockRejectedValue(new Error('批次 CG-QUEUE-404 下没有采购物料'));

    renderQueue();

    await waitFor(() => expect(triggerStandardizeBatchMock).toHaveBeenCalledTimes(1));
    await waitFor(() => expect(screen.getByTestId('queue-count')).toHaveTextContent('0'));
  });
});