import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TrackSort } from './TrackSort';
import type { TrackSortOptions } from '../types';

describe('TrackSort', () => {
  const mockOnSortChange = vi.fn();

  const defaultSortOptions: TrackSortOptions = {
    field: 'title',
    order: 'asc',
  };

  const defaultProps = {
    sortOptions: defaultSortOptions,
    onSortChange: mockOnSortChange,
  };

  beforeEach(() => {
    vi.clearAllMocks();
    localStorage.clear();
  });

  afterEach(() => {
    localStorage.clear();
  });

  it('should render track sort', () => {
    render(<TrackSort {...defaultProps} />);
    expect(
      screen.getByRole('group', { name: 'Options de tri' }),
    ).toBeInTheDocument();
  });

  it('should display sort field select', () => {
    render(<TrackSort {...defaultProps} />);
    expect(
      screen.getByLabelText('Sélectionner le champ de tri'),
    ).toBeInTheDocument();
  });

  it('should display order toggle button when showOrderToggle is true', () => {
    render(<TrackSort {...defaultProps} showOrderToggle={true} />);
    expect(screen.getByLabelText(/Trier par ordre/)).toBeInTheDocument();
  });

  it('should not display order toggle button when showOrderToggle is false', () => {
    render(<TrackSort {...defaultProps} showOrderToggle={false} />);
    expect(screen.queryByLabelText(/Trier par ordre/)).not.toBeInTheDocument();
  });

  it('should display all available sort fields', () => {
    render(<TrackSort {...defaultProps} />);

    expect(screen.getByText('Titre')).toBeInTheDocument();
    expect(screen.getByText('Artiste')).toBeInTheDocument();
    expect(screen.getByText('Album')).toBeInTheDocument();
    expect(screen.getByText('Durée')).toBeInTheDocument();
    expect(screen.getByText('Date')).toBeInTheDocument();
    expect(screen.getByText('Popularité')).toBeInTheDocument();
  });

  it('should display only specified available fields', () => {
    render(
      <TrackSort
        {...defaultProps}
        availableFields={['title', 'artist', 'duration']}
      />,
    );

    expect(screen.getByText('Titre')).toBeInTheDocument();
    expect(screen.getByText('Artiste')).toBeInTheDocument();
    expect(screen.getByText('Durée')).toBeInTheDocument();
    expect(screen.queryByText('Album')).not.toBeInTheDocument();
    expect(screen.queryByText('Date')).not.toBeInTheDocument();
    expect(screen.queryByText('Popularité')).not.toBeInTheDocument();
  });

  it('should call onSortChange when field changes', async () => {
    const user = userEvent.setup();
    render(<TrackSort {...defaultProps} />);

    const fieldSelect = screen.getByLabelText('Sélectionner le champ de tri');
    await user.selectOptions(fieldSelect, 'artist');

    expect(mockOnSortChange).toHaveBeenCalledWith({
      field: 'artist',
      order: 'asc',
    });
  });

  it('should call onSortChange when order toggle is clicked', async () => {
    const user = userEvent.setup();
    render(<TrackSort {...defaultProps} />);

    const orderButton = screen.getByLabelText(/Trier par ordre/);
    await user.click(orderButton);

    expect(mockOnSortChange).toHaveBeenCalledWith({
      field: 'title',
      order: 'desc',
    });
  });

  it('should toggle order between asc and desc', async () => {
    const user = userEvent.setup();
    const { rerender } = render(
      <TrackSort
        {...defaultProps}
        sortOptions={{ field: 'title', order: 'asc' }}
      />,
    );

    const orderButton = screen.getByLabelText(/Trier par ordre/);

    // Initialement asc
    expect(orderButton).toHaveAttribute('aria-pressed', 'false');

    await user.click(orderButton);

    // Après le clic, devrait être desc
    rerender(
      <TrackSort
        {...defaultProps}
        sortOptions={{ field: 'title', order: 'desc' }}
      />,
    );

    const updatedOrderButton = screen.getByLabelText(/Trier par ordre/);
    expect(updatedOrderButton).toHaveAttribute('aria-pressed', 'true');
  });

  it('should display ascending icon when order is asc', () => {
    render(
      <TrackSort
        {...defaultProps}
        sortOptions={{ field: 'title', order: 'asc' }}
      />,
    );

    const orderButton = screen.getByLabelText(/Trier par ordre/);
    expect(orderButton.querySelector('svg')).toBeInTheDocument();
  });

  it('should display descending icon when order is desc', () => {
    render(
      <TrackSort
        {...defaultProps}
        sortOptions={{ field: 'title', order: 'desc' }}
      />,
    );

    const orderButton = screen.getByLabelText(/Trier par ordre/);
    expect(orderButton.querySelector('svg')).toBeInTheDocument();
  });

  it('should persist preference in localStorage when persistPreference is enabled', async () => {
    const storageKey = 'testSortOptions';
    const { rerender } = render(
      <TrackSort
        {...defaultProps}
        sortOptions={{ field: 'title', order: 'asc' }}
        persistPreference={true}
        storageKey={storageKey}
      />,
    );

    // Attendre que useEffect persiste la valeur
    await waitFor(
      () => {
        const stored = localStorage.getItem(storageKey);
        expect(stored).toBeTruthy();
        if (stored) {
          const parsed = JSON.parse(stored);
          expect(parsed.field).toBe('title');
          expect(parsed.order).toBe('asc');
        }
      },
      { timeout: 2000 },
    );

    rerender(
      <TrackSort
        {...defaultProps}
        sortOptions={{ field: 'artist', order: 'desc' }}
        persistPreference={true}
        storageKey={storageKey}
      />,
    );

    // Attendre que useEffect persiste la nouvelle valeur
    await waitFor(
      () => {
        const stored = localStorage.getItem(storageKey);
        expect(stored).toBeTruthy();
        if (stored) {
          const parsed = JSON.parse(stored);
          expect(parsed.field).toBe('artist');
          expect(parsed.order).toBe('desc');
        }
      },
      { timeout: 2000 },
    );
  });

  it('should load preference from localStorage on mount when persistPreference is enabled', () => {
    const storageKey = 'testSortOptions';
    const storedOptions: TrackSortOptions = {
      field: 'duration',
      order: 'desc',
    };
    localStorage.setItem(storageKey, JSON.stringify(storedOptions));

    render(
      <TrackSort
        {...defaultProps}
        sortOptions={{ field: 'title', order: 'asc' }}
        persistPreference={true}
        storageKey={storageKey}
      />,
    );

    // onChange devrait être appelé avec la valeur depuis localStorage
    expect(mockOnSortChange).toHaveBeenCalledWith(storedOptions);
  });

  it('should apply custom className', () => {
    const { container } = render(
      <TrackSort {...defaultProps} className="custom-class" />,
    );
    expect(container.firstChild).toHaveClass('custom-class');
  });

  it('should update sort options when props change', () => {
    const { rerender } = render(
      <TrackSort
        {...defaultProps}
        sortOptions={{ field: 'title', order: 'asc' }}
      />,
    );

    const fieldSelect = screen.getByLabelText(
      'Sélectionner le champ de tri',
    ) as HTMLSelectElement;
    expect(fieldSelect.value).toBe('title');

    rerender(
      <TrackSort
        {...defaultProps}
        sortOptions={{ field: 'artist', order: 'desc' }}
      />,
    );

    const updatedFieldSelect = screen.getByLabelText(
      'Sélectionner le champ de tri',
    ) as HTMLSelectElement;
    expect(updatedFieldSelect.value).toBe('artist');
  });

  it('should have accessible attributes', () => {
    render(<TrackSort {...defaultProps} />);

    const group = screen.getByRole('group');
    expect(group).toHaveAttribute('aria-label');

    const fieldSelect = screen.getByLabelText('Sélectionner le champ de tri');
    expect(fieldSelect).toHaveAttribute('aria-label');

    const orderButton = screen.getByLabelText(/Trier par ordre/);
    expect(orderButton).toHaveAttribute('aria-pressed');
    expect(orderButton).toHaveAttribute('aria-label');
  });

  it('should use custom storage key', async () => {
    const customKey = 'customSortKey';
    localStorage.clear();

    render(
      <TrackSort
        {...defaultProps}
        sortOptions={{ field: 'title', order: 'asc' }}
        persistPreference={true}
        storageKey={customKey}
      />,
    );

    await waitFor(
      () => {
        const stored = localStorage.getItem(customKey);
        expect(stored).toBeTruthy();
        if (stored) {
          const parsed = JSON.parse(stored);
          expect(parsed.field).toBe('title');
          expect(parsed.order).toBe('asc');
        }
      },
      { timeout: 3000 },
    );
  });
});
