// audio.jsx — Web Speech API helpers for Mandarin TTS.
// iOS Safari ships zh-CN voices since iOS 13. We pick the best available.

let _voicesCache = null;
function _voices() {
  if (_voicesCache) return _voicesCache;
  const v = (window.speechSynthesis?.getVoices?.() || []);
  if (v.length) _voicesCache = v;
  return v;
}

function _pickZhVoice() {
  const all = _voices();
  if (!all.length) return null;
  // Prefer named Apple voices, then any zh-CN, then any zh-*.
  const named = ['Tingting', 'Mei-Jia', 'Sin-ji', 'Yu-shu', 'Li-mu'];
  for (const n of named) {
    const m = all.find((v) => v.name?.includes(n));
    if (m) return m;
  }
  return all.find((v) => v.lang === 'zh-CN')
      || all.find((v) => v.lang?.startsWith('zh'))
      || null;
}

// One-shot speak. Cancels any prior utterance.
function speakZh(text, opts = {}) {
  if (!window.speechSynthesis) return;
  try {
    window.speechSynthesis.cancel();
    const u = new SpeechSynthesisUtterance(text);
    u.lang = 'zh-CN';
    u.rate = opts.rate ?? 0.85;     // slightly slow for learners
    u.pitch = opts.pitch ?? 1;
    u.volume = opts.volume ?? 1;
    const v = _pickZhVoice();
    if (v) u.voice = v;
    if (opts.onend) u.onend = opts.onend;
    if (opts.onerror) u.onerror = opts.onerror;
    window.speechSynthesis.speak(u);
  } catch {}
}

// Prime the synth — must run inside a user gesture on iOS or no audio.
function primeSpeech() {
  if (!window.speechSynthesis) return;
  try {
    const u = new SpeechSynthesisUtterance(' ');
    u.volume = 0;
    window.speechSynthesis.speak(u);
  } catch {}
}

// Listen for voices changing (Chrome loads async).
if (window.speechSynthesis) {
  window.speechSynthesis.onvoiceschanged = () => { _voicesCache = null; _voices(); };
}

// Tiny click/blip via WebAudio. Used for UI feedback when sound is on.
let _ctx = null;
function _ensureCtx() {
  if (_ctx) return _ctx;
  try { _ctx = new (window.AudioContext || window.webkitAudioContext)(); } catch { _ctx = null; }
  return _ctx;
}
function blip(kind = 'tap') {
  const ctx = _ensureCtx();
  if (!ctx) return;
  const now = ctx.currentTime;
  const o = ctx.createOscillator();
  const g = ctx.createGain();
  o.connect(g); g.connect(ctx.destination);
  if (kind === 'right') {
    o.type = 'triangle';
    o.frequency.setValueAtTime(660, now);
    o.frequency.exponentialRampToValueAtTime(880, now + 0.12);
    g.gain.setValueAtTime(0.0001, now);
    g.gain.exponentialRampToValueAtTime(0.18, now + 0.01);
    g.gain.exponentialRampToValueAtTime(0.0001, now + 0.18);
    o.start(now); o.stop(now + 0.2);
  } else if (kind === 'wrong') {
    o.type = 'square';
    o.frequency.setValueAtTime(220, now);
    o.frequency.exponentialRampToValueAtTime(140, now + 0.15);
    g.gain.setValueAtTime(0.0001, now);
    g.gain.exponentialRampToValueAtTime(0.12, now + 0.01);
    g.gain.exponentialRampToValueAtTime(0.0001, now + 0.2);
    o.start(now); o.stop(now + 0.22);
  } else {
    o.type = 'sine';
    o.frequency.setValueAtTime(520, now);
    g.gain.setValueAtTime(0.0001, now);
    g.gain.exponentialRampToValueAtTime(0.08, now + 0.005);
    g.gain.exponentialRampToValueAtTime(0.0001, now + 0.06);
    o.start(now); o.stop(now + 0.07);
  }
}

function haptic(kind = 'light') {
  if (!('vibrate' in navigator)) return;
  if (kind === 'right') navigator.vibrate(15);
  else if (kind === 'wrong') navigator.vibrate([20, 30, 20]);
  else navigator.vibrate(8);
}

Object.assign(window, { speakZh, primeSpeech, blip, haptic });
