import { renderHook, act, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useFlyerUploader } from './useFlyerUploader';
import * as aiApiClient from '../services/aiApiClient';
import * as checksumUtil from '../utils/checksum';

// Import the actual error class because the module is mocked
const { JobFailedError } =
  await vi.importActual<typeof import('../services/aiApiClient')>('../services/aiApiClient');

// Mock dependencies
vi.mock('../services/aiApiClient');
vi.mock('../utils/checksum');
vi.mock('../services/logger.client', () => ({
  logger: {
    info: vi.fn(),
    error: vi.fn(),
    warn: vi.fn(),
    debug: vi.fn(),
  },
}));

const mockedAiApiClient = vi.mocked(aiApiClient);
const mockedChecksumUtil = vi.mocked(checksumUtil);

// Helper to wrap the hook with QueryClientProvider, which is required by react-query
const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false, // Disable retries for tests for predictable behavior
      },
    },
  });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

describe('useFlyerUploader Hook with React Query', () => {
  beforeEach(() => {
    vi.resetAllMocks();
    mockedChecksumUtil.generateFileChecksum.mockResolvedValue('mock-checksum');
  });

  it('should handle a successful upload and polling flow', async () => {
    // Arrange
    const mockJobId = 'job-123';
    mockedAiApiClient.uploadAndProcessFlyer.mockResolvedValue({ jobId: mockJobId });
    mockedAiApiClient.getJobStatus
      .mockResolvedValueOnce({
        // First poll: active
        id: mockJobId,
        state: 'active',
        progress: { message: 'Processing...' },
        returnValue: null,
        failedReason: null,
      } as aiApiClient.JobStatus)
      .mockResolvedValueOnce({
        // Second poll: completed
        id: mockJobId,
        state: 'completed',
        progress: { message: 'Complete!' },
        returnValue: { flyerId: 777 },
        failedReason: null,
      } as aiApiClient.JobStatus);

    const { result } = renderHook(() => useFlyerUploader(), { wrapper: createWrapper() });
    const mockFile = new File([''], 'flyer.pdf');

    // Act
    await act(async () => {
      result.current.upload(mockFile);
    });

    // Assert initial upload state
    await waitFor(() => expect(result.current.processingState).toBe('polling'));
    expect(result.current.jobId).toBe(mockJobId);

    // Assert polling state
    await waitFor(() => expect(result.current.statusMessage).toBe('Processing...'));

    // Assert completed state
    await waitFor(() => expect(result.current.processingState).toBe('completed'), {
      timeout: 5000,
    });
    expect(result.current.flyerId).toBe(777);
  });

  it('should handle an upload failure', async () => {
    // Arrange
    const uploadError = {
      status: 409,
      body: { message: 'Duplicate flyer detected.', flyerId: 99 },
    };
    mockedAiApiClient.uploadAndProcessFlyer.mockRejectedValue(uploadError);

    const { result } = renderHook(() => useFlyerUploader(), { wrapper: createWrapper() });
    const mockFile = new File([''], 'flyer.pdf');

    // Act
    await act(async () => {
      result.current.upload(mockFile);
    });

    // Assert error state
    await waitFor(() => expect(result.current.processingState).toBe('error'));
    expect(result.current.errorMessage).toBe('Duplicate flyer detected.');
    expect(result.current.duplicateFlyerId).toBe(99);
    expect(result.current.jobId).toBeNull();
  });

  it('should handle a job failure during polling', async () => {
    // Arrange
    const mockJobId = 'job-456';
    mockedAiApiClient.uploadAndProcessFlyer.mockResolvedValue({ jobId: mockJobId });

    // Mock getJobStatus to throw a JobFailedError
    mockedAiApiClient.getJobStatus.mockRejectedValue(
      new JobFailedError('AI validation failed.', 'AI_VALIDATION_FAILED'),
    );

    const { result } = renderHook(() => useFlyerUploader(), { wrapper: createWrapper() });
    const mockFile = new File([''], 'flyer.pdf');

    // Act
    await act(async () => {
      result.current.upload(mockFile);
    });

    // Assert error state after polling fails
    await waitFor(() => expect(result.current.processingState).toBe('error'));
    expect(result.current.errorMessage).toBe('Polling failed: AI validation failed.');
    expect(result.current.flyerId).toBeNull();
  });
});
