import { useRef, useCallback, useMemo } from 'react';

// Extend Window for vendor-prefixed SpeechRecognition
interface SpeechRecognitionEvent extends Event {
  results: SpeechRecognitionResultList;
  resultIndex: number;
}

type SpeechRecognitionConstructor = new () => SpeechRecognitionInstance;

interface SpeechRecognitionInstance extends EventTarget {
  continuous: boolean;
  interimResults: boolean;
  lang: string;
  start(): void;
  stop(): void;
  abort(): void;
  onresult: ((event: SpeechRecognitionEvent) => void) | null;
  onend: (() => void) | null;
  onerror: ((event: Event & { error: string }) => void) | null;
}

const getSpeechRecognitionCtor = (): SpeechRecognitionConstructor | null => {
  if (typeof window === 'undefined') return null;
  return (
    (window as unknown as Record<string, SpeechRecognitionConstructor>).SpeechRecognition ??
    (window as unknown as Record<string, SpeechRecognitionConstructor>).webkitSpeechRecognition ??
    null
  );
};

export const isWebSpeechSupported = getSpeechRecognitionCtor() !== null;

export function useSpeechRecognition() {
  const instanceRef = useRef<SpeechRecognitionInstance | null>(null);
  const transcriptRef = useRef('');
  const resolveRef = useRef<((text: string) => void) | null>(null);

  const isSupported = useMemo(() => isWebSpeechSupported, []);

  const start = useCallback((continuous = false) => {
    const Ctor = getSpeechRecognitionCtor();
    if (!Ctor) {
      return;
    }

    // Clean up any lingering instance
    if (instanceRef.current) {
      try { instanceRef.current.abort(); } catch {}
    }

    const recognition = new Ctor();
    recognition.continuous = continuous; // desktop: true (click-to-toggle), mobile: false (hold-to-talk)
    recognition.interimResults = true;   // get progressive updates while speaking
    recognition.lang = navigator.language || 'en-US';

    transcriptRef.current = '';
    instanceRef.current = recognition;

    recognition.onresult = (event: SpeechRecognitionEvent) => {
      // Grab the latest result — it's always the most complete/accurate
      const last = event.results[event.results.length - 1];
      const text = last[0].transcript;
      transcriptRef.current = text;
    };

    recognition.onend = () => {
      instanceRef.current = null;
      // If stop() is waiting for a result, resolve it now
      if (resolveRef.current) {
        resolveRef.current(transcriptRef.current);
        resolveRef.current = null;
      }
    };

    recognition.onerror = (event) => {
      console.error('[SpeechRecognition] error:', event.error);
      instanceRef.current = null;
      if (resolveRef.current) {
        resolveRef.current(transcriptRef.current);
        resolveRef.current = null;
      }
    };

    try {
      recognition.start();
    } catch (e) {
      console.error('[SpeechRecognition] start failed:', e);
    }
  }, []);

  const stop = useCallback((): Promise<string> => {
    return new Promise((resolve) => {
      const instance = instanceRef.current;
      if (!instance) {
        // Already ended (e.g. short utterance auto-stopped)
        resolve(transcriptRef.current);
        return;
      }

      // Wait for onend to fire with the final transcript
      resolveRef.current = resolve;

      try {
        instance.stop(); // triggers onend after finalizing
      } catch {
        instanceRef.current = null;
        resolve(transcriptRef.current);
        resolveRef.current = null;
      }

      // Safety timeout in case onend never fires
      setTimeout(() => {
        if (resolveRef.current) {
          resolveRef.current(transcriptRef.current);
          resolveRef.current = null;
          instanceRef.current = null;
        }
      }, 2000);
    });
  }, []);

  const abort = useCallback(() => {
    transcriptRef.current = '';
    const instance = instanceRef.current;
    if (instance) {
      try { instance.abort(); } catch {}
      instanceRef.current = null;
    }
    if (resolveRef.current) {
      resolveRef.current('');
      resolveRef.current = null;
    }
  }, []);

  return { start, stop, abort, isSupported };
}
