import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import ScanDialog from '../../src/components/roms/ScanDialog';

const mockScanDirectory = vi.fn();

vi.mock('../../src/hooks/useRoms', () => ({
  useScanDirectory: () => ({
    mutateAsync: mockScanDirectory,
    isPending: false,
  }),
}));

describe('ScanDialog Component', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should not render when isOpen is false', () => {
    render(<ScanDialog isOpen={false} onOpenChange={vi.fn()} />);

    // Dialog content should not be visible
    expect(screen.queryByText(/scan roms directory/i)).not.toBeInTheDocument();
  });

  it('should render when isOpen is true', () => {
    render(<ScanDialog isOpen={true} onOpenChange={vi.fn()} />);

    expect(screen.getByText(/scan roms directory/i)).toBeInTheDocument();
  });

  it('should have input field for path', () => {
    render(<ScanDialog isOpen={true} onOpenChange={vi.fn()} />);

    expect(screen.getByPlaceholderText(/enter rom directory path/i)).toBeInTheDocument();
  });

  it('should accept text input in path field', async () => {
    const user = await userEvent.setup();

    render(<ScanDialog isOpen={true} onOpenChange={vi.fn()} />);

    const input = screen.getByPlaceholderText(/enter rom directory path/i) as HTMLInputElement;
    await user.type(input, '/path/to/roms');

    expect(input.value).toBe('/path/to/roms');
  });

  it('should have "Scan Directory" button', () => {
    render(<ScanDialog isOpen={true} onOpenChange={vi.fn()} />);

    expect(screen.getByRole('button', { name: /scan directory/i })).toBeInTheDocument();
  });

  it('should call useScanDirectory when form is submitted', async () => {
    const user = await userEvent.setup();
    mockScanDirectory.mockResolvedValue({ processed: 5, createdCount: 3, upserted: 2 });

    render(<ScanDialog isOpen={true} onOpenChange={vi.fn()} />);

    const input = screen.getByPlaceholderText(/enter rom directory path/i);
    const button = screen.getByRole('button', { name: /scan directory/i });

    await user.type(input, '/roms');
    await user.click(button);

    await waitFor(() => {
      expect(mockScanDirectory).toHaveBeenCalledWith('/roms');
    });
  });

  it('should show loading state during scanning', async () => {
    const user = await userEvent.setup();

    const { rerender } = render(<ScanDialog isOpen={true} onOpenChange={vi.fn()} />);

    const input = screen.getByPlaceholderText(/enter rom directory path/i);
    const button = screen.getByRole('button', { name: /scan directory/i });

    await user.type(input, '/roms');

    // We'll need to mock isPending state change, this is just a basic check
    expect(button).toBeInTheDocument();
  });

  it('should display success message after scan', async () => {
    const user = await userEvent.setup();
    mockScanDirectory.mockResolvedValue({ processed: 5, createdCount: 3, upserted: 2 });

    render(<ScanDialog isOpen={true} onOpenChange={vi.fn()} />);

    const input = screen.getByPlaceholderText(/enter rom directory path/i);
    const button = screen.getByRole('button', { name: /scan directory/i });

    await user.type(input, '/roms');
    await user.click(button);

    await waitFor(() => {
      expect(screen.getByText(/scan completed/i)).toBeInTheDocument();
    });
  });

  it('should display error message on scan failure', async () => {
    const user = await userEvent.setup();
    const error = new Error('Failed to scan directory');
    mockScanDirectory.mockRejectedValue(error);

    render(<ScanDialog isOpen={true} onOpenChange={vi.fn()} />);

    const input = screen.getByPlaceholderText(/enter rom directory path/i);
    const button = screen.getByRole('button', { name: /scan directory/i });

    await user.type(input, '/roms');
    await user.click(button);

    await waitFor(() => {
      expect(screen.getByText(/error/i)).toBeInTheDocument();
    });
  });

  it('should call onOpenChange when close button is clicked', async () => {
    const user = await userEvent.setup();
    const onOpenChange = vi.fn();

    render(<ScanDialog isOpen={true} onOpenChange={onOpenChange} />);

    const cancelButton = screen.getByText('Cancel');

    await user.click(cancelButton);
    expect(onOpenChange).toHaveBeenCalledWith(false);
  });

  it('should disable input and button while scanning', async () => {
    const user = await userEvent.setup();
    let isPending = false;

    const ScanDialogWithPending = ({ isOpen, onOpenChange }: any) => {
      return <ScanDialog isOpen={isOpen} onOpenChange={onOpenChange} />;
    };

    render(<ScanDialogWithPending isOpen={true} onOpenChange={vi.fn()} />);

    const input = screen.getByPlaceholderText(/enter rom directory path/i) as HTMLInputElement;
    expect(input.disabled).toBe(false);
  });
});
