// voice.jsx - speech-to-text input + optional text-to-speech replies (exported to window)
// Uses the browser Web Speech API. Degrades gracefully where unsupported.

const SpeechRec = window.SpeechRecognition || window.webkitSpeechRecognition;
const voiceSupported = !!SpeechRec;

// ---- Speech recognition (dictation) ----
// useVoiceInput({ locale, onResult, onInterim }) -> { listening, supported, start, stop, toggle }
function useVoiceInput({ locale = "en-NG", onResult, onInterim } = {}) {
  const { useState, useRef, useEffect, useCallback } = React;
  const [listening, setListening] = useState(false);
  const recRef = useRef(null);

  const stop = useCallback(() => {
    try { recRef.current && recRef.current.stop(); } catch (e) {}
    setListening(false);
  }, []);

  const start = useCallback(() => {
    if (!voiceSupported) return false;
    try {
      const rec = new SpeechRec();
      rec.lang = locale;
      rec.interimResults = true;
      rec.continuous = false;
      rec.maxAlternatives = 1;
      let finalText = "";
      rec.onresult = (e) => {
        let interim = "";
        for (let i = e.resultIndex; i < e.results.length; i++) {
          const r = e.results[i];
          if (r.isFinal) finalText += r[0].transcript;
          else interim += r[0].transcript;
        }
        if (interim && onInterim) onInterim(interim);
        if (finalText && onResult) onResult(finalText.trim());
      };
      rec.onerror = () => setListening(false);
      rec.onend = () => setListening(false);
      recRef.current = rec;
      rec.start();
      setListening(true);
      return true;
    } catch (e) { setListening(false); return false; }
  }, [locale, onResult, onInterim]);

  const toggle = useCallback(() => { listening ? stop() : start(); }, [listening, start, stop]);

  useEffect(() => () => stop(), [stop]);
  return { listening, supported: voiceSupported, start, stop, toggle };
}

// ---- Text-to-speech (Cova reads replies aloud) ----
const synth = window.speechSynthesis;
const ttsSupported = !!synth;

function pickVoice(locale) {
  if (!ttsSupported) return null;
  const voices = synth.getVoices() || [];
  const base = locale.split("-")[0];
  return voices.find((v) => v.lang === locale)
    || voices.find((v) => v.lang && v.lang.startsWith(base))
    || voices.find((v) => v.lang && v.lang.startsWith("en"))
    || voices[0] || null;
}

function stripMarkdown(s) {
  return String(s || "").replace(/\*\*/g, "").replace(/[#_`>]/g, "").replace(/\s+/g, " ").trim();
}

// ---- Spoken replies (browser speech synthesis) ----
// The YarnGPT Nigerian-voice proxy has been retired for now; Cova speaks with
// the browser's built-in voice. speak() honours the "off" setting.
function speak(text, locale = "en-NG", onEnd) {
  const clean = stripMarkdown(text);
  if (!clean) { if (onEnd) onEnd(); return; }

  // Voice replies disabled entirely.
  if (window.__vcCovaVoice === "off") { if (onEnd) onEnd(); return; }

  if (!ttsSupported) { if (onEnd) onEnd(); return; }
  try {
    cancelSpeak();
    const u = new SpeechSynthesisUtterance(clean);
    u.lang = locale;
    const v = pickVoice(locale);
    if (v) u.voice = v;
    u.rate = 1.02; u.pitch = 1.0;
    if (onEnd) { u.onend = onEnd; u.onerror = onEnd; }
    synth.speak(u);
  } catch (e) { if (onEnd) onEnd(); }
}

function cancelSpeak() {
  try { ttsSupported && synth.cancel(); } catch (e) {}
}

// warm up voice list (some browsers populate async)
if (ttsSupported) { try { synth.getVoices(); synth.onvoiceschanged = () => {}; } catch (e) {} }

window.VC_VOICE = { useVoiceInput, voiceSupported, ttsSupported, speak, cancelSpeak };

// ---- Reusable dictate-only mic button (for pre-auth capture fields: hero + CTAs) ----
// Pre-auth inputs don't run a live Cova; they stash the sentence and carry it into
// signup. So voice here NEVER auto-submits - it transcribes into the field, live, and
// the user presses Start themselves. Listening only begins on an explicit tap, so the
// browser mic-permission prompt is consent-driven, never a page-load surprise.
function VoiceMicButton({ locale = "en-NG", onTranscript, onListenChange, size = 40, light = false }) {
  const voice = useVoiceInput({
    locale,
    onInterim: (t) => onTranscript && onTranscript(t),
    onResult: (t) => onTranscript && onTranscript(t),
  });
  React.useEffect(() => { onListenChange && onListenChange(voice.listening); }, [voice.listening]);
  if (!voice.supported) return null;
  const on = voice.listening;
  return (
    <button type="button" onClick={voice.toggle} aria-label={on ? "Listening - tap to stop" : "Tap to speak"}
      title={on ? "Listening - tap to stop" : "Tap to speak"}
      style={{
        display: "grid", placeItems: "center", width: size, height: size, borderRadius: 999, flexShrink: 0,
        transition: "all .2s var(--ease)",
        color: on ? "#fff" : (light ? "var(--accent)" : "var(--text-muted)"),
        background: on ? "var(--danger)" : (light ? "rgba(91,61,245,.10)" : "var(--surface-2)"),
        animation: on ? "vc-pulse-ring 1.6s infinite" : "none",
      }}>
      <window.Icon name="mic" size={Math.round(size * 0.48)} />
    </button>
  );
}

window.VoiceMicButton = VoiceMicButton;
