import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AudioProvider, useAudio } from './AudioContext';
import { ReactNode } from 'react';
import { Track } from '@/types';

const wrapper = ({ children }: { children: ReactNode }) => (
  <AudioProvider>{children}</AudioProvider>
);

const mockTrack: Track = {
  id: '1',
  title: 'Test Track',
  artist: 'Test Artist',
  duration: '3:45',
  durationSec: 225,
  plays: 100,
  likes: 10,
};

describe('AudioContext', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('should provide audio context', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    expect(result.current).toBeDefined();
    expect(result.current).toHaveProperty('currentTrack');
    expect(result.current).toHaveProperty('isPlaying');
    expect(result.current).toHaveProperty('queue');
    expect(result.current).toHaveProperty('playTrack');
    expect(result.current).toHaveProperty('togglePlay');
  });

  it('should have initial track', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    expect(result.current.currentTrack).toBeDefined();
    expect(result.current.currentTrack).toHaveProperty('title');
  });

  it('should play a track', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    act(() => {
      result.current.playTrack(mockTrack);
    });

    expect(result.current.currentTrack).toEqual(mockTrack);
  });

  it('should toggle play/pause', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    const initialPlaying = result.current.isPlaying;

    act(() => {
      result.current.togglePlay();
    });

    expect(result.current.isPlaying).toBe(!initialPlaying);
  });

  it('should set volume', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    act(() => {
      result.current.setVolume(50);
    });

    expect(result.current.volume).toBe(50);
  });

  it('should toggle mute', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    const initialMuted = result.current.isMuted;

    act(() => {
      result.current.toggleMute();
    });

    expect(result.current.isMuted).toBe(!initialMuted);
  });

  it('should toggle shuffle', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    const initialShuffle = result.current.shuffle;

    act(() => {
      result.current.toggleShuffle();
    });

    expect(result.current.shuffle).toBe(!initialShuffle);
  });

  it('should toggle repeat mode', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    act(() => {
      result.current.toggleRepeat();
    });

    expect(['off', 'all', 'one']).toContain(result.current.repeatMode);
  });

  it('should seek to position', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    act(() => {
      result.current.seek(50);
    });

    expect(result.current.progress).toBe(50);
  });

  it('should add track to queue', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    const initialQueueLength = result.current.queue.length;

    act(() => {
      result.current.addToQueue(mockTrack);
    });

    expect(result.current.queue.length).toBe(initialQueueLength + 1);
  });

  it('should remove track from queue', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    const initialQueueLength = result.current.queue.length;

    if (initialQueueLength > 0) {
      const trackId = result.current.queue[0].id;

      act(() => {
        result.current.removeFromQueue(trackId);
      });

      expect(result.current.queue.length).toBe(initialQueueLength - 1);
    }
  });

  it('should clear queue', () => {
    const { result } = renderHook(() => useAudio(), { wrapper });

    act(() => {
      result.current.clearQueue();
    });

    expect(result.current.queue.length).toBe(0);
  });
});
