// BGM 与音效（PRD §6.6）：全部 Web Audio 程序合成，无外部音频文件。
// 8 小节 8-bit 治愈循环（方波主旋律 + 三角波低音），鸟鸣独立声部。
// playbackRate 与低通滤波由 CorruptionDirector 矩阵驱动。
import { director } from "./director";

const TEMPO = 108;
const BEAT = 60 / TEMPO;
const BARS = 8;
const LOOP_LEN = BARS * 4 * BEAT;

function midi(n: number): number { return 440 * Math.pow(2, (n - 69) / 12); }

// 主旋律：8 小节，每小节 4 拍，八分音符网格（0 = 休止）
// C 大调五声，甜到发腻
const LEAD: number[] = [
  72, 0, 76, 0, 79, 0, 76, 0, /**/ 81, 0, 79, 76, 74, 0, 72, 0,
  72, 74, 76, 0, 79, 0, 81, 0, /**/ 79, 0, 76, 0, 74, 0, 0, 0,
  79, 0, 81, 0, 84, 0, 81, 0, /**/ 79, 76, 79, 0, 81, 0, 84, 0,
  84, 81, 79, 76, 74, 76, 79, 0, /**/ 72, 0, 74, 0, 72, 0, 0, 0,
];
// 低音：每拍一个四分音符
const BASS: number[] = [
  48, 55, 52, 55, /**/ 45, 52, 48, 52,
  48, 55, 52, 55, /**/ 43, 50, 47, 50,
  48, 55, 52, 55, /**/ 45, 52, 48, 52,
  41, 48, 43, 50, /**/ 48, 52, 55, 48,
];

export class AudioEngine {
  private static inst: AudioEngine;
  static get(): AudioEngine { if (!this.inst) this.inst = new AudioEngine(); return this.inst; }

  ctx!: AudioContext;
  private master!: GainNode;
  private lowpass!: BiquadFilterNode;
  private bgmGain!: GainNode;
  private birdGain!: GainNode;
  private bgmSrc: AudioBufferSourceNode | null = null;
  private birdSrc: AudioBufferSourceNode | null = null;
  started = false;
  private silenceHold = false;

  async start() {
    if (this.started) return;
    this.started = true;
    this.ctx = new AudioContext();
    this.master = this.ctx.createGain();
    this.master.gain.value = 0.8;
    this.master.connect(this.ctx.destination);
    this.lowpass = this.ctx.createBiquadFilter();
    this.lowpass.type = "lowpass";
    this.lowpass.frequency.value = 18000;
    this.lowpass.connect(this.master);
    this.bgmGain = this.ctx.createGain(); this.bgmGain.gain.value = 0.5; this.bgmGain.connect(this.lowpass);
    this.birdGain = this.ctx.createGain(); this.birdGain.gain.value = 0.5; this.birdGain.connect(this.lowpass);

    const [bgm, birds] = await Promise.all([this.renderBGM(), this.renderBirds()]);
    this.bgmSrc = this.ctx.createBufferSource();
    this.bgmSrc.buffer = bgm; this.bgmSrc.loop = true;
    this.bgmSrc.connect(this.bgmGain); this.bgmSrc.start();
    this.birdSrc = this.ctx.createBufferSource();
    this.birdSrc.buffer = birds; this.birdSrc.loop = true;
    this.birdSrc.connect(this.birdGain); this.birdSrc.start();
    this.applyStage();
  }

  private async renderBGM(): Promise<AudioBuffer> {
    const sr = 44100;
    const off = new OfflineAudioContext(1, Math.ceil(LOOP_LEN * sr), sr);
    const bus = off.createGain(); bus.gain.value = 0.5; bus.connect(off.destination);
    // 方波主旋律
    LEAD.forEach((n, i) => {
      if (!n) return;
      const t = i * BEAT / 2;
      const osc = off.createOscillator(); osc.type = "square"; osc.frequency.value = midi(n);
      const g = off.createGain();
      g.gain.setValueAtTime(0, t);
      g.gain.linearRampToValueAtTime(0.22, t + 0.01);
      g.gain.exponentialRampToValueAtTime(0.001, t + BEAT * 0.45);
      osc.connect(g); g.connect(bus);
      osc.start(t); osc.stop(t + BEAT * 0.5);
    });
    // 三角波低音
    BASS.forEach((n, i) => {
      if (!n) return;
      const t = i * BEAT;
      const osc = off.createOscillator(); osc.type = "triangle"; osc.frequency.value = midi(n);
      const g = off.createGain();
      g.gain.setValueAtTime(0, t);
      g.gain.linearRampToValueAtTime(0.3, t + 0.02);
      g.gain.exponentialRampToValueAtTime(0.001, t + BEAT * 0.9);
      osc.connect(g); g.connect(bus);
      osc.start(t); osc.stop(t + BEAT);
    });
    return off.startRendering();
  }

