import { describe, it, expect, beforeEach } from 'vitest';
import { act, renderHook } from '@testing-library/react';
import { usePlayerStore } from './playerStore';
import type { Track } from '../types';

describe('playerStore', () => {
  const mockTrack1: Track = {
    id: 1,
    title: 'Track 1',
    duration: 180,
    url: 'https://example.com/track1.mp3',
  };

  const mockTrack2: Track = {
    id: 2,
    title: 'Track 2',
    duration: 200,
    url: 'https://example.com/track2.mp3',
  };

  const mockTrack3: Track = {
    id: 3,
    title: 'Track 3',
    duration: 220,
    url: 'https://example.com/track3.mp3',
  };

  beforeEach(() => {
    // Reset store before each test
    const { result } = renderHook(() => usePlayerStore());
    act(() => {
      result.current.clearQueue();
      result.current.stop();
    });
  });

  describe('Initial state', () => {
    it('should have correct initial state', () => {
      const { result } = renderHook(() => usePlayerStore());

      expect(result.current.currentTrack).toBeNull();
      expect(result.current.isPlaying).toBe(false);
      expect(result.current.currentTime).toBe(0);
      expect(result.current.duration).toBe(0);
      expect(result.current.volume).toBe(100);
      expect(result.current.muted).toBe(false);
      expect(result.current.queue).toEqual([]);
      expect(result.current.currentIndex).toBe(-1);
      expect(result.current.repeat).toBe('off');
      expect(result.current.shuffle).toBe(false);
    });
  });

  describe('Play actions', () => {
    it('should play a track', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.play(mockTrack1);
      });

      expect(result.current.currentTrack).toEqual(mockTrack1);
      expect(result.current.isPlaying).toBe(true);
      expect(result.current.currentTime).toBe(0);
      expect(result.current.queue).toContainEqual(mockTrack1);
      expect(result.current.currentIndex).toBe(0);
    });

    it('should resume playback when play is called without track', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.play(mockTrack1);
        result.current.pause();
        result.current.play();
      });

      expect(result.current.isPlaying).toBe(true);
    });

    it('should add track to queue if not already present', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.play(mockTrack1);
        result.current.play(mockTrack2);
      });

      expect(result.current.queue).toHaveLength(2);
      expect(result.current.currentTrack).toEqual(mockTrack2);
      expect(result.current.currentIndex).toBe(1);
    });

    it('should switch to track if already in queue', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2]);
        result.current.play(mockTrack1);
      });

      expect(result.current.currentTrack).toEqual(mockTrack1);
      expect(result.current.currentIndex).toBe(0);
      expect(result.current.queue).toHaveLength(2);
    });
  });

  describe('Pause and resume', () => {
    it('should pause playback', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.play(mockTrack1);
        result.current.pause();
      });

      expect(result.current.isPlaying).toBe(false);
    });

    it('should resume playback', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.play(mockTrack1);
        result.current.pause();
        result.current.resume();
      });

      expect(result.current.isPlaying).toBe(true);
    });

    it('should stop playback', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.play(mockTrack1);
        result.current.setCurrentTime(50);
        result.current.stop();
      });

      expect(result.current.isPlaying).toBe(false);
      expect(result.current.currentTime).toBe(0);
    });
  });

  describe('Navigation', () => {
    it('should go to next track', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2, mockTrack3]);
        result.current.play(mockTrack1);
        result.current.next();
      });

      expect(result.current.currentTrack).toEqual(mockTrack2);
      expect(result.current.currentIndex).toBe(1);
      expect(result.current.currentTime).toBe(0);
    });

    it('should go to previous track', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2]);
        result.current.play(mockTrack2);
        result.current.previous();
      });

      expect(result.current.currentTrack).toEqual(mockTrack1);
      expect(result.current.currentIndex).toBe(0);
    });

    it('should not go to previous if at first track', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.play(mockTrack1);
        const initialIndex = result.current.currentIndex;
        result.current.previous();
      });

      expect(result.current.currentIndex).toBe(0);
    });

    it('should loop playlist when repeat is enabled', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2]);
        result.current.setRepeat('playlist');
        result.current.play(mockTrack2);
        result.current.next();
      });

      expect(result.current.currentTrack).toEqual(mockTrack1);
      expect(result.current.currentIndex).toBe(0);
    });

    it('should not go to next if at end and repeat is off', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2]);
        result.current.setRepeat('off');
        result.current.play(mockTrack2);
        result.current.next();
      });

      expect(result.current.currentTrack).toEqual(mockTrack2);
      expect(result.current.currentIndex).toBe(1);
    });
  });

  describe('Seek and time', () => {
    it('should set current time', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.setDuration(180);
        result.current.setCurrentTime(50);
      });

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

    it('should clamp current time to duration', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.setDuration(180);
        result.current.setCurrentTime(200);
      });

      expect(result.current.currentTime).toBe(180);
    });

    it('should not allow negative time', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.setCurrentTime(-10);
      });

      expect(result.current.currentTime).toBe(0);
    });

    it('should set duration', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.setDuration(180);
      });

      expect(result.current.duration).toBe(180);
    });

    it('should not allow negative duration', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.setDuration(-10);
      });

      expect(result.current.duration).toBe(0);
    });

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

      act(() => {
        result.current.setDuration(180);
        result.current.seek(90);
      });

      expect(result.current.currentTime).toBe(90);
    });
  });

  describe('Volume control', () => {
    it('should set volume', () => {
      const { result } = renderHook(() => usePlayerStore());

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

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

    it('should clamp volume to 0-100', () => {
      const { result } = renderHook(() => usePlayerStore());

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

      expect(result.current.volume).toBe(100);

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

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

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

      expect(result.current.muted).toBe(false);

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

      expect(result.current.muted).toBe(true);

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

      expect(result.current.muted).toBe(false);
    });
  });

  describe('Repeat and shuffle', () => {
    it('should toggle shuffle', () => {
      const { result } = renderHook(() => usePlayerStore());

      expect(result.current.shuffle).toBe(false);

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

      expect(result.current.shuffle).toBe(true);
    });

    it('should set repeat mode', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.setRepeat('track');
      });

      expect(result.current.repeat).toBe('track');

      act(() => {
        result.current.setRepeat('playlist');
      });

      expect(result.current.repeat).toBe('playlist');
    });
  });

  describe('Queue management', () => {
    it('should add tracks to queue', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2]);
      });

      expect(result.current.queue).toHaveLength(2);
      expect(result.current.queue).toContainEqual(mockTrack1);
      expect(result.current.queue).toContainEqual(mockTrack2);
    });

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

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2, mockTrack3]);
        result.current.removeFromQueue(1);
      });

      expect(result.current.queue).toHaveLength(2);
      expect(result.current.queue).not.toContainEqual(mockTrack2);
    });

    it('should adjust current index when removing track before current', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2, mockTrack3]);
        result.current.play(mockTrack2);
        result.current.removeFromQueue(0);
      });

      expect(result.current.currentIndex).toBe(0);
      expect(result.current.currentTrack).toEqual(mockTrack2);
    });

    it('should handle removing current track', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2]);
        result.current.play(mockTrack1);
        result.current.removeFromQueue(0);
      });

      expect(result.current.currentTrack).toEqual(mockTrack2);
      expect(result.current.currentIndex).toBe(0);
    });

    it('should stop playback when removing last track', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.play(mockTrack1);
        result.current.removeFromQueue(0);
      });

      expect(result.current.currentTrack).toBeNull();
      expect(result.current.isPlaying).toBe(false);
      expect(result.current.currentIndex).toBe(-1);
    });

    it('should reorder queue', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2, mockTrack3]);
        result.current.reorderQueue(0, 2);
      });

      expect(result.current.queue[0]).toEqual(mockTrack2);
      expect(result.current.queue[2]).toEqual(mockTrack1);
    });

    it('should adjust current index when reordering', () => {
      const { result } = renderHook(() => usePlayerStore());

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2, mockTrack3]);
        result.current.play(mockTrack1);
        result.current.reorderQueue(0, 2);
      });

      expect(result.current.currentIndex).toBe(2);
      expect(result.current.currentTrack).toEqual(mockTrack1);
    });

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

      act(() => {
        result.current.addToQueue([mockTrack1, mockTrack2]);
        result.current.play(mockTrack1);
        result.current.clearQueue();
      });

      expect(result.current.queue).toHaveLength(0);
      expect(result.current.currentTrack).toBeNull();
      expect(result.current.isPlaying).toBe(false);
      expect(result.current.currentIndex).toBe(-1);
      expect(result.current.currentTime).toBe(0);
    });
  });
});
