/**
 * AudioEngine — 纯 Web Audio API 程序化合成，零音频文件依赖
 *
 * 使用单例模式，所有音效通过 AudioEngine.getInstance().play('soundName') 调用
 * 背景音乐通过 startBGM() / stopBGM() 控制
 */
export class AudioEngine {
  private static _instance: AudioEngine | null = null
  private ctx: AudioContext | null = null
  private bgmNodes: AudioNode[] = []
  private bgmRunning = false
  private muted = false
  private masterGain!: GainNode

  private constructor() {}

  static getInstance(): AudioEngine {
    if (!AudioEngine._instance) AudioEngine._instance = new AudioEngine()
    return AudioEngine._instance
  }

  /** 首次用户交互后调用，初始化 AudioContext */
  init(): void {
    if (this.ctx) return
    try {
      this.ctx = new AudioContext()
      this.masterGain = this.ctx.createGain()
      this.masterGain.gain.value = 0.55
      this.masterGain.connect(this.ctx.destination)
    } catch {
      // 浏览器不支持 Web Audio，静默失败
    }
  }

  setMuted(v: boolean): void {
    this.muted = v
    if (this.masterGain) this.masterGain.gain.value = v ? 0 : 0.55
  }
  isMuted(): boolean { return this.muted }

  private get ac(): AudioContext | null { return this.ctx }

  // ─── 工具方法 ────────────────────────────────────────────────────────────────

  private osc(
    type: OscillatorType,
    freq: number,
    startTime: number,
    endTime: number,
    gainStart = 0.3,
    gainEnd = 0,
    destination: AudioNode | null = null
  ): void {
    if (!this.ac) return
    const g = this.ac.createGain()
    g.gain.setValueAtTime(gainStart, startTime)
    g.gain.exponentialRampToValueAtTime(Math.max(gainEnd, 0.001), endTime)
    g.connect(destination ?? this.masterGain)
    const o = this.ac.createOscillator()
    o.type = type
    o.frequency.setValueAtTime(freq, startTime)
    o.connect(g)
    o.start(startTime)
    o.stop(endTime)
  }

  private noise(
    startTime: number,
    duration: number,
    gainVal = 0.15,
    hipass = 0,
    destination: AudioNode | null = null
  ): void {
    if (!this.ac) return
    const bufLen = this.ac.sampleRate * duration
    const buf = this.ac.createBuffer(1, bufLen, this.ac.sampleRate)
    const data = buf.getChannelData(0)
    for (let i = 0; i < bufLen; i++) data[i] = Math.random() * 2 - 1
    const src = this.ac.createBufferSource()
    src.buffer = buf
    const g = this.ac.createGain()
    g.gain.setValueAtTime(gainVal, startTime)
    g.gain.exponentialRampToValueAtTime(0.001, startTime + duration)
    if (hipass > 0) {
      const f = this.ac.createBiquadFilter()
      f.type = 'highpass'
      f.frequency.value = hipass
      src.connect(f)
      f.connect(g)
    } else {
      src.connect(g)
    }
    g.connect(destination ?? this.masterGain)
    src.start(startTime)
    src.stop(startTime + duration)
  }

  // ─── 音效库 ─────────────────────────────────────────────────────────────────

  /** 钉钉消息通知音：建塔 / 波次开始 */
  playDingTalk(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    // 叮 — 高频正弦
    this.osc('sine', 1047, t,       t + 0.18, 0.4, 0.001)
    // 咚 — 低频跟随
    this.osc('sine',  523, t + 0.1, t + 0.35, 0.25, 0.001)
  }

  /** 钉钉消息发送音（更轻）：波次开始 */
  playNotify(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.osc('sine', 880, t,        t + 0.12, 0.3, 0.001)
    this.osc('sine', 1320, t + 0.07, t + 0.2, 0.2, 0.001)
  }

  /** 拳击音：实习生近战 */
  playPunch(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.noise(t, 0.06, 0.3, 200)
    this.osc('sine', 120, t, t + 0.06, 0.35, 0.001)
  }

