import { storage, KEYS } from '../utils/storage.js';

const MAX_GAIN = 0.4;

class SoundManager {
  constructor() {
    this.ctx = null;
    this.master = null;
    this.musicNodes = null;

    this.muted = storage.getItem(KEYS.muted, '0') === '1';
    this.volume = Phaser.Math.Clamp(storage.getFloat(KEYS.volume, 1), 0, 1);
  }

  init() {
    if (this.ctx) return;
    const AC = window.AudioContext || window.webkitAudioContext;
    if (!AC) return;
    this.ctx = new AC();
    this.master = this.ctx.createGain();
    this.master.gain.value = this._effectiveGain();
    this.master.connect(this.ctx.destination);
  }

  resume() {
    if (this.ctx && this.ctx.state === 'suspended') {
      this.ctx.resume();
    }
  }

  _effectiveGain() {
    return this.muted ? 0 : MAX_GAIN * this.volume;
  }

  _applyGain() {
    if (this.master) this.master.gain.value = this._effectiveGain();
  }

  setMuted(value) {
    this.muted = value;
    storage.setItem(KEYS.muted, value ? '1' : '0');
    this._applyGain();
  }

  isMuted() {
    return this.muted;
  }

  setVolume(value) {
    this.volume = Phaser.Math.Clamp(value, 0, 1);
    storage.setItem(KEYS.volume, this.volume);
    this._applyGain();
  }

  getVolume() {
    return this.volume;
  }

  _beep(freq, duration, type = 'square', volume = 0.3) {
    if (!this.ctx || this.muted) return;
    const osc = this.ctx.createOscillator();
    const gain = this.ctx.createGain();
    osc.type = type;
    osc.frequency.setValueAtTime(freq, this.ctx.currentTime);
    gain.gain.setValueAtTime(volume, this.ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + duration);
    osc.connect(gain);
    gain.connect(this.master);
    osc.start();
    osc.stop(this.ctx.currentTime + duration);
  }

  _sweep(freqStart, freqEnd, duration, type = 'square', volume = 0.3) {
    if (!this.ctx || this.muted) return;
    const osc = this.ctx.createOscillator();
    const gain = this.ctx.createGain();
    osc.type = type;
    osc.frequency.setValueAtTime(freqStart, this.ctx.currentTime);
    osc.frequency.exponentialRampToValueAtTime(Math.max(1, freqEnd), this.ctx.currentTime + duration);
    gain.gain.setValueAtTime(volume, this.ctx.currentTime);
    gain.gain.exponentialRampToValueAtTime(0.001, this.ctx.currentTime + duration);
    osc.connect(gain);
    gain.connect(this.master);
    osc.start();
    osc.stop(this.ctx.currentTime + duration);
  }

  _noise(duration, volume = 0.15) {
    if (!this.ctx || this.muted) return;
    const bufferSize = this.ctx.sampleRate * duration;
    const buffer = this.ctx.createBuffer(1, bufferSize, this.ctx.sampleRate);
    const data = buffer.getChannelData(0);
    for (let i = 0; i < bufferSize; i++) {
      data[i] = (Math.random() * 2 - 1) * (1 - i / bufferSize);
    }
    const src = this.ctx.createBufferSource();
    src.buffer = buffer;
    const gain = this.ctx.createGain();
    gain.gain.value = volume;
    src.connect(gain);
    gain.connect(this.master);
    src.start();
  }

  jump() {
    this._sweep(320, 540, 0.1, 'square', 0.14);
  }

  land() {
    this._beep(180, 0.06, 'triangle', 0.10);
  }

  spring() {
    this._sweep(280, 1000, 0.18, 'square', 0.22);
    setTimeout(() => this._sweep(800, 1300, 0.12, 'triangle', 0.15), 50);
  }

  powerup() {
    [440, 554, 659, 880].forEach((f, i) => setTimeout(() => this._beep(f, 0.13, 'triangle', 0.20), i * 55));
  }

  stomp() {
    this._sweep(420, 100, 0.16, 'sawtooth', 0.22);
    this._noise(0.08, 0.10);
  }

  break() {
    this._noise(0.15, 0.12);
    this._sweep(200, 60, 0.2, 'sawtooth', 0.10);
  }

  death() {
    this._sweep(280, 50, 0.5, 'sawtooth', 0.28);
    setTimeout(() => this._noise(0.3, 0.15), 100);
  }

  milestone() {
    [523, 659, 783, 1046].forEach((f, i) => setTimeout(() => this._beep(f, 0.22, 'triangle', 0.25), i * 90));
  }

  click() {
    this._beep(660, 0.05, 'square', 0.12);
  }

  newBest() {
    [523, 659, 783, 1046, 1318].forEach((f, i) => setTimeout(() => this._beep(f, 0.18, 'triangle', 0.25), i * 80));
  }
}

export const sound = new SoundManager();
