/// <reference types="dom-speech-recognition" preserve="true" />
import { useCallback, useEffect, useRef, useState } from 'react';
import useI18n from './useI18n';
export default function useSpeechRecognition({ onResult, onInterimResult, langCode = navigator.language ?? 'en-US' }) {
    const SpeechRecognitionAPI = window.SpeechRecognition ?? window.webkitSpeechRecognition;
    const supported = !!SpeechRecognitionAPI;
    const t = useI18n();
    const [active, setActive] = useState(false);
    const [error, setError] = useState(null);
    const speechRecognitionRef = useRef(null);
    const speechRecognitionACRef = useRef(new AbortController());
    const onResultACRef = useRef(new AbortController());
    const keepListeningRef = useRef(false);
    const handleResult = useCallback((event) => {
        let nextValue = '';
        for (let i = event.resultIndex; i < event.results.length; i += 1) {
            const result = event.results[i];
            const processed = result[0].transcript;
            nextValue += processed;
            if (result.isFinal) {
                onResult(nextValue);
            }
            else {
                onInterimResult?.(nextValue);
            }
        }
    }, [onResult, onInterimResult]);
    useEffect(() => {
        if (!speechRecognitionRef.current)
            return;
        onResultACRef.current.abort();
        onResultACRef.current = new AbortController();
        speechRecognitionRef.current.addEventListener('result', handleResult, {
            signal: AbortSignal.any([speechRecognitionACRef.current.signal, onResultACRef.current.signal])
        });
    }, [handleResult]);
    const startRecognition = useCallback(() => {
        if (!supported)
            return;
        // abort previous listeners/session
        speechRecognitionACRef.current.abort();
        speechRecognitionACRef.current = new AbortController();
        const recognition = new SpeechRecognitionAPI();
        recognition.lang = langCode;
        recognition.interimResults = true;
        recognition.maxAlternatives = 1;
        recognition.continuous = true;
        recognition.addEventListener('start', () => {
            setActive(true);
            setError(null);
        }, { signal: speechRecognitionACRef.current.signal });
        recognition.addEventListener('result', handleResult, {
            signal: AbortSignal.any([speechRecognitionACRef.current.signal, onResultACRef.current.signal])
        });
        recognition.addEventListener('error', event => {
            if (event.error === 'no-speech') {
                setError(t('no_speech_detected'));
            }
            else if (event.error === 'audio-capture') {
                setError(t('no_microphone_found'));
            }
            else if (event.error === 'not-allowed') {
                setError(t('microphone_access_denied'));
            }
            else {
                setError(t('speech_recognition_error', [event.error]));
            }
            setActive(false);
        }, { signal: speechRecognitionACRef.current.signal });
        recognition.addEventListener('end', () => {
            if (keepListeningRef.current) {
                startRecognition();
            }
            else {
                setActive(false);
            }
        }, { signal: speechRecognitionACRef.current.signal });
        speechRecognitionRef.current = recognition;
        recognition.start();
    }, [SpeechRecognitionAPI, langCode, t, handleResult, supported]);
    const stop = useCallback(() => {
        keepListeningRef.current = false;
        const recognition = speechRecognitionRef.current;
        if (!recognition) {
            setActive(false);
            return;
        }
        recognition.stop();
    }, []);
    const start = useCallback(() => {
        setError(null);
        if (!supported) {
            setError(t('speech_recognition_not_supported'));
            return;
        }
        keepListeningRef.current = true;
        startRecognition();
    }, [startRecognition, supported, t]);
    // Stop recognition on page hide or visibility change
    useEffect(() => {
        const ac = new AbortController();
        const handleVisibilityChange = () => {
            if (document.visibilityState !== 'visible') {
                stop();
            }
        };
        window.addEventListener('pagehide', stop, { signal: ac.signal });
        window.addEventListener('blur', stop, { signal: ac.signal });
        document.addEventListener('visibilitychange', handleVisibilityChange, { signal: ac.signal });
        return () => {
            ac.abort();
        };
    }, []);
    return {
        supported,
        active,
        error,
        start,
        stop
    };
}
//# sourceMappingURL=useSpeechRecognition.js.map