import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from '../../src/lib/queryClient';
import * as useRomsModule from '../../src/hooks/useRoms';
import Roms from '../../src/routes/roms';
import { RomFile } from '../../src/types/rom';

// Mock the useRoms hooks
vi.spyOn(useRomsModule, 'useRoms');
vi.spyOn(useRomsModule, 'useScanDirectory');
vi.spyOn(useRomsModule, 'useEnrichMetadata');
vi.spyOn(useRomsModule, 'useLinkGameToRom');
vi.spyOn(useRomsModule, 'useDeleteRom');

const mockRoms: RomFile[] = [
  {
    id: '1',
    path: '/roms/game1.zip',
    filename: 'game1.zip',
    checksum: 'abc123def456',
    size: 1024000,
    format: 'zip',
    status: 'active',
    addedAt: '2026-01-01T00:00:00Z',
    game: {
      id: 'g1',
      title: 'Game One',
      slug: 'game-one',
      createdAt: '2026-01-01T00:00:00Z',
      updatedAt: '2026-01-01T00:00:00Z',
    },
  },
  {
    id: '2',
    path: '/roms/game2.rar',
    filename: 'game2.rar',
    checksum: 'xyz789uvw012',
    size: 2048000,
    format: 'rar',
    status: 'active',
    addedAt: '2026-01-02T00:00:00Z',
  },
];

describe('ROMs Page', () => {
  beforeEach(() => {
    vi.clearAllMocks();

    // Default mocks
    vi.mocked(useRomsModule.useRoms).mockReturnValue({
      data: mockRoms,
      isLoading: false,
      error: null,
    } as any);

    vi.mocked(useRomsModule.useScanDirectory).mockReturnValue({
      mutateAsync: vi.fn(),
      isPending: false,
    } as any);

    vi.mocked(useRomsModule.useEnrichMetadata).mockReturnValue({
      mutateAsync: vi.fn(),
      isPending: false,
    } as any);

    vi.mocked(useRomsModule.useLinkGameToRom).mockReturnValue({
      mutateAsync: vi.fn(),
      isPending: false,
    } as any);

    vi.mocked(useRomsModule.useDeleteRom).mockReturnValue({
      mutateAsync: vi.fn(),
      isPending: false,
    } as any);
  });

  it('should render empty state when no roms', () => {
    vi.mocked(useRomsModule.useRoms).mockReturnValue({
      data: [],
      isLoading: false,
      error: null,
    } as any);

    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    expect(screen.getByText(/no roms yet/i)).toBeInTheDocument();
  });

  it('should render loading state', () => {
    vi.mocked(useRomsModule.useRoms).mockReturnValue({
      data: undefined,
      isLoading: true,
      error: null,
    } as any);

    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

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

  it('should render error state', () => {
    const error = new Error('Failed to fetch');
    vi.mocked(useRomsModule.useRoms).mockReturnValue({
      data: undefined,
      isLoading: false,
      error,
    } as any);

    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    expect(screen.getByText(/error/i)).toBeInTheDocument();
    expect(screen.getByText(/failed to fetch/i)).toBeInTheDocument();
  });

  it('should render table with roms', () => {
    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    expect(screen.getByText('game1.zip')).toBeInTheDocument();
    expect(screen.getByText('game2.rar')).toBeInTheDocument();
  });

  it('should render "Scan Directory" button', () => {
    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

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

  it('should open scan dialog when "Scan Directory" is clicked', async () => {
    const user = await userEvent.setup();

    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    const scanButton = screen.getByRole('button', { name: /scan directory/i });
    await user.click(scanButton);

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

  it('should render rom with linked game', () => {
    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    expect(screen.getByText('Game One')).toBeInTheDocument();
  });

  it('should render "Link Metadata" button for rom without game', () => {
    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    // game2.rar doesn't have a linked game
    const linkButtons = screen.getAllByRole('button', { name: /link metadata/i });
    expect(linkButtons.length).toBeGreaterThan(0);
  });

  it('should open metadata search dialog when "Link Metadata" is clicked', async () => {
    const user = await userEvent.setup();

    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    const linkButton = screen.getAllByRole('button', { name: /link metadata/i })[0];
    await user.click(linkButton);

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

  it('should show delete button and confirmation', async () => {
    const user = await userEvent.setup();

    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    const deleteButtons = screen.getAllByRole('button', { name: /delete/i });
    expect(deleteButtons.length).toBeGreaterThan(0);
  });

  it('should handle table columns correctly', () => {
    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    // Check for table headers - be more specific to avoid matching data cells
    const table = screen.getByRole('table');
    expect(table.querySelector('th:nth-child(1)')).toHaveTextContent(/filename/i);
    expect(table.querySelector('th:nth-child(2)')).toHaveTextContent(/size/i);
    expect(table.querySelector('th:nth-child(3)')).toHaveTextContent(/checksum/i);
    expect(table.querySelector('th:nth-child(4)')).toHaveTextContent(/status/i);
    expect(table.querySelector('th:nth-child(5)')).toHaveTextContent(/game/i);
    expect(table.querySelector('th:nth-child(6)')).toHaveTextContent(/actions/i);
  });

  it('should display file size in human readable format', () => {
    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    // 1024000 bytes should be displayed as 1000 KB
    expect(screen.getByText(/1000\s*kb/i)).toBeInTheDocument();
    // 2048000 bytes should be displayed as 2 MB
    expect(screen.getByText(/2\s*mb/i)).toBeInTheDocument();
  });

  it('should display checksum truncated with ellipsis', () => {
    render(
      <QueryClientProvider client={queryClient}>
        <Roms />
      </QueryClientProvider>
    );

    // First 8 chars should be shown + ...
    expect(screen.getByText(/abc123de\.\.\./)).toBeInTheDocument();
  });
});
