import { useAtom } from "jotai";
import { useEffect, useRef } from "react";
import { waveformLevelsAtom, recordingDurationMsAtom } from "../atoms/voiceAtoms";
import { insertTranscriptText } from "./useTranscriptComposer";
import { useChatInputState } from "./useChatInputState";
import { MicEvents, MicStateManager } from "../voice/MicStateManager";
const DEFAULT_WAVEFORM = [
    0.18, 0.28, 0.42, 0.58, 0.74, 0.9, 0.74, 0.58, 0.42, 0.28, 0.18,
];
export function useVoiceInputController(config) {
    const managerRef = useRef(null);
    const stopActionRef = useRef("send");
    const abortMessageRef = useRef(null);
    const [levels, setLevels] = useAtom(waveformLevelsAtom);
    const [durationMs, setDurationMs] = useAtom(recordingDurationMsAtom);
    const { mode, setMode, status, setStatus, text, setText, setTranscript, setError, } = useChatInputState();
    const modeRef = useRef(mode);
    const textRef = useRef(text);
    const configRef = useRef(config);
    modeRef.current = mode;
    textRef.current = text;
    configRef.current = config;
    useEffect(() => {
        const manager = new MicStateManager();
        manager.init();
        managerRef.current = manager;
        const unsubData = manager.on(MicEvents.Data, () => {
            setDurationMs((value) => value + 120);
        });
        const unsubLevel = manager.on(MicEvents.Level, (nextLevels) => {
            setLevels(nextLevels);
        });
        const unsubFinished = manager.on(MicEvents.Finished, (blob) => {
            setLevels(DEFAULT_WAVEFORM);
            setDurationMs(0);
            if (stopActionRef.current === "transcribe") {
                setStatus("transcribing");
                void transcribeBlob(blob);
                return;
            }
            setTranscript("");
            setStatus(modeRef.current === "voice" ? "voice-mode-ready" : "idle");
            setError(null);
            void configRef.current?.onVoiceSend?.(blob);
        });
        const unsubAbort = manager.on(MicEvents.Abort, () => {
            setLevels(DEFAULT_WAVEFORM);
            setDurationMs(0);
            setStatus(modeRef.current === "voice" ? "voice-mode-ready" : "idle");
            setError(abortMessageRef.current);
            abortMessageRef.current = null;
        });
        const unsubError = manager.on(MicEvents.Error, (error) => {
            setLevels(DEFAULT_WAVEFORM);
            setDurationMs(0);
            setStatus("error");
            setError(error.message);
        });
        return () => {
            unsubData();
            unsubLevel();
            unsubFinished();
            unsubAbort();
            unsubError();
            manager.dispose();
            managerRef.current = null;
        };
    }, []);
    async function transcribeBlob(blob) {
        try {
            const nextTranscript = configRef.current?.asrClient
                ? (await configRef.current.asrClient.transcribe(blob, {
                    lang: configRef.current.lang,
                })).text.trim()
                : (configRef.current?.simulatedTranscript ?? "hello from transcript").trim();
            if (!nextTranscript) {
                setStatus("error");
                setError("No speech was recognized.");
                return;
            }
            setTranscript(nextTranscript);
            setText(insertTranscriptText(textRef.current, nextTranscript));
            setDurationMs(0);
            setStatus("transcript-ready");
            setError(null);
        }
        catch (error) {
            setStatus("error");
            setError(error instanceof Error ? error.message : String(error));
        }
    }
    function beginVoiceMode() {
        setMode("voice");
        setStatus("voice-mode-ready");
        setError(null);
    }
    function startRecording() {
        if (status === "recording") {
            return;
        }
        setStatus("recording");
        setError(null);
        setTranscript("");
        setDurationMs(0);
        setLevels(DEFAULT_WAVEFORM);
        abortMessageRef.current = null;
        stopActionRef.current = "send";
        void managerRef.current?.start();
    }
    function cancelRecording(message = null) {
        abortMessageRef.current = message;
        managerRef.current?.abort();
    }
    function stopRecording(action = "send") {
        if (durationMs < (configRef.current?.minDurationMs ?? 450)) {
            cancelRecording(configRef.current?.shortRecordingMessage ?? "Hold a little longer to record.");
            return;
        }
        stopActionRef.current = action;
        managerRef.current?.stop();
    }
    return {
        mode,
        status,
        waveformLevels: levels,
        durationMs,
        beginVoiceMode,
        setMode,
        startRecording,
        cancelRecording,
        stopRecording,
    };
}
