// AudioEngine：WebAudio 程序合成（PRD §6.6），无任何外部音频文件。
// 8 小节 8-bit 循环：方波主旋律 + 三角波低音 + 鸟鸣声部。
// playbackRate 与 BiquadFilter 低通由 CorruptionDirector（§6.4）驱动；S4 鸟鸣静音。
// 音效 4 个：对话哔、完成叮、黄昏钟、E2 心电滴。
// 首次用户交互后才启动 AudioContext。

import { director } from '../core/CorruptionDirector';
import type { Stage, StageParams } from '../types';

// 旋律：C - G - Am - F / C - G - F - G，4/4，100 BPM，每拍一个四分音符
const BPM = 100;
const BEAT = 60 / BPM; // 0.6s
const BARS = 8;

const LEAD: number[][] = [
  [76, 79, 84, 79], // C:  E5 G5 C6 G5
  [74, 79, 83, 79], // G:  D5 G5 B5 G5
  [72, 76, 81, 76], // Am: C5 E5 A5 E5
  [72, 77, 81, 77], // F:  C5 F5 A5 F5
  [79, 76, 72, 76], // C:  G5 E5 C5 E5
  [74, 71, 67, 71], // G:  D5 B4 G4 B4
  [69, 72, 77, 72], // F:  A4 C5 F5 C5
  [67, 71, 74, 67], // G:  G4 B4 D5 G4
];
const BASS: number[] = [48, 43, 45, 41, 48, 43, 41, 43]; // C3 G2 A2 F2 ...
// 鸟鸣：第 2/4/6/8 小节第 4 拍
const BIRD_BARS = [1, 3, 5, 7];

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

export class AudioEngine {
  private static _instance: AudioEngine | null = null;
  static get instance(): AudioEngine {
    if (!this._instance) this._instance = new AudioEngine();
    return this._instance;
  }

  private ctx: AudioContext | null = null;
  private master!: GainNode;
  private lp!: BiquadFilterNode;
  private musicBus!: GainNode;
  private birdBus!: GainNode;
  private sfxBus!: GainNode;

  private rate = 1;
  private started = false;
  private nextNoteTime = 0;
  private step = 0; // 0..31（8 小节 × 4 拍）
  private schedulerId = 0;

  private constructor() {
    // 阶段参数 → 音频参数
    director.on('stage', (_s: Stage, p: StageParams) => {
      this.setParams(p);
    });
  }

