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

describe('VolumeControl', () => {
  const mockOnVolumeChange = vi.fn();
  const mockOnMuteToggle = vi.fn();
  const defaultProps = {
    volume: 50,
    muted: false,
    onVolumeChange: mockOnVolumeChange,
    onMuteToggle: mockOnMuteToggle,
  };

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

  it('should render volume control', () => {
    render(<VolumeControl {...defaultProps} />);

    expect(screen.getByLabelText('Volume: 50%')).toBeInTheDocument();
    expect(screen.getByRole('slider')).toBeInTheDocument();
  });

  it('should display volume slider', () => {
    const { container } = render(<VolumeControl {...defaultProps} />);

    const slider = screen.getByRole('slider');
    expect(slider).toBeInTheDocument();

    const volumeTrack = container.querySelector('[class*="bg-primary"]');
    expect(volumeTrack).toHaveStyle({ width: '50%' });
  });

  it('should display mute button', () => {
    render(<VolumeControl {...defaultProps} />);

    const muteButton = screen.getByLabelText('Volume: 50%');
    expect(muteButton).toBeInTheDocument();
  });

  it('should call onMuteToggle when mute button is clicked', async () => {
    const user = userEvent.setup();

    render(<VolumeControl {...defaultProps} />);

    const muteButton = screen.getByLabelText('Volume: 50%');
    await user.click(muteButton);

    expect(mockOnMuteToggle).toHaveBeenCalledTimes(1);
  });

  it('should display VolumeX icon when muted', () => {
    render(<VolumeControl {...defaultProps} muted={true} />);

    const muteButton = screen.getByLabelText('Volume muet');
    expect(muteButton).toBeInTheDocument();
  });

  it('should display Volume1 icon when volume is low', () => {
    render(<VolumeControl {...defaultProps} volume={30} />);

    const muteButton = screen.getByLabelText('Volume: 30%');
    expect(muteButton).toBeInTheDocument();
  });

  it('should display Volume2 icon when volume is high', () => {
    render(<VolumeControl {...defaultProps} volume={70} />);

    const muteButton = screen.getByLabelText('Volume: 70%');
    expect(muteButton).toBeInTheDocument();
  });

  it('should call onVolumeChange when slider is clicked', async () => {
    const { container } = render(<VolumeControl {...defaultProps} />);

    const slider = container.querySelector('[role="slider"]') as HTMLElement;
    const mockRect = {
      left: 0,
      top: 0,
      width: 100,
      height: 10,
      bottom: 10,
      right: 100,
      x: 0,
      y: 0,
      toJSON: vi.fn(),
    };
    vi.spyOn(slider, 'getBoundingClientRect').mockReturnValue(
      mockRect as DOMRect,
    );

    fireEvent.mouseDown(slider, {
      clientX: 75,
    });

    await waitFor(() => {
      expect(mockOnVolumeChange).toHaveBeenCalled();
    });

    const volumeCall = mockOnVolumeChange.mock.calls[0][0];
    expect(volumeCall).toBeGreaterThan(70);
    expect(volumeCall).toBeLessThan(80);
  });

  it('should handle drag interaction', async () => {
    const { container } = render(<VolumeControl {...defaultProps} />);

    const slider = container.querySelector('[role="slider"]') as HTMLElement;
    const mockRect = {
      left: 0,
      top: 0,
      width: 100,
      height: 10,
      bottom: 10,
      right: 100,
      x: 0,
      y: 0,
      toJSON: vi.fn(),
    };
    vi.spyOn(slider, 'getBoundingClientRect').mockReturnValue(
      mockRect as DOMRect,
    );

    fireEvent.mouseDown(slider, { clientX: 25 });
    fireEvent.mouseMove(document, { clientX: 75 });
    fireEvent.mouseUp(document);

    await waitFor(() => {
      expect(mockOnVolumeChange).toHaveBeenCalled();
    });
  });

  it('should display volume value when showValue is true', () => {
    render(<VolumeControl {...defaultProps} showValue={true} />);

    expect(screen.getByText('50%')).toBeInTheDocument();
  });

  it('should display "Mute" when muted and showValue is true', () => {
    render(<VolumeControl {...defaultProps} muted={true} showValue={true} />);

    expect(screen.getByText('Mute')).toBeInTheDocument();
  });

  it('should not display volume value when showValue is false', () => {
    render(<VolumeControl {...defaultProps} showValue={false} />);

    expect(screen.queryByText('50%')).not.toBeInTheDocument();
  });

  it('should hide slider when showSlider is false', () => {
    render(<VolumeControl {...defaultProps} showSlider={false} />);

    expect(screen.queryByRole('slider')).not.toBeInTheDocument();
  });

  it('should be disabled when disabled prop is true', () => {
    render(<VolumeControl {...defaultProps} disabled={true} />);

    const muteButton = screen.getByLabelText('Volume: 50%');
    expect(muteButton).toBeDisabled();
    expect(muteButton).toHaveAttribute('aria-disabled', 'true');

    const slider = screen.getByRole('slider');
    expect(slider).toHaveAttribute('aria-disabled', 'true');
  });

  it('should not call callbacks when disabled', async () => {
    const user = userEvent.setup();

    render(<VolumeControl {...defaultProps} disabled={true} />);

    const muteButton = screen.getByLabelText('Volume: 50%');

    // Disabled buttons should not trigger callbacks
    fireEvent.click(muteButton);

    expect(mockOnMuteToggle).not.toHaveBeenCalled();
  });

  it('should have correct aria attributes', () => {
    render(<VolumeControl {...defaultProps} />);

    const slider = screen.getByRole('slider');
    expect(slider).toHaveAttribute('aria-label', 'Volume');
    expect(slider).toHaveAttribute('aria-valuemin', '0');
    expect(slider).toHaveAttribute('aria-valuemax', '100');
    expect(slider).toHaveAttribute('aria-valuenow', '50');
  });

  it('should show volume as 0 when muted', () => {
    const { container } = render(
      <VolumeControl {...defaultProps} muted={true} />,
    );

    const slider = screen.getByRole('slider');
    expect(slider).toHaveAttribute('aria-valuenow', '0');

    const volumeTrack = container.querySelector('[class*="bg-primary"]');
    expect(volumeTrack).toHaveStyle({ width: '0%' });
  });

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

    expect(container.firstChild).toHaveClass('custom-class');
  });

  it('should handle volume at 0', () => {
    render(<VolumeControl {...defaultProps} volume={0} />);

    const muteButton = screen.getByLabelText('Volume: 0%');
    expect(muteButton).toBeInTheDocument();
  });

  it('should handle volume at 100', () => {
    render(<VolumeControl {...defaultProps} volume={100} />);

    const muteButton = screen.getByLabelText('Volume: 100%');
    expect(muteButton).toBeInTheDocument();
  });

  it('should clean up event listeners on unmount', () => {
    const removeEventListenerSpy = vi.spyOn(document, 'removeEventListener');
    const { container, unmount } = render(<VolumeControl {...defaultProps} />);

    const slider = container.querySelector('[role="slider"]') as HTMLElement;
    const mockRect = {
      left: 0,
      top: 0,
      width: 100,
      height: 10,
      bottom: 10,
      right: 100,
      x: 0,
      y: 0,
      toJSON: vi.fn(),
    };
    vi.spyOn(slider, 'getBoundingClientRect').mockReturnValue(
      mockRect as DOMRect,
    );

    fireEvent.mouseDown(slider);
    fireEvent.mouseUp(document);

    unmount();

    expect(removeEventListenerSpy).toHaveBeenCalled();
  });
});