  private async renderBirds(): Promise<AudioBuffer> {
    const sr = 44100;
    const off = new OfflineAudioContext(1, Math.ceil(LOOP_LEN * sr), sr);
    const bus = off.createGain(); bus.gain.value = 0.35; bus.connect(off.destination);
    // 确定性鸟鸣：短促下滑音簇
    const times = [0.8, 1.1, 4.2, 4.45, 7.9, 8.15, 8.4, 11.6, 13.2, 13.4, 15.8];
    for (const t0 of times) {
      for (let k = 0; k < 2; k++) {
        const t = t0 + k * 0.12;
        const osc = off.createOscillator(); osc.type = "sine";
        osc.frequency.setValueAtTime(2400 + (k % 2) * 300, t);
        osc.frequency.exponentialRampToValueAtTime(1700, t + 0.08);
        const g = off.createGain();
        g.gain.setValueAtTime(0, t);
        g.gain.linearRampToValueAtTime(0.5, t + 0.01);
        g.gain.exponentialRampToValueAtTime(0.001, t + 0.09);
        osc.connect(g); g.connect(bus);
        osc.start(t); osc.stop(t + 0.1);
      }
    }
    return off.startRendering();
  }

  // 侵蚀矩阵联动（§6.4）
  applyStage() {
    if (!this.started || this.silenceHold) return;
    const p = director.audioParams;
    const t = this.ctx.currentTime;
    if (this.bgmSrc) this.bgmSrc.playbackRate.setTargetAtTime(p.rate, t, 0.5);
    if (this.birdSrc) this.birdSrc.playbackRate.setTargetAtTime(p.rate, t, 0.5);
    this.lowpass.frequency.setTargetAtTime(p.lowpass, t, 0.5);
    this.birdGain.gain.setTargetAtTime(p.birds * 0.5, t, 0.5);
  }

  // 「突然的安静」：T14 第 40–60 秒所有声音渐隐至零
  fadeToSilence(seconds: number) {
    if (!this.started) return;
    this.silenceHold = true;
    this.master.gain.cancelScheduledValues(this.ctx.currentTime);
    this.master.gain.setValueAtTime(this.master.gain.value, this.ctx.currentTime);
    this.master.gain.linearRampToValueAtTime(0, this.ctx.currentTime + seconds);
  }
  restoreVolume(seconds = 1.5) {
    if (!this.started) return;
    this.silenceHold = false;
    this.master.gain.cancelScheduledValues(this.ctx.currentTime);
    this.master.gain.setValueAtTime(this.master.gain.value, this.ctx.currentTime);
    this.master.gain.linearRampToValueAtTime(0.8, this.ctx.currentTime + seconds);
    this.applyStage();
  }
  stopBgm() {
    if (this.bgmSrc) { this.bgmSrc.stop(); this.bgmSrc = null; }
    if (this.birdSrc) { this.birdSrc.stop(); this.birdSrc = null; }
  }

  // ---- 音效（仅 4 个，全部程序合成） ----
  private blip(freq: number, dur: number, type: OscillatorType, vol = 0.2, slideTo?: number) {
    if (!this.started) return;
    const t = this.ctx.currentTime;
    const osc = this.ctx.createOscillator(); osc.type = type;
    osc.frequency.setValueAtTime(freq, t);
    if (slideTo) osc.frequency.exponentialRampToValueAtTime(slideTo, t + dur);
    const g = this.ctx.createGain();
    g.gain.setValueAtTime(0, t);
    g.gain.linearRampToValueAtTime(vol, t + 0.005);
    g.gain.exponentialRampToValueAtTime(0.001, t + dur);
    osc.connect(g); g.connect(this.master);
    osc.start(t); osc.stop(t + dur + 0.02);
  }
  // 1. 对话哔哔声
  sfxTalk() { this.blip(760, 0.05, "square", 0.06); }
  // 2. 任务完成叮声
  sfxDing() { this.blip(1318, 0.12, "square", 0.15); setTimeout(() => this.blip(1760, 0.25, "square", 0.15), 90); }
  // 3. 黄昏钟声
  sfxBell() {
    this.blip(523, 1.6, "sine", 0.3);
    this.blip(1046, 1.2, "sine", 0.12);
    setTimeout(() => { this.blip(523, 1.8, "sine", 0.25); this.blip(1046, 1.2, "sine", 0.1); }, 1300);
  }
  // 4. E2 结局的单一心电监护滴声
  sfxEcg() { this.blip(990, 0.14, "sine", 0.35); }
}

export const audio = AudioEngine.get();
