// 回响星核 / Echo Nexus — 程序化音频引擎（Web Audio API，零资源文件）
//
// 设计哲学：深空考古的氛围音效，全部由振荡器 + 噪声 + 包络合成，
// 无需任何外部音频文件。音色偏向"谐振/水晶/低频脉冲"，契合全息晶体美学。

type SfxName =
  | "pulse" // 主动脉冲扫描（短促上升音）
  | "pulseCombo" // 连击脉冲（更高音 + 泛音）
  | "decodeClick" // 解码点击节点（按颜色变调）
  | "decodeFail" // 解码点击错误（低沉短音）
  | "decodeSuccess" // 解码成功（和弦上扬）
  | "fragmentUnlock" // 记忆碎片浮现（空灵长音）
  | "techBuy" // 购买技术（确认音）
  | "expeditionStart" // 探险出发（引擎启动）
  | "expeditionNode" // 探险节点结算（中频）
  | "expeditionVictory" // 探险胜利（凯旋和弦）
  | "expeditionDefeat" // 探险失败（下行低音）
  | "prestige" // 飞升（宏大扫频）
  | "achievement" // 成就解锁（亮丽琶音）
  | "uiHover" // 界面悬停（极轻）
  | "uiClick"; // 界面点击（轻确认）

const COLOR_FREQ: Record<string, number> = {
  emerald: 523.25, // C5
  rose: 587.33, // D5
  amber: 659.25, // E5
  fuchsia: 698.46, // F5
};

class AudioEngine {
  private ctx: AudioContext | null = null;
  private master: GainNode | null = null;
  private enabled = true;
  private volume = 0.35;

  /** 首次用户交互后初始化（浏览器自动播放策略） */
  private ensure() {
    if (typeof window === "undefined") return null;
    if (!this.ctx) {
      const AC =
        window.AudioContext ||
        (window as unknown as { webkitAudioContext: typeof AudioContext })
          .webkitAudioContext;
      if (!AC) return null;
      this.ctx = new AC();
      this.master = this.ctx.createGain();
      this.master.gain.value = this.volume;
      this.master.connect(this.ctx.destination);
    }
    if (this.ctx.state === "suspended") {
      void this.ctx.resume();
    }
    return this.ctx;
  }

  setEnabled(v: boolean) {
    this.enabled = v;
  }

  setVolume(v: number) {
    this.volume = Math.max(0, Math.min(1, v));
    if (this.master) this.master.gain.value = this.volume;
  }

  /** 简易正弦音 + ADSR 包络 */
  private tone(
    freq: number,
    dur: number,
    type: OscillatorType = "sine",
    gain = 0.3,
    delay = 0,
    detune = 0,
    filterFreq?: number
  ) {
    const ctx = this.ensure();
    if (!ctx || !this.master) return;
    const t0 = ctx.currentTime + delay;
    const osc = ctx.createOscillator();
    const g = ctx.createGain();
    osc.type = type;
    osc.frequency.setValueAtTime(freq, t0);
    if (detune) osc.detune.setValueAtTime(detune, t0);
    // ADSR
    g.gain.setValueAtTime(0, t0);
    g.gain.linearRampToValueAtTime(gain, t0 + 0.008);
    g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
    let node: AudioNode = osc;
    if (filterFreq) {
      const f = ctx.createBiquadFilter();
      f.type = "lowpass";
      f.frequency.value = filterFreq;
      osc.connect(f);
      f.connect(g);
    } else {
      osc.connect(g);
    }
    g.connect(this.master);
    osc.start(t0);
    osc.stop(t0 + dur + 0.05);
    void node;
  }

  /** 频率扫描音（飞升/出发用） */
  private sweep(
    fStart: number,
    fEnd: number,
    dur: number,
    type: OscillatorType = "sine",
    gain = 0.3,
    delay = 0
  ) {
    const ctx = this.ensure();
    if (!ctx || !this.master) return;
    const t0 = ctx.currentTime + delay;
    const osc = ctx.createOscillator();
    const g = ctx.createGain();
    osc.type = type;
    osc.frequency.setValueAtTime(fStart, t0);
    osc.frequency.exponentialRampToValueAtTime(Math.max(1, fEnd), t0 + dur);
    g.gain.setValueAtTime(0, t0);
    g.gain.linearRampToValueAtTime(gain, t0 + 0.02);
    g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
    osc.connect(g);
    g.connect(this.master);
    osc.start(t0);
    osc.stop(t0 + dur + 0.05);
  }

  /** 噪声脉冲（解码失败/探险打击） */
  private noise(dur: number, gain = 0.2, delay = 0, filterFreq = 1200) {
    const ctx = this.ensure();
    if (!ctx || !this.master) return;
    const t0 = ctx.currentTime + delay;
    const len = Math.floor(ctx.sampleRate * dur);
    const buf = ctx.createBuffer(1, len, ctx.sampleRate);
    const data = buf.getChannelData(0);
    for (let i = 0; i < len; i++) {
      data[i] = (Math.random() * 2 - 1) * (1 - i / len);
    }
    const src = ctx.createBufferSource();
    src.buffer = buf;
    const f = ctx.createBiquadFilter();
    f.type = "lowpass";
    f.frequency.value = filterFreq;
    const g = ctx.createGain();
    g.gain.setValueAtTime(gain, t0);
    g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
    src.connect(f);
    f.connect(g);
    g.connect(this.master);
    src.start(t0);
    src.stop(t0 + dur + 0.02);
  }

