import type { StageParams } from './Director';

// §6.6 程序合成：8 小节 8-bit 治愈循环（方波主旋律 + 三角波低音）+ 鸟鸣声部 + 4 音效。无外部音频文件。
const BPM = 100;
const BEAT = 60 / BPM;          // 0.6s
const LOOP_BEATS = 32;          // 8 小节 × 4 拍
const LOOP_SEC = LOOP_BEATS * BEAT; // 19.2s
const SR = 44100;

const midi = (n: number) => 440 * Math.pow(2, (n - 69) / 12);
// 主旋律（32 个四分音符，C 大调，明亮）
const LEAD = [72, 76, 79, 76, 77, 76, 74, 72, 74, 76, 77, 79, 76, 74, 72, 74,
  79, 81, 79, 77, 76, 77, 79, 76, 72, 74, 76, 77, 79, 76, 72, 72].map(midi);
// 低音（每小节 2 个半音符，I–vi–IV–V）
const BASS_ROOTS = [48, 45, 41, 43, 48, 45, 41, 43].map(midi);

function square(ph: number) { return Math.sin(ph) >= 0 ? 1 : -1; }
function triangle(ph: number) { return (2 / Math.PI) * Math.asin(Math.sin(ph)); }

function renderMusic(): AudioBuffer {
  const buf = new AudioBuffer({ length: Math.floor(LOOP_SEC * SR), sampleRate: SR, numberOfChannels: 1 });
  const d = buf.getChannelData(0);
  const addNote = (freq: number, start: number, dur: number, amp: number, wave: (p: number) => number) => {
    const s0 = Math.floor(start * SR), s1 = Math.floor((start + dur) * SR);
    for (let i = s0; i < s1 && i < d.length; i++) {
      const t = (i - s0) / SR;
      // 简单 AD 包络
      const env = Math.min(1, t / 0.01) * Math.exp(-t * 3.2);
      d[i] += amp * env * wave(2 * Math.PI * freq * (i / SR));
    }
  };
  // lead: 四分音符
  for (let i = 0; i < LEAD.length; i++) addNote(LEAD[i], i * BEAT, BEAT * 0.9, 0.12, square);
  // bass: 每小节两个半音符
  for (let b = 0; b < 8; b++) {
    const root = BASS_ROOTS[b];
    addNote(root, b * 4 * BEAT, BEAT * 2 * 0.95, 0.11, triangle);
    addNote(root, b * 4 * BEAT + 2 * BEAT, BEAT * 2 * 0.95, 0.11, triangle);
  }
  return buf;
}

function renderBirds(): AudioBuffer {
  const buf = new AudioBuffer({ length: Math.floor(LOOP_SEC * SR), sampleRate: SR, numberOfChannels: 1 });
  const d = buf.getChannelData(0);
  const chirp = (start: number) => {
    const dur = 0.09;
    const s0 = Math.floor(start * SR), s1 = Math.floor((start + dur) * SR);
    for (let i = s0; i < s1 && i < d.length; i++) {
      const t = (i - s0) / SR;
      const f = 2400 + 900 * t / dur;                 // 上滑
      const env = Math.min(1, t / 0.005) * Math.exp(-t * 22);
      d[i] += 0.07 * env * Math.sin(2 * Math.PI * f * t);
    }
  };
  // 每小节两声鸟鸣
  for (let b = 0; b < 8; b++) { chirp(b * 4 * BEAT + 0.5); chirp(b * 4 * BEAT + 2.3); }
  return buf;
}

class AudioEngine {
  ctx: AudioContext | null = null;
  private master!: GainNode;
  private musicGain!: GainNode;
  private birdGain!: GainNode;
  private filter!: BiquadFilterNode;
  private musicSrc: AudioBufferSourceNode | null = null;
  private birdSrc: AudioBufferSourceNode | null = null;
  private musicBuf!: AudioBuffer;
  private birdBuf!: AudioBuffer;
  private started = false;
  private baseMaster = 0.5;
  private birdLevel = 1.0;

