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

const DEFAULT_SILENCE_MS = 2000;

function getSpeechRecognitionCtor(): SpeechRecognitionConstructor | null {
    if (typeof window === 'undefined') {
        return null;
    }
    return window.SpeechRecognition ?? window.webkitSpeechRecognition ?? null;
}

export type UseTrainingSpeechRecognitionOptions = {
    /** Milisegundos sin nuevos resultados STT antes de disparar onSilence. */
    silenceMs?: number;
    lang?: string;
    onSilence: (text: string) => void | Promise<void>;
};

export function useTrainingSpeechRecognition({
    silenceMs = Number(import.meta.env.VITE_TRAINING_SILENCE_MS) || DEFAULT_SILENCE_MS,
    lang = import.meta.env.VITE_TRAINING_SPEECH_LANG || 'es-ES',
    onSilence,
}: UseTrainingSpeechRecognitionOptions) {
    const [listening, setListening] = useState(false);
    const [supported, setSupported] = useState(true);
    const [interimText, setInterimText] = useState('');
    const [pendingText, setPendingText] = useState('');
    const [speechError, setSpeechError] = useState<string | null>(null);

    const recognitionRef = useRef<SpeechRecognition | null>(null);
    const bufferRef = useRef('');
    const silenceTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
    const onSilenceRef = useRef(onSilence);
    const queueRef = useRef<string[]>([]);
    const processingRef = useRef(false);

    onSilenceRef.current = onSilence;

    const processQueue = useCallback(async () => {
        if (processingRef.current) {
            return;
        }
        processingRef.current = true;
        try {
            while (queueRef.current.length > 0) {
                const text = queueRef.current.shift();
                if (!text) {
                    continue;
                }
                await onSilenceRef.current(text);
            }
        } finally {
            processingRef.current = false;
            if (queueRef.current.length > 0) {
                void processQueue();
            }
        }
    }, []);

    const clearSilenceTimer = useCallback(() => {
        if (silenceTimerRef.current != null) {
            clearTimeout(silenceTimerRef.current);
            silenceTimerRef.current = null;
        }
    }, []);

    const flushOnSilence = useCallback(() => {
        clearSilenceTimer();
        const text = bufferRef.current.trim();
        bufferRef.current = '';
        setPendingText('');
        setInterimText('');
        if (!text) {
            return;
        }
        queueRef.current.push(text);
        void processQueue();
    }, [clearSilenceTimer, processQueue]);

    const scheduleSilenceFlush = useCallback(() => {
        clearSilenceTimer();
        silenceTimerRef.current = setTimeout(() => {
            void flushOnSilence();
        }, Math.max(500, silenceMs));
    }, [clearSilenceTimer, flushOnSilence, silenceMs]);

    const stopListening = useCallback(() => {
        clearSilenceTimer();
        const recognition = recognitionRef.current;
        recognitionRef.current = null;
        if (recognition) {
            try {
                recognition.onresult = null;
                recognition.onerror = null;
                recognition.onend = null;
                recognition.abort();
            } catch {
                // ignore
            }
        }
        setListening(false);
        setInterimText('');
    }, [clearSilenceTimer]);

    const startListening = useCallback(() => {
        const Ctor = getSpeechRecognitionCtor();
        if (!Ctor) {
            setSupported(false);
            return;
        }

        setSpeechError(null);
        bufferRef.current = '';
        setPendingText('');
        setInterimText('');

        const recognition = new Ctor();
        recognition.continuous = true;
        recognition.interimResults = true;
        recognition.lang = lang;
        recognition.maxAlternatives = 1;

        recognition.onresult = (event: SpeechRecognitionEvent) => {
            let interim = '';
            for (let i = event.resultIndex; i < event.results.length; i++) {
                const result = event.results[i];
                const transcript = result[0]?.transcript ?? '';
                if (result.isFinal) {
                    bufferRef.current += transcript;
                } else {
                    interim += transcript;
                }
            }
            setPendingText(bufferRef.current);
            setInterimText(interim);
            scheduleSilenceFlush();
        };

        recognition.onerror = (event: SpeechRecognitionErrorEvent) => {
            if (event.error === 'not-allowed' || event.error === 'service-not-allowed') {
                setSpeechError('permission_denied');
                stopListening();
                return;
            }
            if (event.error !== 'no-speech' && event.error !== 'aborted') {
                setSpeechError(event.error);
            }
        };

        recognition.onend = () => {
            if (recognitionRef.current === recognition) {
                try {
                    recognition.start();
                } catch {
                    stopListening();
                }
            }
        };

        recognitionRef.current = recognition;

        try {
            recognition.start();
            setListening(true);
        } catch {
            setSpeechError('start_failed');
            recognitionRef.current = null;
        }
    }, [lang, scheduleSilenceFlush, stopListening]);

    const toggleListening = useCallback(() => {
        if (listening) {
            flushOnSilence();
            stopListening();
            return;
        }
        startListening();
    }, [flushOnSilence, listening, startListening, stopListening]);

    useEffect(() => {
        setSupported(getSpeechRecognitionCtor() != null);
        return () => {
            stopListening();
        };
    }, [stopListening]);

    return {
        listening,
        supported,
        interimText,
        pendingText,
        speechError,
        toggleListening,
        stopListening,
    };
}
