/**
 * AudioManager.js
 * Manages game sound effects using wx.createWebAudioContext for programmatic synthesis.
 * No external audio files needed — all sounds are generated via PCM buffers.
 */

class AudioManager {
  constructor() {
    this._soundEnabled = true;
    this._musicEnabled = true;

    /** @type {AudioContext|null} WebAudio context */
    this._audioCtx = null;

    // Cached audio buffers for each sound
    /** @type {Map<string, AudioBuffer>} */
    this._buffers = new Map();

    this._initialized = false;

    // Listen for settings changes
    if (typeof GameGlobal !== 'undefined' && GameGlobal.eventBus) {
      GameGlobal.eventBus.on('settings:changed', (settings) => {
        this._soundEnabled = settings.soundEnabled;
        this._musicEnabled = settings.musicEnabled;
      });
    }
  }

  /**
   * Initialize WebAudio context and generate all sound buffers.
   * Must be called after user interaction (touch) on some platforms.
   */
  init() {
    if (this._initialized) return;

    try {
      if (typeof wx !== 'undefined' && wx.createWebAudioContext) {
        this._audioCtx = wx.createWebAudioContext();
      }
    } catch (e) {
      console.warn('[AudioManager] Failed to create WebAudioContext:', e);
    }

    if (!this._audioCtx) {
      console.warn('[AudioManager] WebAudioContext not available, audio disabled.');
      return;
    }

    // Pre-generate all sound effect buffers
    this._generateSounds();
    this._initialized = true;
    console.log('[AudioManager] Initialized with programmatic audio synthesis.');
  }

  /**
   * Generate all game sound effect buffers.
   * @private
   */
  _generateSounds() {
    const ctx = this._audioCtx;
    const sampleRate = ctx.sampleRate;

    // Shoot sound: short high-frequency burst
    this._buffers.set('shoot', this._generateBuffer(sampleRate, 0.08, (i, len) => {
      const t = i / len;
      const freq = 800 - t * 400;
      const envelope = 1 - t;
      return Math.sin(2 * Math.PI * freq * i / sampleRate) * envelope * 0.3;
    }));

    // Explosion (small): noise burst with decay
    this._buffers.set('explosion_small', this._generateBuffer(sampleRate, 0.2, (i, len) => {
      const t = i / len;
      const envelope = (1 - t) * (1 - t);
      const noise = Math.random() * 2 - 1;
      const tone = Math.sin(2 * Math.PI * 120 * i / sampleRate);
      return (noise * 0.6 + tone * 0.4) * envelope * 0.35;
    }));

    // Explosion (big): longer, deeper noise burst
    this._buffers.set('explosion_big', this._generateBuffer(sampleRate, 0.4, (i, len) => {
      const t = i / len;
      const envelope = (1 - t) * (1 - t);
      const noise = Math.random() * 2 - 1;
      const tone = Math.sin(2 * Math.PI * 60 * i / sampleRate);
      const tone2 = Math.sin(2 * Math.PI * 90 * i / sampleRate);
      return (noise * 0.5 + tone * 0.3 + tone2 * 0.2) * envelope * 0.4;
    }));

    // Hit (bullet hits armor but doesn't destroy): metallic ping
    this._buffers.set('hit', this._generateBuffer(sampleRate, 0.1, (i, len) => {
      const t = i / len;
      const envelope = (1 - t);
      return Math.sin(2 * Math.PI * 1200 * i / sampleRate) * envelope * 0.2 +
             Math.sin(2 * Math.PI * 2400 * i / sampleRate) * envelope * 0.1;
    }));

    // Bullet hit wall: short thud
    this._buffers.set('hit_wall', this._generateBuffer(sampleRate, 0.06, (i, len) => {
      const t = i / len;
      const envelope = (1 - t);
      const noise = Math.random() * 2 - 1;
      return (noise * 0.4 + Math.sin(2 * Math.PI * 300 * i / sampleRate) * 0.6) * envelope * 0.2;
    }));

    // Power-up pickup: ascending chime
    this._buffers.set('powerup', this._generateBuffer(sampleRate, 0.25, (i, len) => {
      const t = i / len;
      const freq = 400 + t * 800;
      const envelope = t < 0.1 ? t / 0.1 : (1 - (t - 0.1) / 0.9);
      return (Math.sin(2 * Math.PI * freq * i / sampleRate) * 0.5 +
              Math.sin(2 * Math.PI * freq * 1.5 * i / sampleRate) * 0.2) * envelope * 0.3;
    }));

    // Game over: descending tone
    this._buffers.set('gameover', this._generateBuffer(sampleRate, 0.6, (i, len) => {
      const t = i / len;
      const freq = 400 - t * 250;
      const envelope = 1 - t;
      return Math.sin(2 * Math.PI * freq * i / sampleRate) * envelope * 0.3;
    }));

    // Victory: ascending fanfare
    this._buffers.set('victory', this._generateBuffer(sampleRate, 0.5, (i, len) => {
      const t = i / len;
      // Three-note ascending pattern
      let freq;
      if (t < 0.33) freq = 523; // C5
      else if (t < 0.66) freq = 659; // E5
      else freq = 784; // G5
      const segT = (t % 0.33) / 0.33;
      const envelope = segT < 0.1 ? segT / 0.1 : Math.max(0, 1 - (segT - 0.1) / 0.9);
      return (Math.sin(2 * Math.PI * freq * i / sampleRate) * 0.4 +
              Math.sin(2 * Math.PI * freq * 2 * i / sampleRate) * 0.15) * envelope * 0.3;
    }));

    // Move: low rumble (very short, for tank movement)
    this._buffers.set('move', this._generateBuffer(sampleRate, 0.05, (i, len) => {
      const t = i / len;
      const envelope = 1 - t;
      return Math.sin(2 * Math.PI * 80 * i / sampleRate) * envelope * 0.1;
    }));
  }