  start() {
    if (this.started) { this.ctx?.resume(); return; }
    this.ctx = new (window.AudioContext || (window as any).webkitAudioContext)();
    const ctx = this.ctx;
    this.master = ctx.createGain(); this.master.gain.value = this.baseMaster; this.master.connect(ctx.destination);
    this.filter = ctx.createBiquadFilter(); this.filter.type = 'lowpass'; this.filter.frequency.value = 20000; this.filter.connect(this.master);
    this.musicGain = ctx.createGain(); this.musicGain.gain.value = 1; this.musicGain.connect(this.filter);
    this.birdGain = ctx.createGain(); this.birdGain.gain.value = this.birdLevel; this.birdGain.connect(this.filter);
    this.musicBuf = renderMusic(); this.birdBuf = renderBirds();
    this._spawnSources();
    this.started = true;
  }

  private _spawnSources() {
    if (!this.ctx) return;
    this.musicSrc = this.ctx.createBufferSource(); this.musicSrc.buffer = this.musicBuf; this.musicSrc.loop = true; this.musicSrc.connect(this.musicGain); this.musicSrc.start();
    this.birdSrc = this.ctx.createBufferSource(); this.birdSrc.buffer = this.birdBuf; this.birdSrc.loop = true; this.birdSrc.connect(this.birdGain); this.birdSrc.start();
  }

  setStage(p: StageParams) {
    if (!this.ctx) return;
    const now = this.ctx.currentTime;
    this.musicSrc?.playbackRate.setTargetAtTime(p.playbackRate, now, 0.4);
    this.birdSrc?.playbackRate.setTargetAtTime(p.playbackRate, now, 0.4);
    this.filter.frequency.setTargetAtTime(p.lowpass > 0 ? p.lowpass : 20000, now, 0.4);
    this.birdGain.gain.setTargetAtTime(p.birdMute ? 0 : this.birdLevel, now, 0.4);
  }

  /** T14「突然的安静」：淡入淡出主音量 */
  fadeMaster(level: number, seconds: number) {
    if (!this.ctx) return;
    const now = this.ctx.currentTime;
    this.master.gain.cancelScheduledValues(now);
    this.master.gain.setValueAtTime(this.master.gain.value, now);
    this.master.gain.linearRampToValueAtTime(level, now + seconds);
  }
  restoreMaster(seconds = 1) { this.fadeMaster(this.baseMaster, seconds); }

  // ---- SFX（全部程序合成）----
  private blip(freq: number, dur: number, type: OscillatorType, amp = 0.3, slideTo?: number) {
    if (!this.ctx) return;
    const ctx = this.ctx, now = ctx.currentTime;
    const o = ctx.createOscillator(), g = ctx.createGain();
    o.type = type; o.frequency.setValueAtTime(freq, now);
    if (slideTo) o.frequency.exponentialRampToValueAtTime(slideTo, now + dur);
    g.gain.setValueAtTime(0.0001, now);
    g.gain.exponentialRampToValueAtTime(amp, now + 0.008);
    g.gain.exponentialRampToValueAtTime(0.0001, now + dur);
    o.connect(g); g.connect(this.master); o.start(now); o.stop(now + dur + 0.02);
  }
  sfxBeep() { this.blip(620, 0.05, 'square', 0.12); }               // 对话哔哔
  sfxDing() { this.blip(880, 0.12, 'square', 0.22); setTimeout(() => this.blip(1320, 0.18, 'square', 0.2), 90); } // 任务完成叮
  sfxBell() { this.blip(392, 1.6, 'triangle', 0.3); setTimeout(() => this.blip(261, 2.0, 'triangle', 0.25), 120); } // 黄昏钟声
  sfxEcg() { this.blip(1000, 0.15, 'sine', 0.35); }                 // E2 心电监护滴声
}

export const Audio = new AudioEngine();
