'use client';

import { useCallback, useEffect, useRef, useState } from 'react';

/**
 * Web Speech API wrapper. Browser-only - gracefully reports `supported: false`
 * when SpeechRecognition isn't available (Firefox, Safari < 14.1, server-side
 * render).
 *
 * Surfaces both the interim (still-being-spoken) and final (committed)
 * transcripts so the UI can show "real-time" feedback while typing.
 *
 * Usage:
 *   const { supported, isListening, transcript, interim, start, stop, reset } =
 *     useVoiceTranscription();
 *
 * The summary form appends `transcript` (final committed text) to the textarea
 * and separately persists it to `voiceTranscript` on the server. The rep can
 * still edit the summary freely - the raw transcript stays untouched.
 */
interface SpeechRecognitionEventLike {
  resultIndex: number;
  results: ArrayLike<{
    isFinal: boolean;
    0: { transcript: string };
  }>;
}

interface SpeechRecognitionLike {
  continuous: boolean;
  interimResults: boolean;
  lang: string;
  onresult: ((event: SpeechRecognitionEventLike) => void) | null;
  onerror: ((event: { error: string }) => void) | null;
  onend: (() => void) | null;
  start: () => void;
  stop: () => void;
  abort: () => void;
}

type SpeechRecognitionCtor = new () => SpeechRecognitionLike;

function getSpeechRecognitionCtor(): SpeechRecognitionCtor | null {
  if (typeof window === 'undefined') return null;
  const w = window as unknown as {
    SpeechRecognition?: SpeechRecognitionCtor;
    webkitSpeechRecognition?: SpeechRecognitionCtor;
  };
  return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
}

export interface VoiceTranscriptionApi {
  /** True when the browser exposes Web Speech (or a vendor-prefixed equivalent). */
  supported: boolean;
  /** Currently capturing audio. */
  isListening: boolean;
  /** Final, committed transcript accumulated across the current session. */
  transcript: string;
  /** Latest interim (still being recognized) transcript fragment. */
  interim: string;
  /** Set when the underlying API returns an error (permission denied, no mic, etc.). */
  error: string | null;
  start: () => void;
  stop: () => void;
  reset: () => void;
}

export function useVoiceTranscription(opts?: { lang?: string }): VoiceTranscriptionApi {
  // SSR-safe: getSpeechRecognitionCtor() returns null on the server. We want
  // the support flag to be stable across renders rather than flipping inside
  // an effect (set-state-in-effect lint), so derive it from the constructor
  // lookup at render time - useState's lazy initializer runs once per mount.
  const [supported] = useState(() => getSpeechRecognitionCtor() !== null);
  const [isListening, setIsListening] = useState(false);
  const [transcript, setTranscript] = useState('');
  const [interim, setInterim] = useState('');
  const [error, setError] = useState<string | null>(null);
  const recognitionRef = useRef<SpeechRecognitionLike | null>(null);

  useEffect(() => {
    const Ctor = getSpeechRecognitionCtor();
    if (!Ctor) return;

    const recognition = new Ctor();
    recognition.continuous = true;
    recognition.interimResults = true;
    recognition.lang = opts?.lang ?? 'en-US';

    recognition.onresult = (event) => {
      let interimText = '';
      for (let i = event.resultIndex; i < event.results.length; i++) {
        const res = event.results[i];
        if (!res) continue;
        const chunk = res[0].transcript;
        if (res.isFinal) {
          setTranscript((prev) => (prev ? `${prev} ${chunk}`.replace(/ {2,}/g, ' ') : chunk));
        } else {
          interimText += chunk;
        }
      }
      setInterim(interimText);
    };

    recognition.onerror = (event) => {
      setError(event.error);
      setIsListening(false);
    };

    recognition.onend = () => {
      setIsListening(false);
      setInterim('');
    };

    recognitionRef.current = recognition;

    return () => {
      try {
        recognition.abort();
      } catch {
        // ignore - already-stopped recognizers throw on abort()
      }
      recognitionRef.current = null;
    };
  }, [opts?.lang]);

  const start = useCallback(() => {
    if (!recognitionRef.current || isListening) return;
    setError(null);
    try {
      recognitionRef.current.start();
      setIsListening(true);
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Failed to start voice capture');
    }
  }, [isListening]);

  const stop = useCallback(() => {
    if (!recognitionRef.current || !isListening) return;
    try {
      recognitionRef.current.stop();
    } catch {
      // ignore
    }
  }, [isListening]);

  const reset = useCallback(() => {
    setTranscript('');
    setInterim('');
    setError(null);
  }, []);

  return { supported, isListening, transcript, interim, error, start, stop, reset };
}
