import { describe, it, expect, vi, beforeEach } from 'vitest'
import { musicService, MusicTrack } from '../../shared/services/music'
import type { MusicVibe } from '../../shared/types'

vi.mock('../../shared/supabase', () => ({
  isSupabaseConfigured: () => false,
  supabase: {},
}))

describe('music service', () => {
  const vibes: MusicVibe[] = ['electronic', 'hip-hop', 'pop', 'rock', 'chill']

  beforeEach(() => {
    vi.clearAllMocks()
    musicService.clearCache()
  })

  describe('loadTracksForVibe', () => {
    it('should return mock tracks when Supabase not configured', async () => {
      const tracks = await musicService.loadTracksForVibe('electronic')
      
      expect(tracks).toBeDefined()
      expect(tracks.length).toBeGreaterThan(0)
    })

    it('should return tracks for each vibe', async () => {
      for (const vibe of vibes) {
        const tracks = await musicService.loadTracksForVibe(vibe)
        expect(tracks).toBeDefined()
        expect(tracks.length).toBeGreaterThan(0)
      }
    })

    it('should cache tracks after first load', async () => {
      const tracks1 = await musicService.loadTracksForVibe('electronic')
      const tracks2 = await musicService.loadTracksForVibe('electronic')
      
      expect(tracks1).toBe(tracks2)
    })

    it('should return tracks with correct vibe property', async () => {
      for (const vibe of vibes) {
        const tracks = await musicService.loadTracksForVibe(vibe)
        tracks.forEach(track => {
          expect(track.vibe).toBe(vibe)
        })
      }
    })

    it('should return tracks with required properties', async () => {
      const tracks = await musicService.loadTracksForVibe('electronic')
      
      tracks.forEach(track => {
        expect(track.id).toBeDefined()
        expect(track.title).toBeDefined()
        expect(track.artist).toBeDefined()
        expect(track.duration).toBeDefined()
        expect(track.url).toBeDefined()
        expect(track.vibe).toBeDefined()
      })
    })
  })

  describe('clearCache', () => {
    it('should clear specific vibe cache', async () => {
      await musicService.loadTracksForVibe('electronic')
      await musicService.loadTracksForVibe('hip-hop')
      
      musicService.clearCache('electronic')
      
      const tracks = await musicService.loadTracksForVibe('hip-hop')
      expect(tracks).toBeDefined()
    })

    it('should clear all cache when no vibe specified', async () => {
      await musicService.loadTracksForVibe('electronic')
      await musicService.loadTracksForVibe('hip-hop')
      
      musicService.clearCache()
      
      expect(musicService).toBeDefined()
    })
  })

  describe('getRandomTrack', () => {
    it('should return null for empty array', () => {
      const track = musicService.getRandomTrack([])
      expect(track).toBeNull()
    })

    it('should return a track from the array', () => {
      const tracks = [
        { id: '1', title: 'Track 1', artist: 'Artist 1', duration: 180, url: '', vibe: 'electronic' as MusicVibe },
        { id: '2', title: 'Track 2', artist: 'Artist 2', duration: 200, url: '', vibe: 'electronic' as MusicVibe },
      ]
      
      const track = musicService.getRandomTrack(tracks)
      expect(track).not.toBeNull()
      expect(['1', '2']).toContain(track!.id)
    })

    it('should return the only track for single-element array', () => {
      const tracks = [
        { id: '1', title: 'Track 1', artist: 'Artist 1', duration: 180, url: '', vibe: 'electronic' as MusicVibe },
      ]
      
      const track = musicService.getRandomTrack(tracks)
      expect(track).not.toBeNull()
      expect(track!.id).toBe('1')
    })
  })

  describe('getNextTrack', () => {
    const tracks = [
      { id: '1', title: 'Track 1', artist: 'Artist 1', duration: 180, url: '', vibe: 'electronic' as MusicVibe },
      { id: '2', title: 'Track 2', artist: 'Artist 2', duration: 200, url: '', vibe: 'electronic' as MusicVibe },
      { id: '3', title: 'Track 3', artist: 'Artist 3', duration: 220, url: '', vibe: 'electronic' as MusicVibe },
    ]

    it('should return null for empty array', () => {
      const track = musicService.getNextTrack([], '1')
      expect(track).toBeNull()
    })

    it('should return the only track for single-element array', () => {
      const singleTrack = [tracks[0]]
      const track = musicService.getNextTrack(singleTrack, '1')
      expect(track).not.toBeNull()
      expect(track!.id).toBe('1')
    })

    it('should return next track in sequence', () => {
      const track = musicService.getNextTrack(tracks, '1', false)
      expect(track).not.toBeNull()
      expect(track!.id).toBe('2')
    })

    it('should wrap around to first track', () => {
      const track = musicService.getNextTrack(tracks, '3', false)
      expect(track).not.toBeNull()
      expect(track!.id).toBe('1')
    })

    it('should return random track when shuffle is true', () => {
      const track = musicService.getNextTrack(tracks, '1', true)
      expect(track).not.toBeNull()
      expect(track!.id).not.toBe('1')
    })

    it('should return different track when shuffling single remaining', () => {
      const twoTracks = [tracks[0], tracks[1]]
      const track = musicService.getNextTrack(twoTracks, '1', true)
      expect(track).not.toBeNull()
      expect(track!.id).toBe('2')
    })
  })
})
