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

const mockEnrichMetadata = vi.fn();

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

const mockResults: EnrichedGame[] = [
  {
    source: 'igdb',
    externalIds: { igdb: 123 },
    title: 'Game One',
    slug: 'game-one',
    releaseDate: '2020-01-15',
    genres: ['Action', 'Adventure'],
    platforms: ['Nintendo Switch'],
    coverUrl: 'https://example.com/cover1.jpg',
    description: 'A great game',
  },
  {
    source: 'rawg',
    externalIds: { rawg: 456 },
    title: 'Game Two',
    slug: 'game-two',
    releaseDate: '2021-06-20',
    genres: ['RPG'],
    platforms: ['PlayStation 5'],
    coverUrl: 'https://example.com/cover2.jpg',
    description: 'Another game',
  },
];

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

  it('should not render when isOpen is false', () => {
    render(
      <MetadataSearchDialog
        romId="rom-1"
        isOpen={false}
        onOpenChange={vi.fn()}
        onSelect={vi.fn()}
      />
    );

    expect(screen.queryByText(/search metadata/i)).not.toBeInTheDocument();
  });

  it('should render when isOpen is true', () => {
    render(
      <MetadataSearchDialog romId="rom-1" isOpen={true} onOpenChange={vi.fn()} onSelect={vi.fn()} />
    );

    expect(screen.getByText(/search metadata/i)).toBeInTheDocument();
  });

  it('should have search input field', () => {
    render(
      <MetadataSearchDialog romId="rom-1" isOpen={true} onOpenChange={vi.fn()} onSelect={vi.fn()} />
    );

    expect(screen.getByPlaceholderText(/search game title/i)).toBeInTheDocument();
  });

  it('should accept search input', async () => {
    const user = await userEvent.setup();

    render(
      <MetadataSearchDialog romId="rom-1" isOpen={true} onOpenChange={vi.fn()} onSelect={vi.fn()} />
    );

    const input = screen.getByPlaceholderText(/search game title/i) as HTMLInputElement;
    await user.type(input, 'Game One');

    expect(input.value).toBe('Game One');
  });

  it('should call useEnrichMetadata when search is triggered', async () => {
    const user = await userEvent.setup();
    mockEnrichMetadata.mockResolvedValue([mockResults[0]]);

    render(
      <MetadataSearchDialog romId="rom-1" isOpen={true} onOpenChange={vi.fn()} onSelect={vi.fn()} />
    );

    const input = screen.getByPlaceholderText(/search game title/i);
    const searchButton = screen.getByRole('button', { name: /search/i });

    await user.type(input, 'Game One');
    await user.click(searchButton);

    await waitFor(() => {
      expect(mockEnrichMetadata).toHaveBeenCalledWith('Game One');
    });
  });

  it('should display search results', async () => {
    const user = await userEvent.setup();
    mockEnrichMetadata.mockResolvedValue(mockResults);

    render(
      <MetadataSearchDialog romId="rom-1" isOpen={true} onOpenChange={vi.fn()} onSelect={vi.fn()} />
    );

    const input = screen.getByPlaceholderText(/search game title/i);
    const searchButton = screen.getByRole('button', { name: /search/i });

    await user.type(input, 'Game');
    await user.click(searchButton);

    await waitFor(() => {
      expect(screen.getByText('Game One')).toBeInTheDocument();
      expect(screen.getByText('Game Two')).toBeInTheDocument();
    });
  });

  it('should display source badge for each result', async () => {
    const user = await userEvent.setup();
    mockEnrichMetadata.mockResolvedValue(mockResults);

    render(
      <MetadataSearchDialog romId="rom-1" isOpen={true} onOpenChange={vi.fn()} onSelect={vi.fn()} />
    );

    const input = screen.getByPlaceholderText(/search game title/i);
    const searchButton = screen.getByRole('button', { name: /search/i });

    await user.type(input, 'Game');
    await user.click(searchButton);

    await waitFor(() => {
      expect(screen.getByText('IGDB')).toBeInTheDocument();
      expect(screen.getByText('RAWG')).toBeInTheDocument();
    });
  });

  it('should show "No results" message when search returns empty', async () => {
    const user = await userEvent.setup();
    mockEnrichMetadata.mockResolvedValue([]);

    render(
      <MetadataSearchDialog romId="rom-1" isOpen={true} onOpenChange={vi.fn()} onSelect={vi.fn()} />
    );

    const input = screen.getByPlaceholderText(/search game title/i);
    const searchButton = screen.getByRole('button', { name: /search/i });

    await user.type(input, 'NonexistentGame');
    await user.click(searchButton);

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

  it('should call onSelect when result is selected', async () => {
    const user = await userEvent.setup();
    const onSelect = vi.fn();
    mockEnrichMetadata.mockResolvedValue(mockResults);

    render(
      <MetadataSearchDialog
        romId="rom-1"
        isOpen={true}
        onOpenChange={vi.fn()}
        onSelect={onSelect}
      />
    );

    const input = screen.getByPlaceholderText(/search game title/i);
    const searchButton = screen.getByRole('button', { name: /search/i });

    await user.type(input, 'Game');
    await user.click(searchButton);

    await waitFor(() => {
      expect(screen.getByText('Game One')).toBeInTheDocument();
    });

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

    expect(onSelect).toHaveBeenCalledWith(mockResults[0]);
  });

  it('should have cover image for each result', async () => {
    const user = await userEvent.setup();
    mockEnrichMetadata.mockResolvedValue(mockResults);

    const { container } = render(
      <MetadataSearchDialog romId="rom-1" isOpen={true} onOpenChange={vi.fn()} onSelect={vi.fn()} />
    );

    const input = screen.getByPlaceholderText(/search game title/i);
    const searchButton = screen.getByRole('button', { name: /search/i });

    await user.type(input, 'Game');
    await user.click(searchButton);

    await waitFor(() => {
      const images = container.querySelectorAll('img');
      expect(images.length).toBeGreaterThan(0);
    });
  });

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

    render(
      <MetadataSearchDialog romId="rom-1" isOpen={true} onOpenChange={vi.fn()} onSelect={vi.fn()} />
    );

    const input = screen.getByPlaceholderText(/search game title/i);
    const searchButton = screen.getByRole('button', { name: /search/i });

    await user.type(input, 'Game');
    await user.click(searchButton);

    // The button should be in the document during and after search
    expect(searchButton).toBeInTheDocument();
  });

  it('should call onOpenChange when closing dialog', async () => {
    const user = await userEvent.setup();
    const onOpenChange = vi.fn();

    render(
      <MetadataSearchDialog
        romId="rom-1"
        isOpen={true}
        onOpenChange={onOpenChange}
        onSelect={vi.fn()}
      />
    );

    // Find and click close button
    const buttons = screen.getAllByRole('button');
    const closeButton = buttons.find(
      (btn) =>
        btn.getAttribute('aria-label')?.includes('close') ||
        btn.textContent?.includes('✕') ||
        btn.textContent?.includes('Cancel')
    );

    if (closeButton) {
      await user.click(closeButton);
      expect(onOpenChange).toHaveBeenCalled();
    }
  });

  it('should display release date for results', async () => {
    const user = await userEvent.setup();
    mockEnrichMetadata.mockResolvedValue(mockResults);

    render(
      <MetadataSearchDialog romId="rom-1" isOpen={true} onOpenChange={vi.fn()} onSelect={vi.fn()} />
    );

    const input = screen.getByPlaceholderText(/search game title/i);
    const searchButton = screen.getByRole('button', { name: /search/i });

    await user.type(input, 'Game');
    await user.click(searchButton);

    await waitFor(() => {
      expect(screen.getByText(/2020/)).toBeInTheDocument();
      expect(screen.getByText(/2021/)).toBeInTheDocument();
    });
  });
});