  /**
   * Generate a PCM audio buffer.
   * @private
   * @param {number} sampleRate
   * @param {number} duration - Duration in seconds.
   * @param {Function} generator - (sampleIndex, totalSamples) => sampleValue [-1, 1]
   * @returns {AudioBuffer}
   */
  _generateBuffer(sampleRate, duration, generator) {
    const ctx = this._audioCtx;
    const length = Math.floor(sampleRate * duration);
    const buffer = ctx.createBuffer(1, length, sampleRate);
    const data = buffer.getChannelData(0);
    for (let i = 0; i < length; i++) {
      data[i] = generator(i, length);
    }
    return buffer;
  }

  /**
   * Play a sound effect by name.
   * @param {string} name - Sound name (shoot, explosion_small, explosion_big, hit, hit_wall, powerup, gameover, victory, move).
   */
  playSFX(name) {
    if (!this._soundEnabled || !this._initialized || !this._audioCtx) return;

    const buffer = this._buffers.get(name);
    if (!buffer) return;

    try {
      const source = this._audioCtx.createBufferSource();
      source.buffer = buffer;
      source.connect(this._audioCtx.destination);
      source.start(0);
    } catch (e) {
      // Silently ignore playback errors
    }
  }

  /**
   * Register a sound effect (kept for backward compatibility).
   * @param {string} name
   * @param {string} path
   */
  register(name, path) {
    // No-op: sounds are now generated programmatically
  }

  /**
   * Play background music (no-op for now, can be implemented later with audio files).
   * @param {string} path
   */
  playBGM(path) {
    // BGM requires audio files — not implemented in programmatic mode
  }

  /** Stop background music. */
  stopBGM() {}

  /** Pause all audio. */
  pauseAll() {}

  /** Resume audio. */
  resumeAll() {}

  /** Destroy audio context. */
  destroy() {
    if (this._audioCtx) {
      try { this._audioCtx.close(); } catch (e) {}
      this._audioCtx = null;
    }
    this._buffers.clear();
    this._initialized = false;
  }

  /** Whether sound effects are enabled. */
  get soundEnabled() { return this._soundEnabled; }
  set soundEnabled(v) { this._soundEnabled = v; }

  /** Whether music is enabled. */
  get musicEnabled() { return this._musicEnabled; }
  set musicEnabled(v) { this._musicEnabled = v; }
}

module.exports = AudioManager;
