// ---- VOICE DICTATE (Whisper + Web Speech fallback) ----
const { useState: useVoiceState, useRef: useVoiceRef, useEffect: useVoiceEffect } = React;

function VoiceDictateButton({ onTranscript, language = 'es' }) {
  const [status, setStatus] = useVoiceState('idle'); // 'idle' | 'recording' | 'transcribing' | 'error'
  const [error, setError] = useVoiceState(null);
  const [elapsed, setElapsed] = useVoiceState(0);
  const mediaRecorderRef = useVoiceRef(null);
  const chunksRef = useVoiceRef([]);
  const streamRef = useVoiceRef(null);
  const timerRef = useVoiceRef(null);
  const startTimeRef = useVoiceRef(0);

  useVoiceEffect(() => {
    return () => {
      if (streamRef.current) streamRef.current.getTracks().forEach(t => t.stop());
      if (timerRef.current) clearInterval(timerRef.current);
    };
  }, []);

  async function startRecording() {
    setError(null);
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      streamRef.current = stream;
      const mimeType = MediaRecorder.isTypeSupported('audio/webm') ? 'audio/webm' : '';
      const rec = new MediaRecorder(stream, mimeType ? { mimeType } : undefined);
      mediaRecorderRef.current = rec;
      chunksRef.current = [];
      rec.ondataavailable = (e) => { if (e.data.size > 0) chunksRef.current.push(e.data); };
      rec.onstop = handleStop;
      rec.start();
      startTimeRef.current = Date.now();
      setElapsed(0);
      timerRef.current = setInterval(() => {
        setElapsed(Math.floor((Date.now() - startTimeRef.current) / 1000));
      }, 250);
      setStatus('recording');
    } catch (err) {
      console.warn('mic denied', err);
      setError('No se pudo acceder al micrófono. Revisa los permisos del navegador.');
      setStatus('error');
    }
  }

  function stopRecording() {
    if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {
      mediaRecorderRef.current.stop();
    }
    if (streamRef.current) streamRef.current.getTracks().forEach(t => t.stop());
    if (timerRef.current) clearInterval(timerRef.current);
  }

  async function handleStop() {
    setStatus('transcribing');
    const blob = new Blob(chunksRef.current, { type: 'audio/webm' });
    chunksRef.current = [];

    // If too short (< 0.5s), abort
    if (blob.size < 1000) {
      setStatus('idle');
      return;
    }

    try {
      const text = await transcribeWithWhisper(blob, language);
      if (text) {
        onTranscript(text);
        setStatus('idle');
        return;
      }
      throw new Error('empty transcript');
    } catch (err) {
      console.warn('Whisper failed, no fallback for already-recorded audio:', err);
      setError('No se pudo transcribir. Intenta de nuevo.');
      setStatus('error');
      setTimeout(() => { setStatus('idle'); setError(null); }, 2500);
    }
  }

  const toggle = () => {
    if (status === 'recording') stopRecording();
    else if (status === 'idle') startRecording();
  };

  const label = {
    idle: 'Dictar',
    recording: `Detener · ${elapsed}s`,
    transcribing: 'Transcribiendo…',
    error: 'Error',
  }[status];

  return (
    <div style={{display: 'inline-flex', alignItems: 'center', gap: 8}}>
      <button
        type="button"
        onClick={toggle}
        disabled={status === 'transcribing'}
        className="voice-btn"
        data-status={status}
        title={error || (status === 'recording' ? 'Click para detener' : 'Dictar con voz')}
      >
        <span className="voice-dot" />
        {status === 'recording' ? (
          <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
            <rect x="3" y="3" width="6" height="6" rx="1" fill="currentColor"/>
          </svg>
        ) : status === 'transcribing' ? (
          <svg width="12" height="12" viewBox="0 0 12 12" fill="none" className="spin">
            <path d="M10.5 6a4.5 4.5 0 1 1-1.32-3.18" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
          </svg>
        ) : (
          <svg width="12" height="14" viewBox="0 0 12 14" fill="none">
            <path d="M6 1.5a2 2 0 0 0-2 2v3a2 2 0 0 0 4 0v-3a2 2 0 0 0-2-2Z" stroke="currentColor" strokeWidth="1.4"/>
            <path d="M2 6.5a4 4 0 0 0 8 0M6 10.5v2M4 12.5h4" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/>
          </svg>
        )}
        {label}
      </button>
      {error && <span style={{fontSize: 11, color: 'var(--muted)'}}>{error}</span>}
    </div>
  );
}

async function transcribeWithWhisper(audioBlob, language = 'es') {
  // 1) Proxy del backend (producción): la key vive en el server.
  try {
    const res = await fetch(`/api/transcribe?language=${encodeURIComponent(language)}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/octet-stream' },
      body: audioBlob,
    });
    if (res.ok) {
      const j = await res.json();
      if (j.ok && j.text) return j.text.trim();
    } else if (res.status !== 404) {
      // El proxy existe pero falló (429/502/…): no tiene sentido el fallback
      // directo salvo que haya key local.
      console.warn('transcribe proxy HTTP', res.status);
    }
  } catch (err) {
    console.warn('transcribe proxy error:', err?.message || err);
  }

  // 2) Fallback directo a OpenAI SOLO para dev local con secrets.local.js.
  const key = window.__PROTOTYPE_SECRETS?.OPENAI_KEY;
  if (!key) throw new Error('transcribe proxy failed and no local api key');

  const form = new FormData();
  // Whisper expects a file; give it an extension it likes.
  form.append('file', new File([audioBlob], 'audio.webm', { type: 'audio/webm' }));
  form.append('model', 'whisper-1');
  if (language) form.append('language', language);
  form.append('response_format', 'json');

  const res = await fetch('https://api.openai.com/v1/audio/transcriptions', {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${key}` },
    body: form,
  });
  if (!res.ok) {
    const errTxt = await res.text().catch(() => '');
    throw new Error(`whisper ${res.status}: ${errTxt}`);
  }
  const data = await res.json();
  return (data.text || '').trim();
}

Object.assign(window, { VoiceDictateButton, transcribeWithWhisper });