  /** 键盘敲击音：资深开发射击 */
  playKeyboard(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.noise(t, 0.04, 0.18, 1200)
    this.osc('square', 440, t, t + 0.03, 0.12, 0.001)
  }

  /** PPT 大师 AOE — 空洞回声 */
  playPPTBlast(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.osc('sawtooth', 180, t, t + 0.4, 0.2, 0.001)
    this.osc('sine',      90, t, t + 0.5, 0.15, 0.001)
    this.noise(t, 0.3, 0.08, 100)
  }

  /** 产品经理 — 需求变更铃 */
  playPMAttack(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.osc('triangle', 660, t,        t + 0.15, 0.25, 0.001)
    this.osc('triangle', 440, t + 0.08, t + 0.25, 0.2, 0.001)
  }

  /** 运营专员 — 数据上涨 */
  playOpsAttack(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.osc('sine', 523, t,        t + 0.08, 0.2, 0.001)
    this.osc('sine', 659, t + 0.05, t + 0.15, 0.2, 0.001)
    this.osc('sine', 784, t + 0.1,  t + 0.22, 0.2, 0.001)
  }

  /** 外包程序员 — 闷声丢出 */
  playOutsourceAttack(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.noise(t, 0.08, 0.2, 300)
    this.osc('sawtooth', 200, t, t + 0.08, 0.15, 0.001)
  }

  /** 怪物死亡 — 碎纸机 */
  playEnemyDeath(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.noise(t, 0.12, 0.22, 800)
    this.noise(t + 0.05, 0.08, 0.15, 2000)
  }

  /** Boss 登场 — 警报 + 低沉冲击 */
  playBossAppear(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    // 低沉轰鸣
    this.osc('sawtooth', 55, t,      t + 1.2, 0.5, 0.001)
    this.osc('sawtooth', 60, t + 0.1, t + 1.0, 0.3, 0.001)
    // 警报扫频
    for (let i = 0; i < 4; i++) {
      const st = t + i * 0.25
      this.osc('square', 440, st,        st + 0.1, 0.25, 0.001)
      this.osc('square', 660, st + 0.12, st + 0.22, 0.2, 0.001)
    }
  }

  /** Boss 技能触发 — 电话挂断 */
  playBossSkill(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.osc('square', 1480, t,        t + 0.06, 0.3, 0.001)
    this.osc('square', 1109, t + 0.07, t + 0.13, 0.3, 0.001)
    this.osc('square',  740, t + 0.14, t + 0.22, 0.3, 0.001)
    this.noise(t, 0.25, 0.1, 1000)
  }

  /** 塔被摧毁 — 电话挂断 */
  playTowerDestroyed(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.osc('square', 440, t,       t + 0.1,  0.35, 0.001)
    this.osc('square', 350, t + 0.1, t + 0.25, 0.35, 0.001)
    this.osc('square', 220, t + 0.2, t + 0.4,  0.35, 0.001)
    this.noise(t, 0.4, 0.12, 500)
  }

  /** 新波次开始 — 钉钉来电铃声 */
  playWaveStart(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    const pattern = [0, 0.18, 0.36, 0.64, 0.82, 1.0]
    const freqs   = [784, 784, 784, 659, 784, 880]
    pattern.forEach((delay, i) => {
      this.osc('sine', freqs[i], t + delay, t + delay + 0.14, 0.3, 0.001)
    })
  }

  /** KPI 告警（KPI < 30%）— 紧张低音 */
  playKPIWarning(): void {
    if (!this.ac) return
    const t = this.ac.currentTime
    this.osc('sawtooth', 110, t,       t + 0.3, 0.4, 0.001)
    this.osc('sawtooth', 146, t + 0.1, t + 0.3, 0.3, 0.001)
  }

  // ─── 背景音乐 ────────────────────────────────────────────────────────────────

