export type CallSoundStyle = 'chime' | 'soft' | 'retro';

let sharedCtx: AudioContext | undefined;

const getAudioContext = (): AudioContext | undefined => {
  try {
    if (!sharedCtx || sharedCtx.state === 'closed') sharedCtx = new AudioContext();
    if (sharedCtx.state === 'suspended') sharedCtx.resume().catch(() => undefined);
    return sharedCtx;
  } catch {
    return undefined;
  }
};

/**
 * Create and resume the shared AudioContext from within a user gesture
 * (e.g. clicking "Join"). Browsers block AudioContext playback until it has
 * been started by a gesture, so join/leave sounds — which fire later without
 * any gesture — would otherwise be silent. Call this on call entry so every
 * participant's later membership-change sounds are actually audible.
 */
export const unlockCallSounds = (): void => {
  getAudioContext();
};

type Note = {
  freq: number;
  /** Offset from now, in seconds */
  at: number;
  /** Duration in seconds */
  dur: number;
};

const playNotes = (notes: Note[], type: OscillatorType, peakGain: number): void => {
  const ctx = getAudioContext();
  if (!ctx) return;
  const now = ctx.currentTime;
  notes.forEach(({ freq, at, dur }) => {
    const osc = ctx.createOscillator();
    const gain = ctx.createGain();
    osc.type = type;
    osc.frequency.value = freq;
    const start = now + at;
    // Short attack/decay envelope to avoid clicks
    gain.gain.setValueAtTime(0, start);
    gain.gain.linearRampToValueAtTime(peakGain, start + 0.015);
    gain.gain.exponentialRampToValueAtTime(0.0001, start + dur);
    osc.connect(gain);
    gain.connect(ctx.destination);
    osc.start(start);
    osc.stop(start + dur + 0.02);
  });
};

const SOUNDS: Record<CallSoundStyle, { join: () => void; leave: () => void }> = {
  chime: {
    join: () =>
      playNotes(
        [
          { freq: 587.33, at: 0, dur: 0.12 },
          { freq: 880, at: 0.1, dur: 0.2 },
        ],
        'sine',
        0.25,
      ),
    leave: () =>
      playNotes(
        [
          { freq: 880, at: 0, dur: 0.12 },
          { freq: 587.33, at: 0.1, dur: 0.2 },
        ],
        'sine',
        0.25,
      ),
  },
  soft: {
    join: () => playNotes([{ freq: 523.25, at: 0, dur: 0.4 }], 'triangle', 0.18),
    leave: () => playNotes([{ freq: 392, at: 0, dur: 0.4 }], 'triangle', 0.18),
  },
  retro: {
    join: () =>
      playNotes(
        [
          { freq: 440, at: 0, dur: 0.07 },
          { freq: 554.37, at: 0.07, dur: 0.07 },
          { freq: 659.25, at: 0.14, dur: 0.14 },
        ],
        'square',
        0.1,
      ),
    leave: () =>
      playNotes(
        [
          { freq: 659.25, at: 0, dur: 0.07 },
          { freq: 554.37, at: 0.07, dur: 0.07 },
          { freq: 440, at: 0.14, dur: 0.14 },
        ],
        'square',
        0.1,
      ),
  },
};

export const playCallJoinSound = (style: CallSoundStyle): void => SOUNDS[style]?.join();

export const playCallLeaveSound = (style: CallSoundStyle): void => SOUNDS[style]?.leave();
