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

import ProcurementListReview from '@/pages/ProcurementListReview';

function okResponse(body: Blob) {
  return {
    ok: true,
    headers: new Headers({
      'content-disposition': 'attachment; filename="procurement-list.xlsx"',
    }),
    blob: vi.fn(async () => body),
  } as unknown as Response;
}

describe('ProcurementListReview', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  function renderPage() {
    const queryClient = new QueryClient({
      defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
    });
    return render(
      <QueryClientProvider client={queryClient}>
        <ProcurementListReview />
      </QueryClientProvider>,
    );
  }

  it('uploads selected attachments to the stateless export API and downloads the returned xlsx', async () => {
    Object.defineProperty(URL, 'createObjectURL', { configurable: true, value: vi.fn(() => 'blob:procurement-list') });
    Object.defineProperty(URL, 'revokeObjectURL', { configurable: true, value: vi.fn() });
    const createObjectURL = vi.mocked(URL.createObjectURL);
    const revokeObjectURL = vi.mocked(URL.revokeObjectURL);
    const click = vi.fn();
    const originalCreateElement = document.createElement.bind(document);
    const appendChild = vi.spyOn(document.body, 'appendChild');
    vi.spyOn(document, 'createElement').mockImplementation((tagName: string) => {
      const element = originalCreateElement(tagName);
      if (tagName === 'a') {
        Object.defineProperty(element, 'click', { value: click });
      }
      return element;
    });
    const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(okResponse(new Blob(['xlsx'])));

    renderPage();

    fireEvent.change(screen.getByLabelText('上传附件'), {
      target: {
        files: [
          new File(['pdf'], '采购清单.pdf', { type: 'application/pdf' }),
          new File(['xlsx'], '采购清单.xlsx', {
            type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
          }),
        ],
      },
    });
    fireEvent.click(screen.getByRole('button', { name: '生成 Excel' }));

    await waitFor(() => {
      expect(fetchMock).toHaveBeenCalledWith('/api/procurement-list/export', expect.objectContaining({
        method: 'POST',
        body: expect.any(FormData),
      }));
    });
    const formData = (fetchMock.mock.calls[0]?.[1] as RequestInit).body as FormData;
    expect(formData.getAll('files')).toHaveLength(2);
    expect(click).toHaveBeenCalledTimes(1);
    expect(createObjectURL).toHaveBeenCalledWith(expect.any(Blob));
    expect(revokeObjectURL).toHaveBeenCalledWith('blob:procurement-list');
    expect(appendChild).toHaveBeenCalled();
  });

  it('shows export errors without requiring task or batch state', async () => {
    vi.spyOn(globalThis, 'fetch').mockResolvedValue({
      ok: false,
      status: 500,
      json: vi.fn(async () => ({ error: '模型返回非法 JSON' })),
    } as unknown as Response);

    renderPage();

    fireEvent.change(screen.getByLabelText('上传附件'), {
      target: { files: [new File(['pdf'], '采购清单.pdf', { type: 'application/pdf' })] },
    });
    fireEvent.click(screen.getByRole('button', { name: '生成 Excel' }));

    expect(await screen.findByText('模型返回非法 JSON')).toBeInTheDocument();
    expect(screen.queryByText('新建任务')).not.toBeInTheDocument();
    expect(screen.queryByText('批次 ID')).not.toBeInTheDocument();
  });
});