  /** 首次用户交互后调用 */
  start(): void {
    if (this.started) return;
    this.started = true;
    const AC = window.AudioContext || (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext;
    this.ctx = new AC();
    this.master = this.ctx.createGain();
    this.master.gain.value = 0.35;
    this.master.connect(this.ctx.destination);

    this.lp = this.ctx.createBiquadFilter();
    this.lp.type = 'lowpass';
    this.lp.frequency.value = 22050;
    this.lp.connect(this.master);

    this.musicBus = this.ctx.createGain();
    this.musicBus.gain.value = 0.9;
    this.musicBus.connect(this.lp);

    this.birdBus = this.ctx.createGain();
    this.birdBus.gain.value = 0.5;
    this.birdBus.connect(this.lp);

    this.sfxBus = this.ctx.createGain();
    this.sfxBus.gain.value = 1;
    this.sfxBus.connect(this.master); // 音效不走低通

    this.nextNoteTime = this.ctx.currentTime + 0.1;
    this.schedulerId = window.setInterval(() => this.scheduler(), 25);
    this.setParams(director.params);
  }

  private setParams(p: StageParams): void {
    this.rate = p.playbackRate;
    if (!this.ctx) return;
    this.lp.frequency.setTargetAtTime(p.lowpass, this.ctx.currentTime, 0.2);
    this.birdBus.gain.setTargetAtTime(p.birdMuted ? 0 : 0.5, this.ctx.currentTime, 0.2);
  }

  private scheduler(): void {
    if (!this.ctx) return;
    const lookAhead = 0.15;
    while (this.nextNoteTime < this.ctx.currentTime + lookAhead) {
      this.scheduleStep(this.step, this.nextNoteTime);
      const beatDur = BEAT / this.rate;
      this.nextNoteTime += beatDur;
      this.step = (this.step + 1) % (BARS * 4);
    }
  }

  private scheduleStep(step: number, time: number): void {
    if (!this.ctx) return;
    const bar = Math.floor(step / 4);
    const beat = step % 4;
    const beatDur = BEAT / this.rate;

    // 方波主旋律
    const lead = LEAD[bar][beat];
    this.tone(midiToFreq(lead), time, beatDur * 0.9, 'square', 0.10, this.musicBus, 0.005, 0.06);

    // 三角波低音（每拍重复根音）
    const bass = BASS[bar];
    this.tone(midiToFreq(bass), time, beatDur * 0.95, 'triangle', 0.16, this.musicBus, 0.01, 0.1);

    // 鸟鸣声部
    if (BIRD_BARS.includes(bar) && beat === 3) {
      this.chirp(time);
      this.chirp(time + beatDur * 0.35);
    }
  }

  private tone(
    freq: number, time: number, dur: number, type: OscillatorType,
    vol: number, bus: GainNode, attack = 0.005, release = 0.08,
  ): void {
    if (!this.ctx) return;
    const osc = this.ctx.createOscillator();
    const g = this.ctx.createGain();
    osc.type = type;
    osc.frequency.value = freq;
    g.gain.setValueAtTime(0.0001, time);
    g.gain.exponentialRampToValueAtTime(vol, time + attack);
    g.gain.setValueAtTime(vol, time + Math.max(attack, dur - release));
    g.gain.exponentialRampToValueAtTime(0.0001, time + dur);
    osc.connect(g).connect(bus);
    osc.start(time);
    osc.stop(time + dur + 0.02);
  }

  /** 鸟鸣：高频正弦快速双音 */
  private chirp(time: number): void {
    if (!this.ctx) return;
    const base = 2600 + Math.random() * 600;
    const osc = this.ctx.createOscillator();
    const g = this.ctx.createGain();
    osc.type = 'sine';
    osc.frequency.setValueAtTime(base, time);
    osc.frequency.exponentialRampToValueAtTime(base * 1.5, time + 0.05);
    g.gain.setValueAtTime(0.0001, time);
    g.gain.exponentialRampToValueAtTime(0.08, time + 0.01);
    g.gain.exponentialRampToValueAtTime(0.0001, time + 0.12);
    osc.connect(g).connect(this.birdBus);
    osc.start(time);
    osc.stop(time + 0.15);
  }

  // ---------- 音效（4 个，全合成） ----------

  /** 对话哔哔声 */
  beep(): void {
    if (!this.ctx) return;
    this.tone(660, this.ctx.currentTime, 0.05, 'square', 0.10, this.sfxBus, 0.002, 0.03);
  }

  /** 任务完成叮声 */
  ding(): void {
    if (!this.ctx) return;
    const t = this.ctx.currentTime;
    this.tone(880, t, 0.12, 'sine', 0.16, this.sfxBus, 0.002, 0.1);
    this.tone(1320, t + 0.1, 0.22, 'sine', 0.16, this.sfxBus, 0.002, 0.18);
  }

  /** 黄昏钟声 */
  bell(): void {
    if (!this.ctx) return;
    const t = this.ctx.currentTime;
    for (let i = 0; i < 2; i++) {
      this.tone(220, t + i * 1.2, 1.4, 'triangle', 0.22, this.sfxBus, 0.01, 1.2);
      this.tone(110, t + i * 1.2, 1.6, 'sine', 0.14, this.sfxBus, 0.01, 1.4);
    }
  }

  /** E2 心电监护滴声（单次） */
  ecg(): void {
    if (!this.ctx) return;
    const t = this.ctx.currentTime;
    this.tone(1000, t, 0.08, 'sine', 0.18, this.sfxBus, 0.002, 0.06);
  }

  /** 井底回声：极轻的监护仪滴声（C7） */
  ecgSoft(): void {
    if (!this.ctx) return;
    const t = this.ctx.currentTime;
    this.tone(1000, t, 0.08, 'sine', 0.05, this.sfxBus, 0.002, 0.06);
  }

  // ---------- T14 第 40–60 秒：所有声音渐隐至零 ----------

  fadeAllToZero(durationMs: number): void {
    if (!this.ctx) return;
    const t = this.ctx.currentTime;
    this.master.gain.cancelScheduledValues(t);
    this.master.gain.setValueAtTime(this.master.gain.value, t);
    this.master.gain.linearRampToValueAtTime(0.0001, t + durationMs / 1000);
  }

  restoreMaster(durationMs = 2000): void {
    if (!this.ctx) return;
    const t = this.ctx.currentTime;
    this.master.gain.cancelScheduledValues(t);
    this.master.gain.setValueAtTime(this.master.gain.value, t);
    this.master.gain.linearRampToValueAtTime(0.35, t + durationMs / 1000);
  }
}

export const audio = AudioEngine.instance;
