// One-shot call sound effects via WebAudio. Generated on the fly so no
// binary assets needed. Intentionally short + low-volume — these fire
// multiple times per call and shouldn't feel intrusive.

type Sfx = 'join' | 'leave' | 'end' | 'mute' | 'unmute' | 'deafen' | 'undeafen';

let ctx: AudioContext | null = null;

function getCtx(): AudioContext | null {
  if (ctx) return ctx;
  const AudioCtx =
    window.AudioContext ??
    (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
  if (!AudioCtx) return null;
  ctx = new AudioCtx();
  return ctx;
}

function beep(freq: number, durationSec: number, delaySec: number, gain = 0.15): void {
  const c = getCtx();
  if (!c) return;
  const osc = c.createOscillator();
  const g = c.createGain();
  osc.type = 'sine';
  osc.frequency.value = freq;
  osc.connect(g);
  g.connect(c.destination);
  const t0 = c.currentTime + delaySec;
  g.gain.setValueAtTime(0, t0);
  g.gain.linearRampToValueAtTime(gain, t0 + 0.02);
  g.gain.exponentialRampToValueAtTime(0.001, t0 + durationSec);
  osc.start(t0);
  osc.stop(t0 + durationSec + 0.02);
}

export async function playSfx(kind: Sfx): Promise<void> {
  const c = getCtx();
  if (!c) return;
  if (c.state === 'suspended') {
    try {
      await c.resume();
    } catch {
      /* ignore */
    }
  }
  switch (kind) {
    case 'join':
      // Rising two-note chirp — someone entered.
      beep(523.25, 0.12, 0, 0.16); // C5
      beep(783.99, 0.18, 0.1, 0.16); // G5
      break;
    case 'leave':
      // Falling two-note — someone left.
      beep(659.25, 0.12, 0, 0.14); // E5
      beep(329.63, 0.18, 0.1, 0.14); // E4
      break;
    case 'end':
      // Soft descending thud — call ended.
      beep(440, 0.18, 0, 0.14); // A4
      beep(293.66, 0.26, 0.14, 0.14); // D4
      break;
    case 'mute':
      // Discord-style: short downward blip when mic goes silent. Quick, low
      // volume so it doesn't fight whatever the user is listening to.
      beep(880, 0.06, 0, 0.1); // A5
      beep(660, 0.08, 0.04, 0.1); // E5
      break;
    case 'unmute':
      // Mirror: upward blip when mic comes back.
      beep(660, 0.06, 0, 0.1);
      beep(880, 0.08, 0.04, 0.1);
      break;
    case 'deafen':
      // Lower + slightly longer than mute — Discord uses a deeper tone for
      // deafen so the user can tell the two states apart without looking.
      beep(660, 0.07, 0, 0.1);
      beep(392, 0.12, 0.05, 0.1); // G4
      break;
    case 'undeafen':
      // Mirror of deafen: low → mid.
      beep(392, 0.07, 0, 0.1);
      beep(660, 0.12, 0.05, 0.1);
      break;
  }
}

export function playJoinBeep(): Promise<void> {
  return playSfx('join');
}
export function playLeaveBeep(): Promise<void> {
  return playSfx('leave');
}
export function playEndBeep(): Promise<void> {
  return playSfx('end');
}
export function playMuteBeep(): Promise<void> {
  return playSfx('mute');
}
export function playUnmuteBeep(): Promise<void> {
  return playSfx('unmute');
}
export function playDeafenBeep(): Promise<void> {
  return playSfx('deafen');
}
export function playUndeafenBeep(): Promise<void> {
  return playSfx('undeafen');
}
