"use client";

import type { ConsoleUiKind } from "./consoleThemes";

let ctx: AudioContext | null = null;
let muted = false;

const sampleCache = new Map<string, HTMLAudioElement>();

function ac(): AudioContext | null {
  if (typeof window === "undefined") return null;
  const AC =
    window.AudioContext ||
    (window as unknown as { webkitAudioContext: typeof AudioContext })
      .webkitAudioContext;
  if (!AC) return null;
  if (!ctx) ctx = new AC();
  if (ctx.state === "suspended") void ctx.resume();
  return ctx;
}

export function setConsoleUiMuted(next: boolean) {
  muted = next;
}

export function isConsoleUiMuted() {
  return muted;
}

function beep(
  frequency: number,
  {
    type = "sine" as OscillatorType,
    duration = 0.07,
    gain = 0.06,
    slide,
    delay = 0,
  }: {
    type?: OscillatorType;
    duration?: number;
    gain?: number;
    slide?: number;
    delay?: number;
  } = {},
) {
  if (muted) return;
  const c = ac();
  if (!c) return;
  const t0 = c.currentTime + delay;
  const o = c.createOscillator();
  const g = c.createGain();
  o.type = type;
  o.frequency.setValueAtTime(frequency, t0);
  if (slide != null) {
    o.frequency.exponentialRampToValueAtTime(Math.max(40, slide), t0 + duration);
  }
  g.gain.setValueAtTime(0.0001, t0);
  g.gain.exponentialRampToValueAtTime(gain, t0 + 0.008);
  g.gain.exponentialRampToValueAtTime(0.0001, t0 + duration);
  o.connect(g);
  g.connect(c.destination);
  o.start(t0);
  o.stop(t0 + duration + 0.02);
}

function noise(duration = 0.05, gain = 0.03) {
  if (muted) return;
  const c = ac();
  if (!c) return;
  const n = Math.floor(c.sampleRate * duration);
  const buf = c.createBuffer(1, n, c.sampleRate);
  const data = buf.getChannelData(0);
  for (let i = 0; i < n; i++) data[i] = (Math.random() * 2 - 1) * (1 - i / n);
  const src = c.createBufferSource();
  const g = c.createGain();
  const f = c.createBiquadFilter();
  f.type = "highpass";
  f.frequency.value = 800;
  src.buffer = buf;
  g.gain.value = gain;
  src.connect(f);
  f.connect(g);
  g.connect(c.destination);
  src.start();
}

export type UiSound = "boot" | "move" | "confirm" | "back" | "error";

/** Play a short UI sample from /public/sfx/ui (no looping ambience). */
function playSample(path: string, volume = 0.45) {
  if (muted || typeof window === "undefined") return;
  let base = sampleCache.get(path);
  if (!base) {
    base = new Audio(path);
    base.preload = "auto";
    sampleCache.set(path, base);
  }
  const node = base.cloneNode(true) as HTMLAudioElement;
  node.volume = volume;
  void node.play().catch(() => {
    /* autoplay / missing file — ignore */
  });
}

const PS3_SAMPLES: Record<UiSound, { src: string; vol: number } | null> = {
  boot: { src: "/sfx/ui/xmb-boot.mp3", vol: 0.5 },
  move: { src: "/sfx/ui/xmb-move-alt.mp3", vol: 0.38 },
  confirm: { src: "/sfx/ui/xmb-confirm.mp3", vol: 0.5 },
  back: { src: "/sfx/ui/xmb-back.mp3", vol: 0.45 },
  error: null,
};

const PSP_SAMPLES: Record<UiSound, { src: string; vol: number } | null> = {
  boot: { src: "/sfx/ui/psp-boot.mp3", vol: 0.45 },
  move: { src: "/sfx/ui/psp-move.mp3", vol: 0.4 },
  confirm: { src: "/sfx/ui/psp-confirm.mp3", vol: 0.5 },
  back: { src: "/sfx/ui/xmb-back.mp3", vol: 0.4 },
  error: null,
};

export function playUi(kind: ConsoleUiKind, sound: UiSound) {
  if (kind === "ps3-xmb") {
    const s = PS3_SAMPLES[sound];
    if (s) {
      playSample(s.src, s.vol);
      return;
    }
    beep(180, { type: "square", duration: 0.1, gain: 0.035 });
    return;
  }

  if (kind === "psp-xmb") {
    const s = PSP_SAMPLES[sound];
    if (s) {
      playSample(s.src, s.vol);
      return;
    }
    beep(180, { type: "square", duration: 0.1, gain: 0.03 });
    return;
  }

  switch (kind) {
    case "nes":
    case "snes":
    case "n64":
      if (sound === "boot") {
        noise(0.08, 0.04);
        beep(120, { type: "square", duration: 0.12, gain: 0.05, slide: 80 });
      } else if (sound === "move") {
        beep(440, { type: "square", duration: 0.04, gain: 0.035 });
      } else if (sound === "confirm") {
        beep(660, { type: "square", duration: 0.06, gain: 0.04 });
        beep(880, { type: "square", duration: 0.08, gain: 0.035, delay: 0.05 });
      } else {
        beep(220, { type: "square", duration: 0.06, gain: 0.03 });
      }
      break;

    case "gb":
    case "gbc":
    case "gba":
    case "nds":
    case "gg":
      if (sound === "boot") {
        beep(870, { type: "square", duration: 0.08, gain: 0.04 });
        beep(870, { type: "square", duration: 0.08, gain: 0.035, delay: 0.12 });
      } else if (sound === "move") {
        beep(1100, { type: "square", duration: 0.03, gain: 0.03 });
      } else if (sound === "confirm") {
        beep(1318, { type: "square", duration: 0.05, gain: 0.035 });
      } else {
        beep(400, { type: "square", duration: 0.05, gain: 0.025 });
      }
      break;

    case "genesis":
    case "sms":
    case "saturn":
    case "segacd":
      if (sound === "boot") {
        beep(196, { type: "sawtooth", duration: 0.15, gain: 0.035, slide: 392 });
      } else if (sound === "move") {
        beep(520, { type: "sawtooth", duration: 0.04, gain: 0.03 });
      } else if (sound === "confirm") {
        beep(784, { type: "sawtooth", duration: 0.09, gain: 0.035 });
      } else {
        beep(260, { type: "sawtooth", duration: 0.06, gain: 0.025 });
      }
      break;

    case "arcade":
      if (sound === "boot") {
        beep(1400, { type: "square", duration: 0.06, gain: 0.04, slide: 900 });
        beep(1800, { type: "square", duration: 0.07, gain: 0.035, delay: 0.08 });
      } else if (sound === "move") {
        beep(900, { type: "square", duration: 0.03, gain: 0.03 });
      } else if (sound === "confirm") {
        beep(440, { type: "square", duration: 0.08, gain: 0.045 });
        beep(660, { type: "square", duration: 0.1, gain: 0.04, delay: 0.07 });
      } else {
        noise(0.04, 0.03);
      }
      break;

    case "atari2600":
      if (sound === "boot") noise(0.1, 0.04);
      else if (sound === "move") beep(300, { type: "square", duration: 0.04, gain: 0.03 });
      else beep(500, { type: "square", duration: 0.06, gain: 0.03 });
      break;

    case "vb":
      if (sound === "boot") {
        beep(200, { type: "sawtooth", duration: 0.2, gain: 0.04, slide: 100 });
      } else if (sound === "move") {
        beep(700, { type: "sawtooth", duration: 0.04, gain: 0.03 });
      } else {
        beep(900, { type: "sawtooth", duration: 0.08, gain: 0.03 });
      }
      break;
  }
}
