'use client'

import { useCallback, useRef, useEffect } from 'react'
import { createContext, useContext } from 'react'

// Audio file paths
const AUDIO_FILES = {
  rain: '/sounds/rain-ambient.mp3',
  thunder: '/sounds/thunder.mp3',
  transform: '/sounds/transform-complete.mp3',
  enter: '/sounds/enter.mp3',
}

export function useSound() {
  const audioContextRef = useRef<AudioContext | null>(null)
  const audioBuffersRef = useRef<Map<string, AudioBuffer>>(new Map())
  const activeSourcesRef = useRef<Map<string, AudioBufferSourceNode>>(new Map())
  const rainGainRef = useRef<GainNode | null>(null)

  const getContext = useCallback(() => {
    if (!audioContextRef.current) {
      audioContextRef.current = new (window.AudioContext || (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext)()
    }
    // Resume if suspended (autoplay policy)
    if (audioContextRef.current.state === 'suspended') {
      audioContextRef.current.resume()
    }
    return audioContextRef.current
  }, [])

  // Load an audio file into buffer
  const loadAudio = useCallback(async (key: string, url: string) => {
    try {
      const ctx = getContext()
      const response = await fetch(url)
      const arrayBuffer = await response.arrayBuffer()
      const audioBuffer = await ctx.decodeAudioData(arrayBuffer)
      audioBuffersRef.current.set(key, audioBuffer)
      return audioBuffer
    } catch (error) {
      console.warn(`Failed to load audio: ${url}`, error)
      return null
    }
  }, [getContext])

  // Play a loaded audio buffer
  const playBuffer = useCallback((key: string, options: {
    loop?: boolean
    volume?: number
    fadeIn?: number
  } = {}) => {
    const buffer = audioBuffersRef.current.get(key)
    if (!buffer) return null

    try {
      const ctx = getContext()
      const source = ctx.createBufferSource()
      const gainNode = ctx.createGain()

      source.buffer = buffer
      source.loop = options.loop || false

      source.connect(gainNode)
      gainNode.connect(ctx.destination)

      if (options.fadeIn) {
        gainNode.gain.setValueAtTime(0, ctx.currentTime)
        gainNode.gain.linearRampToValueAtTime(options.volume || 0.5, ctx.currentTime + options.fadeIn)
      } else {
        gainNode.gain.setValueAtTime(options.volume || 0.5, ctx.currentTime)
      }

      source.start(0)
      activeSourcesRef.current.set(key, source)

      if (key === 'rain') {
        rainGainRef.current = gainNode
      }

      return { source, gainNode }
    } catch {
      return null
    }
  }, [getContext])

  // Stop a playing sound with optional fade out
  const stopSound = useCallback((key: string, fadeOut: number = 0) => {
    const source = activeSourcesRef.current.get(key)
    if (!source) return

    try {
      const ctx = getContext()

      if (fadeOut > 0 && key === 'rain' && rainGainRef.current) {
        rainGainRef.current.gain.linearRampToValueAtTime(0, ctx.currentTime + fadeOut)
        setTimeout(() => {
          source.stop()
          activeSourcesRef.current.delete(key)
        }, fadeOut * 1000)
      } else {
        source.stop()
        activeSourcesRef.current.delete(key)
      }
    } catch {
      activeSourcesRef.current.delete(key)
    }
  }, [getContext])

  // Preload all audio files (returns true if at least rain loaded)
  const preloadAudio = useCallback(async () => {
    const results = await Promise.all([
      loadAudio('rain', AUDIO_FILES.rain),
      loadAudio('thunder', AUDIO_FILES.thunder),
      loadAudio('transform', AUDIO_FILES.transform),
      loadAudio('enter', AUDIO_FILES.enter),
    ])
    // Return whether rain loaded successfully (for fallback logic)
    return results[0] !== null
  }, [loadAudio])

  // === AMBIENT RAIN ===
  const startRain = useCallback(() => {
    if (activeSourcesRef.current.has('rain')) return
    playBuffer('rain', { loop: true, volume: 0.08, fadeIn: 4 })
  }, [playBuffer])

  const stopRain = useCallback(() => {
    stopSound('rain', 2)
  }, [stopSound])

  // === THUNDER (random intervals) ===
  const playThunder = useCallback(() => {
    playBuffer('thunder', { volume: 0.15 })
  }, [playBuffer])

  // === TRANSFORMATION COMPLETE ===
  const playTransformComplete = useCallback(() => {
    playBuffer('transform', { volume: 0.2 })
  }, [playBuffer])

  // === ENTER SOUND ===
  const playEnter = useCallback(() => {
    playBuffer('enter', { volume: 0.15 })
  }, [playBuffer])

  // === SYNTHESIZED SOUNDS (for micro-interactions) ===

  // Hover - low resonant pulse
  const playHover = useCallback(() => {
    try {
      const ctx = getContext()
      const osc = ctx.createOscillator()
      const gain = ctx.createGain()
      const filter = ctx.createBiquadFilter()

      osc.connect(filter)
      filter.connect(gain)
      gain.connect(ctx.destination)

      osc.type = 'sine'
      osc.frequency.setValueAtTime(80, ctx.currentTime)

      filter.type = 'lowpass'
      filter.frequency.setValueAtTime(200, ctx.currentTime)
      filter.Q.setValueAtTime(5, ctx.currentTime)

      gain.gain.setValueAtTime(0, ctx.currentTime)
      gain.gain.linearRampToValueAtTime(0.04, ctx.currentTime + 0.02)
      gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.15)

      osc.start(ctx.currentTime)
      osc.stop(ctx.currentTime + 0.15)
    } catch {
      // Silent fail
    }
  }, [getContext])

  // Click - deep muted impact
  const playClick = useCallback(() => {
    try {
      const ctx = getContext()

      // Layer 1: Low thump
      const osc1 = ctx.createOscillator()
      const gain1 = ctx.createGain()

      osc1.connect(gain1)
      gain1.connect(ctx.destination)

      osc1.type = 'sine'
      osc1.frequency.setValueAtTime(80, ctx.currentTime)
      osc1.frequency.exponentialRampToValueAtTime(40, ctx.currentTime + 0.1)

      gain1.gain.setValueAtTime(0.1, ctx.currentTime)
      gain1.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.15)

      osc1.start(ctx.currentTime)
      osc1.stop(ctx.currentTime + 0.15)

      // Layer 2: Subtle high click
      const osc2 = ctx.createOscillator()
      const gain2 = ctx.createGain()
      const filter = ctx.createBiquadFilter()

      osc2.connect(filter)
      filter.connect(gain2)
      gain2.connect(ctx.destination)

      osc2.type = 'triangle'
      osc2.frequency.setValueAtTime(800, ctx.currentTime)

      filter.type = 'highpass'
      filter.frequency.setValueAtTime(600, ctx.currentTime)

      gain2.gain.setValueAtTime(0.02, ctx.currentTime)
      gain2.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.05)

      osc2.start(ctx.currentTime)
      osc2.stop(ctx.currentTime + 0.05)
    } catch {
      // Silent fail
    }
  }, [getContext])

  // Error - muted static/glitch
  const playError = useCallback(() => {
    try {
      const ctx = getContext()

      // Create noise
      const bufferSize = ctx.sampleRate * 0.2
      const noiseBuffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate)
      const output = noiseBuffer.getChannelData(0)

      for (let i = 0; i < bufferSize; i++) {
        output[i] = Math.random() * 2 - 1
      }

      const noise = ctx.createBufferSource()
      const gain = ctx.createGain()
      const filter = ctx.createBiquadFilter()

      noise.buffer = noiseBuffer

      noise.connect(filter)
      filter.connect(gain)
      gain.connect(ctx.destination)

      filter.type = 'bandpass'
      filter.frequency.setValueAtTime(400, ctx.currentTime)
      filter.Q.setValueAtTime(2, ctx.currentTime)

      gain.gain.setValueAtTime(0.08, ctx.currentTime)
      gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.2)

      noise.start(ctx.currentTime)
      noise.stop(ctx.currentTime + 0.2)
    } catch {
      // Silent fail
    }
  }, [getContext])

  // Legacy ambient drone (fallback if no audio file)
  const playAmbientDrone = useCallback(() => {
    try {
      const ctx = getContext()

      const osc1 = ctx.createOscillator()
      const osc2 = ctx.createOscillator()
      const gain = ctx.createGain()
      const filter = ctx.createBiquadFilter()

      osc1.connect(filter)
      osc2.connect(filter)
      filter.connect(gain)
      gain.connect(ctx.destination)

      osc1.type = 'sine'
      osc2.type = 'sine'
      osc1.frequency.setValueAtTime(55, ctx.currentTime)
      osc2.frequency.setValueAtTime(57, ctx.currentTime)

      filter.type = 'lowpass'
      filter.frequency.setValueAtTime(150, ctx.currentTime)

      gain.gain.setValueAtTime(0, ctx.currentTime)
      gain.gain.linearRampToValueAtTime(0.015, ctx.currentTime + 3)

      osc1.start(ctx.currentTime)
      osc2.start(ctx.currentTime)

      return () => {
        gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 2)
        setTimeout(() => {
          osc1.stop()
          osc2.stop()
        }, 2000)
      }
    } catch {
      return () => {}
    }
  }, [getContext])

  return {
    // Audio file playback
    preloadAudio,
    startRain,
    stopRain,
    playThunder,
    playTransformComplete,
    playEnter,
    // Synthesized sounds
    playHover,
    playClick,
    playError,
    playAmbientDrone,
    // Utilities
    getContext,
  }
}

// Sound context for global sound state
interface SoundContextType {
  enabled: boolean
  setEnabled: (enabled: boolean) => void
}

export const SoundContext = createContext<SoundContextType>({
  enabled: false,
  setEnabled: () => {}
})

export function useSoundContext() {
  return useContext(SoundContext)
}