  play(name: SfxName, opts?: { color?: string; combo?: number }) {
    if (!this.enabled) return;
    const ctx = this.ensure();
    if (!ctx) return;

    switch (name) {
      case "pulse": {
        // 上升短音 + 轻泛音
        const base = 220 + (opts?.combo ? opts.combo * 18 : 0);
        this.tone(base, 0.18, "sine", 0.28);
        this.tone(base * 2, 0.14, "triangle", 0.1, 0.01);
        break;
      }
      case "pulseCombo": {
        const base = 440 + (opts?.combo ? opts.combo * 24 : 0);
        this.tone(base, 0.2, "sine", 0.3);
        this.tone(base * 1.5, 0.18, "triangle", 0.14, 0.01);
        this.tone(base * 2, 0.16, "sine", 0.08, 0.02);
        break;
      }
      case "decodeClick": {
        const f = opts?.color ? COLOR_FREQ[opts.color] ?? 523 : 523;
        this.tone(f, 0.16, "sine", 0.26);
        this.tone(f * 2, 0.1, "triangle", 0.08, 0.005);
        break;
      }
      case "decodeFail": {
        this.tone(160, 0.16, "sawtooth", 0.18, 0, 0, 800);
        this.noise(0.08, 0.1, 0, 600);
        break;
      }
      case "decodeSuccess": {
        // 上扬大三和弦琶音
        this.tone(523.25, 0.22, "sine", 0.24, 0);
        this.tone(659.25, 0.22, "sine", 0.22, 0.08);
        this.tone(783.99, 0.3, "sine", 0.24, 0.16);
        this.tone(1046.5, 0.34, "triangle", 0.12, 0.2);
        break;
      }
      case "fragmentUnlock": {
        // 空灵长音
        this.tone(880, 0.7, "sine", 0.18, 0);
        this.tone(1108.73, 0.7, "sine", 0.12, 0.04);
        this.tone(1318.51, 0.8, "triangle", 0.08, 0.1);
        this.sweep(440, 880, 0.6, "sine", 0.1, 0.05);
        break;
      }
      case "techBuy": {
        this.tone(587.33, 0.12, "sine", 0.22);
        this.tone(880, 0.16, "triangle", 0.16, 0.06);
        break;
      }
      case "expeditionStart": {
        // 引擎启动：低频上升
        this.sweep(80, 240, 0.5, "sawtooth", 0.16);
        this.sweep(120, 360, 0.5, "square", 0.06, 0.02);
        this.noise(0.4, 0.08, 0, 400);
        break;
      }
      case "expeditionNode": {
        this.tone(440, 0.14, "triangle", 0.2);
        this.tone(660, 0.12, "sine", 0.1, 0.04);
        break;
      }
      case "expeditionVictory": {
        // 凯旋上行
        this.tone(523.25, 0.18, "sine", 0.24, 0);
        this.tone(659.25, 0.18, "sine", 0.24, 0.1);
        this.tone(783.99, 0.18, "sine", 0.24, 0.2);
        this.tone(1046.5, 0.4, "triangle", 0.2, 0.3);
        break;
      }
      case "expeditionDefeat": {
        // 下行低音
        this.tone(330, 0.3, "sawtooth", 0.2, 0, 0, 700);
        this.tone(220, 0.4, "sine", 0.18, 0.12);
        this.tone(146.83, 0.5, "sine", 0.16, 0.24);
        break;
      }
      case "prestige": {
        // 宏大扫频 + 和弦
        this.sweep(110, 880, 1.2, "sine", 0.2);
        this.sweep(220, 1760, 1.2, "triangle", 0.1, 0.05);
        this.tone(523.25, 0.6, "sine", 0.16, 0.3);
        this.tone(659.25, 0.6, "sine", 0.16, 0.42);
        this.tone(783.99, 0.8, "sine", 0.16, 0.54);
        this.tone(1046.5, 1.0, "triangle", 0.12, 0.66);
        break;
      }
      case "achievement": {
        // 亮丽琶音
        this.tone(659.25, 0.16, "sine", 0.22, 0);
        this.tone(880, 0.16, "sine", 0.22, 0.08);
        this.tone(1046.5, 0.16, "sine", 0.22, 0.16);
        this.tone(1318.51, 0.4, "triangle", 0.18, 0.24);
        break;
      }
      case "uiHover": {
        this.tone(880, 0.05, "sine", 0.05);
        break;
      }
      case "uiClick": {
        this.tone(660, 0.07, "sine", 0.1);
        break;
      }
    }
  }
}

/** 全局单例（客户端） */
let _engine: AudioEngine | null = null;
export function getAudio(): AudioEngine {
  if (!_engine) _engine = new AudioEngine();
  return _engine;
}

export type { SfxName };