  /**
   * 启动背景音乐
   * 风格：轻快 chiptune / 像素塔防，BPM=128，C大调，4层结构
   * 完全程序化合成，无音频文件依赖
   */
  startBGM(): void {
    if (!this.ac || this.bgmRunning) return
    this.bgmRunning = true
    this._scheduleBGM()
  }

  stopBGM(): void {
    this.bgmRunning = false
    this.bgmNodes.forEach(n => {
      try { (n as OscillatorNode).stop?.() } catch { /* ignore */ }
    })
    this.bgmNodes = []
  }

  private _scheduleBGM(): void {
    if (!this.ac || !this.bgmRunning) return
    const ac = this.ac
    const t = ac.currentTime

    const BPM = 128
    const beat = 60 / BPM          // 0.469s / 拍
    const bar  = beat * 4           // 1.875s / 小节
    const BARS = 8                  // 循环 8 小节
    const loopLen = bar * BARS      // ≈ 15s

    // ── 辅助：创建带 Gain 的振荡器，自动连接到 masterGain ──────────────────
    const addOsc = (
      type: OscillatorType,
      freq: number,
      start: number,
      end: number,
      vol: number,
      freqEnd?: number
    ): void => {
      const g = ac.createGain()
      g.gain.setValueAtTime(vol, start)
      g.gain.exponentialRampToValueAtTime(0.0001, end)
      g.connect(this.masterGain)
      const o = ac.createOscillator()
      o.type = type
      o.frequency.setValueAtTime(freq, start)
      if (freqEnd !== undefined) {
        o.frequency.linearRampToValueAtTime(freqEnd, end)
      }
      o.connect(g)
      o.start(start)
      o.stop(end)
      this.bgmNodes.push(o)
    }

    // ── 辅助：创建打击音（noise burst） ────────────────────────────────────
    const addDrum = (start: number, dur: number, vol: number, hp = 0): void => {
      const bufLen = Math.ceil(ac.sampleRate * dur)
      const buf = ac.createBuffer(1, bufLen, ac.sampleRate)
      const d = buf.getChannelData(0)
      for (let i = 0; i < bufLen; i++) d[i] = Math.random() * 2 - 1
      const src = ac.createBufferSource()
      src.buffer = buf
      const g = ac.createGain()
      g.gain.setValueAtTime(vol, start)
      g.gain.exponentialRampToValueAtTime(0.0001, start + dur)
      if (hp > 0) {
        const f = ac.createBiquadFilter()
        f.type = 'highpass'
        f.frequency.value = hp
        src.connect(f); f.connect(g)
      } else {
        src.connect(g)
      }
      g.connect(this.masterGain)
      src.start(start)
      src.stop(start + dur)
    }

    // ── 音符频率表（C4 = 261.63 Hz） ──────────────────────────────────────
    // C4  D4  E4  F4  G4  A4  B4  C5  D5  E5  G5
    const N: Record<string, number> = {
      C4: 261.63, D4: 293.66, E4: 329.63, F4: 349.23,
      G4: 392.00, A4: 440.00, B4: 493.88,
      C5: 523.25, D5: 587.33, E5: 659.25, G5: 783.99,
      // 低八度
      C3: 130.81, E3: 164.81, F3: 174.61, G3: 196.00, A3: 220.00,
    }

    // ═══════════════════════════════════════════════════════════════
    // 层 1：底鼓 + 军鼓（kick on 1,3；snare on 2,4）
    // ═══════════════════════════════════════════════════════════════
    for (let b = 0; b < BARS * 4; b++) {
      const st = t + b * beat
      const pos = b % 4  // 每小节内位置 0-3

      if (pos === 0 || pos === 2) {
        // Kick：低频 sine 扫频（100→30Hz）+ noise burst
        addOsc('sine', 100, st, st + 0.18, 0.45, 30)
        addDrum(st, 0.12, 0.12, 0)
      }
      if (pos === 1 || pos === 3) {
        // Snare：中频 noise + 轻微 sine
        addDrum(st, 0.1, 0.18, 800)
        addOsc('sine', 220, st, st + 0.07, 0.08)
      }
      // Hi-hat 每半拍（8 分音符）
      addDrum(st,            0.05, 0.07, 6000)
      addDrum(st + beat / 2, 0.04, 0.05, 6000)
    }

    // ═══════════════════════════════════════════════════════════════
    // 层 2：和弦低音线 (square, 低八度)
    // 进行：C - F - G - C - Am - F - G - C
    // ═══════════════════════════════════════════════════════════════
    const bassChord = [
      N.C3, N.F3, N.G3, N.C3,
      N.A3, N.F3, N.G3, N.C3,
    ]
    for (let b = 0; b < BARS; b++) {
      const st = t + b * bar
      const freq = bassChord[b]
      // 每小节 4 拍低音 arpeggio（根音每拍重复）
      for (let i = 0; i < 4; i++) {
        addOsc('square', freq, st + i * beat, st + i * beat + beat * 0.8, 0.1)
      }
      // 五度音（和声感）
      addOsc('triangle', freq * 1.5, st, st + bar * 0.9, 0.035)
    }

    // ═══════════════════════════════════════════════════════════════
    // 层 3：主旋律（triangle，亮而柔和，8小节 melody）
    // 轻快的 C 大调旋律，8小节/32拍
    // ═══════════════════════════════════════════════════════════════
    // 每项：[频率, 持续拍数]
    const melody: [number, number][] = [
      // 小节 1-2：上行跳跃
      [N.E4, 0.5], [N.G4, 0.5], [N.C5, 1], [N.B4, 0.5], [N.A4, 0.5],
      [N.G4, 1],   [N.A4, 0.5], [N.G4, 0.5],
      // 小节 3-4：中段装饰
      [N.E4, 0.5], [N.F4, 0.5], [N.G4, 1], [N.E4, 0.5], [N.D4, 0.5],
      [N.C4, 1],   [N.D4, 0.5], [N.E4, 0.5],
      // 小节 5-6：高潮段
      [N.G4, 0.5], [N.A4, 0.5], [N.C5, 1], [N.D5, 0.5], [N.C5, 0.5],
      [N.B4, 1],   [N.A4, 0.5], [N.G4, 0.5],
      // 小节 7-8：收尾回落
      [N.E4, 0.5], [N.D4, 0.5], [N.C4, 0.5], [N.E4, 0.5],
      [N.G4, 0.5], [N.F4, 0.5], [N.E4, 0.5], [N.D4, 0.5],
      [N.C4, 1.5], [0, 0.5],
    ]
    let melodyT = t
    for (const [freq, dur] of melody) {
      const durSec = dur * beat
      if (freq > 0) {
        addOsc('triangle', freq, melodyT, melodyT + durSec * 0.85, 0.18)
        // 加一层八度泛音（更亮更饱满）
        addOsc('sine', freq * 2, melodyT, melodyT + durSec * 0.6, 0.06)
      }
      melodyT += durSec
    }

    // ═══════════════════════════════════════════════════════════════
    // 层 4：装饰音（高八度 sine 短促音符，活跃感）
    // 每2小节出现一次 arpegio 装饰
    // ═══════════════════════════════════════════════════════════════
    const decoPattern = [
      // 小节2结尾装饰
      { st: t + bar * 1.75, notes: [N.C5, N.E5, N.G5] },
      // 小节4结尾装饰
      { st: t + bar * 3.75, notes: [N.G4, N.C5, N.E5] },
      // 小节6结尾装饰
      { st: t + bar * 5.75, notes: [N.C5, N.D5, N.E5] },
    ]
    for (const { st, notes } of decoPattern) {
      notes.forEach((freq, i) => {
        addOsc('sine', freq, st + i * 0.08, st + i * 0.08 + 0.12, 0.1)
      })
    }

    // 循环调度
    setTimeout(() => {
      if (this.bgmRunning) {
        this.bgmNodes = []
        this._scheduleBGM()
      }
    }, (loopLen - 0.3) * 1000)
  }
}
