import { SoundStorage } from '@/storage/storages'

interface CachedSound {
  id: string
  name: string
  base64: string
}

// Core storage instances
const soundStorage = new SoundStorage()
const activeSounds = new Map<string, HTMLAudioElement[]>()
const audioCache = new Map<string, HTMLAudioElement>()

export function useSoundComposable() {
  /**
   * Converts a sound URL to base64 format
   */
  async function soundToBase64(url: string): Promise<string> {
    try {
      const response = await fetch(url)
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`)
      }

      const blob = await response.blob()
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onloadend = () => resolve((reader.result as string).split(',')[1])
        reader.onerror = reject
        reader.readAsDataURL(blob)
      })
    } catch (error) {
      console.error('Error converting sound to base64:', error)
      throw error
    }
  }

  /**
   * Preloads a sound file and caches it for future use
   */
  async function preloadSound(soundUrl: string): Promise<HTMLAudioElement> {
    if (audioCache.has(soundUrl)) {
      return audioCache.get(soundUrl)!
    }

    let audio: HTMLAudioElement
    const cachedSound = await soundStorage.get(soundUrl)

    if (cachedSound) {
      audio = new Audio(`data:audio/mpeg;base64,${cachedSound.base64}`)
    } else {
      try {
        const base64 = await soundToBase64(soundUrl)
        const soundData: CachedSound = {
          id: soundUrl,
          name: soundUrl.split('/').pop() || soundUrl,
          base64
        }
        await soundStorage.add(soundData)
        audio = new Audio(`data:audio/mpeg;base64,${base64}`)
      } catch (error) {
        console.error(`Failed to load and cache sound ${soundUrl}:`, error)
        audio = new Audio(soundUrl)
      }
    }

    audio.load()
    audioCache.set(soundUrl, audio)
    return audio
  }

  /**
   * Plays a sound with optional looping and duplicate prevention
   */
  async function playSound(soundUrl: string, loop: boolean = false, ignoreIfPlaying: boolean = false): Promise<void> {
    try {
      const playingSounds = activeSounds.get(soundUrl) || []

      if (ignoreIfPlaying && playingSounds.some((audio) => !audio.paused)) {
        return
      }

      stopSound(soundUrl)

      const audio = await preloadSound(soundUrl)
      const playingAudio = audio.cloneNode() as HTMLAudioElement
      playingAudio.loop = loop

      if (!activeSounds.has(soundUrl)) {
        activeSounds.set(soundUrl, [])
      }

      activeSounds.get(soundUrl)!.push(playingAudio)

      // Cleanup when sound ends
      playingAudio.addEventListener(
        'ended',
        () => {
          const sounds = activeSounds.get(soundUrl)
          if (!sounds) return

          const index = sounds.indexOf(playingAudio)
          if (index > -1) {
            sounds.splice(index, 1)
          }
          if (sounds.length === 0) {
            activeSounds.delete(soundUrl)
          }
        },
        { once: true }
      )

      await playingAudio.play()
    } catch (error) {
      console.error(`Failed to play sound ${soundUrl}:`, error)
    }
  }

  /**
   * Stops all instances of a specific sound
   */
  function stopSound(soundUrl: string): void {
    const sounds = activeSounds.get(soundUrl)
    if (!sounds) return

    sounds.forEach((audio) => {
      audio.pause()
      audio.currentTime = 0
    })
    activeSounds.delete(soundUrl)
  }

  /**
   * Stops all currently playing sounds
   */
  function stopAllSounds(): void {
    activeSounds.forEach((_, url) => stopSound(url))
    activeSounds.clear()
  }

  /**
   * Clears all cached sounds and stops playback
   */
  async function clearSoundCache(): Promise<void> {
    stopAllSounds()
    audioCache.clear()
    await soundStorage.reset()
  }

  /**
   * Preloads multiple sounds simultaneously
   */
  async function preloadSounds(soundUrls: string[]): Promise<HTMLAudioElement[]> {
    return Promise.all(soundUrls.map(preloadSound))
  }

  return {
    playSound,
    stopSound,
    stopAllSounds,
    clearSoundCache,
    preloadSounds
  }
}
