/**
 * public/voice-ui.js — Browser-side voice interface.
 *
 * Provides a mic button for chat input with Web Speech API fallback
 * when not running in Electron desktop mode. In desktop mode, delegates
 * to the native voice activation via IPC bridge.
 *
 * ES module loaded from index.html.
 */

/**
 * Initialize voice UI controls.
 *
 * @param {object} opts
 * @param {function} opts.apiFetch Authenticated fetch wrapper.
 * @param {HTMLElement} opts.micButton The microphone button element.
 * @param {function} opts.onTranscription Called with (text) when speech is transcribed.
 * @returns {{ destroy: function, setVoiceState: function }}
 */
export function init({ apiFetch, micButton, onTranscription }) {
  if (!micButton) return { destroy: () => {}, setVoiceState: () => {} };

  let recognition = null;
  let isListening = false;
  let mediaStream = null;

  const hasDesktopBridge = !!(window.desktopBridge && window.desktopBridge.voiceListen);
  const hasSpeechAPI = !!(window.SpeechRecognition || window.webkitSpeechRecognition);

  // ── State rendering ─────────────────────────────────────────

  function updateButton(state) {
    micButton.classList.remove('listening', 'transcribing', 'error');
    micButton.title = 'Voice input';

    switch (state) {
      case 'listening':
        micButton.classList.add('listening');
        micButton.title = 'Listening... (click to stop)';
        break;
      case 'transcribing':
        micButton.classList.add('transcribing');
        micButton.title = 'Transcribing...';
        break;
      case 'error':
        micButton.classList.add('error');
        micButton.title = 'Voice error (click to retry)';
        setTimeout(() => micButton.classList.remove('error'), 2000);
        break;
    }
  }

  // ── Web Speech API (browser fallback) ───────────────────────

  function startWebSpeechRecognition() {
    const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SpeechRecognition) {
      console.warn('[voice-ui] Web Speech API not available');
      updateButton('error');
      return;
    }

    recognition = new SpeechRecognition();
    recognition.continuous = false;
    recognition.interimResults = false;
    recognition.lang = 'en-US';

    recognition.onstart = () => {
      isListening = true;
      updateButton('listening');
    };

    recognition.onresult = (event) => {
      updateButton('transcribing');
      const transcript = event.results[0]?.[0]?.transcript;
      if (transcript && transcript.trim()) {
        if (onTranscription) onTranscription(transcript.trim());
      }
      isListening = false;
      updateButton('idle');
    };

    recognition.onerror = (event) => {
      console.warn(`[voice-ui] Speech recognition error: ${event.error}`);
      isListening = false;
      if (event.error !== 'aborted') updateButton('error');
    };

    recognition.onend = () => {
      isListening = false;
      updateButton('idle');
    };

    recognition.start();
  }

  function stopWebSpeechRecognition() {
    if (recognition) {
      try { recognition.abort(); } catch { /* ignore */ }
      recognition = null;
    }
    isListening = false;
    updateButton('idle');
  }

  // ── Desktop mode (delegate to native) ───────────────────────

  async function triggerDesktopListen() {
    try {
      updateButton('listening');
      await window.desktopBridge.voiceListen();
      // State will be updated via WebSocket broadcast
    } catch (err) {
      console.error(`[voice-ui] Desktop listen failed: ${err.message}`);
      updateButton('error');
    }
  }

  async function triggerServerListen() {
    try {
      updateButton('listening');
      await apiFetch('/api/voice/listen', { method: 'POST' });
      // State will be updated via WebSocket broadcast
    } catch (err) {
      console.error(`[voice-ui] Server listen failed: ${err.message}`);
      // Fall back to Web Speech API if server listen fails
      if (hasSpeechAPI) {
        startWebSpeechRecognition();
      } else {
        updateButton('error');
      }
    }
  }

  // ── Click handler ───────────────────────────────────────────

  function handleClick() {
    if (isListening) {
      stopWebSpeechRecognition();
      return;
    }

    // Priority: Desktop bridge > Server API > Web Speech API
    if (hasDesktopBridge) {
      triggerDesktopListen();
    } else {
      // Try server-side voice first (it uses native wake word engine)
      triggerServerListen();
    }
  }

  micButton.addEventListener('click', handleClick);

  // ── External state updates (from WebSocket) ─────────────────

  function setVoiceState(state) {
    switch (state) {
      case 'voice_detecting':
        updateButton('idle');
        break;
      case 'voice_listening':
        updateButton('listening');
        break;
      case 'voice_transcribing':
        updateButton('transcribing');
        break;
      case 'voice_idle':
        updateButton('idle');
        break;
      case 'voice_message':
        updateButton('idle');
        break;
      default:
        // Don't change state for unrelated events
        break;
    }
  }

  function destroy() {
    micButton.removeEventListener('click', handleClick);
    stopWebSpeechRecognition();
    if (mediaStream) {
      mediaStream.getTracks().forEach(t => t.stop());
      mediaStream = null;
    }
  }

  return { destroy, setVoiceState };
}
