import { jsx as _jsx } from "react/jsx-runtime";
/**
 * React Hooks for NeuroLink Client SDK
 *
 * Provides React hooks for interacting with NeuroLink agents, chat, workflows,
 * and voice features. Compatible with React 18+ and follows React hooks best practices.
 *
 * @remarks
 * This module requires React 18+ as a peer dependency. Install it with:
 * ```bash
 * npm install react react-dom
 * # or
 * pnpm add react react-dom
 * ```
 *
 * @module @neurolink/react
 */
import { useState, useCallback, useRef, useEffect, useContext, createContext, useMemo, } from "react";
import { NeuroLinkClient, createClient } from "./httpClient.js";
// =============================================================================
// Context and Provider
// =============================================================================
/**
 * Context for NeuroLink client
 */
const NeuroLinkContext = createContext(null);
/**
 * Provider component for NeuroLink client
 *
 * Wraps your application to provide the NeuroLink client to all hooks.
 *
 * @example
 * ```tsx
 * import { NeuroLinkProvider } from '@neurolink/react';
 *
 * function App() {
 *   return (
 *     <NeuroLinkProvider
 *       config={{
 *         baseUrl: 'https://api.neurolink.example.com',
 *         apiKey: process.env.NEUROLINK_API_KEY,
 *       }}
 *     >
 *       <YourApp />
 *     </NeuroLinkProvider>
 *   );
 * }
 * ```
 */
export function NeuroLinkProvider({ config, children, }) {
    const client = useMemo(() => createClient(config), [config]);
    return (_jsx(NeuroLinkContext.Provider, { value: client, children: children }));
}
/**
 * Hook to access the NeuroLink client
 *
 * Must be used within a NeuroLinkProvider.
 *
 * @throws Error if used outside of NeuroLinkProvider
 */
export function useNeuroLinkClient() {
    const client = useContext(NeuroLinkContext);
    if (!client) {
        throw new Error("useNeuroLinkClient must be used within a NeuroLinkProvider. " +
            "Wrap your component tree with <NeuroLinkProvider config={...}>.");
    }
    return client;
}
// =============================================================================
// Utility Hooks
// =============================================================================
/**
 * Generate a unique message ID
 */
function generateMessageId() {
    return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
// =============================================================================
// useChat Hook
// =============================================================================
/**
 * React hook for chat interactions with NeuroLink agents
 *
 * Provides a chat interface with support for streaming responses,
 * tool calls, and conversation history management.
 *
 * @example Basic usage
 * ```tsx
 * function ChatComponent() {
 *   const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
 *     api: '/api/chat',
 *     agentId: 'my-agent',
 *   });
 *
 *   return (
 *     <div>
 *       {messages.map(m => (
 *         <div key={m.id} className={m.role}>
 *           {m.content}
 *         </div>
 *       ))}
 *       <form onSubmit={handleSubmit}>
 *         <input value={input} onChange={handleInputChange} />
 *         <button type="submit" disabled={isLoading}>Send</button>
 *       </form>
 *     </div>
 *   );
 * }
 * ```
 */
export function useChat(options = {}) {
    const { agentId, initialMessages = [], sessionId: initialSessionId, systemPrompt, onFinish, onError, onToolCall, body, generateId = generateMessageId, } = options;
    const client = useNeuroLinkClient();
    const [messages, setMessages] = useState(initialMessages);
    const [input, setInput] = useState("");
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState(null);
    const [toolCalls, setToolCalls] = useState([]);
    const abortControllerRef = useRef(null);
    const sessionIdRef = useRef(initialSessionId);
    // Keep a ref to the latest messages so callbacks never see stale state
    const messagesRef = useRef(initialMessages);
    useEffect(() => {
        messagesRef.current = messages;
    }, [messages]);
    /**
     * Handle input change
     */
    const handleInputChange = useCallback((e) => {
        setInput(e.target.value);
    }, []);
    /**
     * Append a message and get response
     */
    const append = useCallback(async (message) => {
        const userMessage = {
            id: generateId(),
            createdAt: new Date(),
            ...message,
        };
        setMessages((prev) => [...prev, userMessage]);
        setIsLoading(true);
        setError(null);
        setToolCalls([]);
        // Create abort controller
        abortControllerRef.current = new AbortController();
        const assistantId = generateId();
        const currentToolCalls = [];
        let assistantContent = "";
        // Add placeholder for assistant message
        const assistantMessage = {
            id: assistantId,
            role: "assistant",
            content: "",
            createdAt: new Date(),
        };
        setMessages((prev) => [...prev, assistantMessage]);
        try {
            // Read from the ref so we always have the latest messages
            const currentMessages = messagesRef.current;
            await client.stream({
                input: {
                    text: userMessage.content,
                },
                ...(agentId ? { context: { agentId } } : {}),
                ...(systemPrompt ? { systemPrompt } : {}),
                ...(body
                    ? {
                        context: {
                            ...(agentId ? { agentId } : {}),
                            messages: [...currentMessages, userMessage].map((m) => ({
                                role: m.role,
                                content: m.content,
                            })),
                            sessionId: sessionIdRef.current,
                            ...body,
                        },
                    }
                    : {
                        context: {
                            ...(agentId ? { agentId } : {}),
                            messages: [...currentMessages, userMessage].map((m) => ({
                                role: m.role,
                                content: m.content,
                            })),
                            sessionId: sessionIdRef.current,
                        },
                    }),
            }, {
                onText: (text) => {
                    assistantContent += text;
                    setMessages((prev) => prev.map((m) => m.id === assistantId
                        ? { ...m, content: assistantContent }
                        : m));
                },
                onToolCall: (toolCall) => {
                    currentToolCalls.push(toolCall);
                    setToolCalls([...currentToolCalls]);
                    onToolCall?.(toolCall);
                },
                onToolResult: (toolResult) => {
                    setMessages((prev) => prev.map((m) => m.id === assistantId
                        ? {
                            ...m,
                            toolCalls: currentToolCalls,
                            toolResults: [...(m.toolResults ?? []), toolResult],
                        }
                        : m));
                },
                onMetadata: (metadata) => {
                    if (metadata?.sessionId) {
                        sessionIdRef.current = metadata.sessionId;
                    }
                },
            }, { signal: abortControllerRef.current.signal });
            // Final message update
            const finalMessage = {
                id: assistantId,
                role: "assistant",
                content: assistantContent,
                toolCalls: currentToolCalls.length > 0 ? currentToolCalls : undefined,
                createdAt: new Date(),
            };
            setMessages((prev) => prev.map((m) => m.id === assistantId ? finalMessage : m));
            onFinish?.(finalMessage);
            return assistantId;
        }
        catch (err) {
            if (err.name === "AbortError") {
                return null;
            }
            const apiError = err;
            setError(apiError);
            onError?.(apiError);
            return null;
        }
        finally {
            setIsLoading(false);
            setToolCalls([]);
            abortControllerRef.current = null;
        }
    }, [
        client,
        agentId,
        systemPrompt,
        body,
        generateId,
        onFinish,
        onError,
        onToolCall,
    ]);
    /**
     * Handle form submission
     */
    const handleSubmit = useCallback((e, submitOptions) => {
        e?.preventDefault?.();
        if (!input.trim())
            return;
        const message = {
            role: "user",
            content: input,
            metadata: submitOptions?.data,
        };
        setInput("");
        append(message);
    }, [input, append]);
    /**
     * Reload the last assistant message
     */
    const reload = useCallback(async () => {
        // Read from the ref so we always have the latest messages
        const currentMessages = messagesRef.current;
        const lastUserMessageIndex = currentMessages.findLastIndex((m) => m.role === "user");
        if (lastUserMessageIndex === -1)
            return null;
        const lastUserMessage = currentMessages[lastUserMessageIndex];
        // Remove messages after the last user message
        setMessages((prev) => prev.slice(0, lastUserMessageIndex));
        return append({
            role: "user",
            content: lastUserMessage.content,
        });
    }, [append]);
    /**
     * Stop streaming
     */
    const stop = useCallback(() => {
        abortControllerRef.current?.abort();
    }, []);
    /**
     * Clear error
     */
    const clearError = useCallback(() => {
        setError(null);
    }, []);
    // Cleanup on unmount
    useEffect(() => {
        return () => {
            abortControllerRef.current?.abort();
        };
    }, []);
    return {
        messages,
        input,
        setInput,
        handleInputChange,
        handleSubmit,
        append,
        reload,
        stop,
        setMessages,
        isLoading,
        error,
        clearError,
        toolCalls,
    };
}
// =============================================================================
// useAgent Hook
// =============================================================================
/**
 * React hook for interacting with NeuroLink agents
 *
 * Provides methods for executing agents with both streaming
 * and non-streaming responses, with session management.
 *
 * @example Basic usage
 * ```tsx
 * function AgentComponent() {
 *   const { execute, isLoading, result, error } = useAgent({
 *     agentId: 'my-agent',
 *     onResponse: (result) => console.log('Agent responded:', result),
 *   });
 *
 *   return (
 *     <div>
 *       <button onClick={() => execute('Hello!')}>
 *         {isLoading ? 'Thinking...' : 'Ask Agent'}
 *       </button>
 *       {result && <p>{result.content}</p>}
 *       {error && <p className="error">{error.message}</p>}
 *     </div>
 *   );
 * }
 * ```
 */
export function useAgent(options) {
    const { agentId, sessionId: initialSessionId, onResponse, onError, onToolCall, initialInput, } = options;
    const client = useNeuroLinkClient();
    const [sessionId, setSessionId] = useState(initialSessionId ?? null);
    const [isLoading, setIsLoading] = useState(false);
    const [isStreaming, setIsStreaming] = useState(false);
    const [result, setResult] = useState(null);
    const [error, setError] = useState(null);
    const abortControllerRef = useRef(null);
    const hasAutoExecuted = useRef(false);
    // Keep a ref to the latest sessionId so callbacks never see stale state
    const sessionIdRef = useRef(initialSessionId ?? null);
    useEffect(() => {
        sessionIdRef.current = sessionId;
    }, [sessionId]);
    /**
     * Execute agent (non-streaming)
     */
    const execute = useCallback(async (input, executeOptions) => {
        setIsLoading(true);
        setError(null);
        abortControllerRef.current = new AbortController();
        try {
            const response = await client.executeAgent({
                agentId,
                input,
                sessionId: sessionIdRef.current ?? undefined,
                ...executeOptions,
            }, { signal: abortControllerRef.current.signal });
            const agentResult = response.data;
            setResult(agentResult);
            setSessionId(agentResult.sessionId);
            onResponse?.(agentResult);
            return agentResult;
        }
        catch (err) {
            const apiError = err;
            setError(apiError);
            onError?.(apiError);
            throw err;
        }
        finally {
            setIsLoading(false);
            abortControllerRef.current = null;
        }
    }, [client, agentId, onResponse, onError]);
    /**
     * Stream agent execution
     */
    const stream = useCallback(async (input, callbacks) => {
        setIsStreaming(true);
        setIsLoading(true);
        setError(null);
        abortControllerRef.current = new AbortController();
        try {
            await client.streamAgent({
                agentId,
                input,
                sessionId: sessionIdRef.current ?? undefined,
                stream: true,
            }, {
                ...callbacks,
                onToolCall: (toolCall) => {
                    callbacks?.onToolCall?.(toolCall);
                    onToolCall?.(toolCall);
                },
                onDone: (streamResult) => {
                    callbacks?.onDone?.(streamResult);
                    setIsStreaming(false);
                },
                onError: (apiError) => {
                    callbacks?.onError?.(apiError);
                    setError(apiError);
                    onError?.(apiError);
                },
            }, { signal: abortControllerRef.current.signal });
        }
        catch (err) {
            const apiError = err;
            setError(apiError);
            onError?.(apiError);
        }
        finally {
            setIsLoading(false);
            setIsStreaming(false);
            abortControllerRef.current = null;
        }
    }, [client, agentId, onToolCall, onError]);
    /**
     * Abort current execution
     */
    const abort = useCallback(() => {
        abortControllerRef.current?.abort();
        setIsLoading(false);
        setIsStreaming(false);
    }, []);
    /**
     * Clear error
     */
    const clearError = useCallback(() => {
        setError(null);
    }, []);
    // Auto-execute on mount if initialInput is provided
    useEffect(() => {
        if (initialInput && !hasAutoExecuted.current) {
            hasAutoExecuted.current = true;
            execute(initialInput);
        }
    }, [initialInput, execute]);
    // Cleanup on unmount
    useEffect(() => {
        return () => {
            abortControllerRef.current?.abort();
        };
    }, []);
    return {
        execute,
        stream,
        sessionId,
        setSessionId,
        isLoading,
        isStreaming,
        result,
        error,
        clearError,
        abort,
    };
}
// =============================================================================
// useWorkflow Hook
// =============================================================================
/**
 * React hook for executing NeuroLink workflows
 *
 * Provides methods for executing, resuming, and monitoring workflows
 * with automatic status polling and suspension handling.
 *
 * @example Basic usage
 * ```tsx
 * function WorkflowComponent() {
 *   const { execute, status, result, isLoading, error } = useWorkflow({
 *     workflowId: 'data-processing-workflow',
 *     onComplete: (result) => console.log('Workflow completed:', result),
 *     onStepComplete: (step) => console.log('Step completed:', step.stepId),
 *   });
 *
 *   return (
 *     <div>
 *       <button onClick={() => execute({ data: inputData })}>
 *         Run Workflow
 *       </button>
 *       {status && <p>Status: {status}</p>}
 *       {result?.output && <pre>{JSON.stringify(result.output, null, 2)}</pre>}
 *     </div>
 *   );
 * }
 * ```
 */
export function useWorkflow(options) {
    const { workflowId, onComplete, onError, onStepComplete, pollInterval = 2000, } = options;
    const client = useNeuroLinkClient();
    const [runId, setRunId] = useState(null);
    const [status, setStatus] = useState(null);
    const [isLoading, setIsLoading] = useState(false);
    const [result, setResult] = useState(null);
    const [error, setError] = useState(null);
    const pollIntervalRef = useRef(null);
    const previousStepsRef = useRef(new Set());
    /**
     * Stop polling
     */
    const stopPolling = useCallback(() => {
        if (pollIntervalRef.current) {
            clearInterval(pollIntervalRef.current);
            pollIntervalRef.current = null;
        }
    }, []);
    /**
     * Poll for workflow status
     */
    const pollStatus = useCallback(async (currentRunId) => {
        try {
            const response = await client.getWorkflowStatus(workflowId, currentRunId);
            const workflowResult = response.data;
            setStatus(workflowResult.status);
            setResult(workflowResult);
            // Check for newly completed steps
            if (workflowResult.steps && onStepComplete) {
                for (const step of workflowResult.steps) {
                    if (step.status === "completed" &&
                        !previousStepsRef.current.has(step.stepId)) {
                        previousStepsRef.current.add(step.stepId);
                        onStepComplete(step);
                    }
                }
            }
            // Handle completion
            if (workflowResult.status === "completed") {
                stopPolling();
                setIsLoading(false);
                onComplete?.(workflowResult);
            }
            else if (workflowResult.status === "failed") {
                stopPolling();
                setIsLoading(false);
                if (workflowResult.error) {
                    setError(workflowResult.error);
                    onError?.(workflowResult.error);
                }
            }
            else if (workflowResult.status === "suspended") {
                stopPolling();
                setIsLoading(false);
            }
        }
        catch (err) {
            stopPolling();
            setIsLoading(false);
            const apiError = err;
            setError(apiError);
            onError?.(apiError);
        }
    }, [client, workflowId, onComplete, onError, onStepComplete, stopPolling]);
    /**
     * Start polling for workflow status
     */
    const startPolling = useCallback((currentRunId) => {
        stopPolling();
        pollIntervalRef.current = setInterval(() => pollStatus(currentRunId), pollInterval);
    }, [pollInterval, pollStatus, stopPolling]);
    /**
     * Execute workflow
     */
    const execute = useCallback(async (input, executeOptions) => {
        setIsLoading(true);
        setError(null);
        setStatus(null);
        setResult(null);
        previousStepsRef.current.clear();
        try {
            const response = await client.executeWorkflow({
                workflowId,
                input,
                ...executeOptions,
            });
            const workflowResult = response.data;
            setRunId(workflowResult.runId);
            setStatus(workflowResult.status);
            setResult(workflowResult);
            // Start polling if workflow is running
            if (workflowResult.status === "running") {
                startPolling(workflowResult.runId);
            }
            else if (workflowResult.status === "completed") {
                setIsLoading(false);
                onComplete?.(workflowResult);
            }
            else if (workflowResult.status === "failed") {
                setIsLoading(false);
                if (workflowResult.error) {
                    setError(workflowResult.error);
                    onError?.(workflowResult.error);
                }
            }
            return workflowResult;
        }
        catch (err) {
            setIsLoading(false);
            const apiError = err;
            setError(apiError);
            onError?.(apiError);
            throw err;
        }
    }, [client, workflowId, onComplete, onError, startPolling]);
    /**
     * Resume suspended workflow
     */
    const resume = useCallback(async (resumeToken, resumeData) => {
        setIsLoading(true);
        setError(null);
        try {
            const response = await client.resumeWorkflow(workflowId, resumeToken, resumeData);
            const workflowResult = response.data;
            setRunId(workflowResult.runId);
            setStatus(workflowResult.status);
            setResult(workflowResult);
            // Start polling if workflow is running
            if (workflowResult.status === "running") {
                startPolling(workflowResult.runId);
            }
            else if (workflowResult.status === "completed") {
                setIsLoading(false);
                onComplete?.(workflowResult);
            }
            return workflowResult;
        }
        catch (err) {
            setIsLoading(false);
            const apiError = err;
            setError(apiError);
            onError?.(apiError);
            throw err;
        }
    }, [client, workflowId, onComplete, onError, startPolling]);
    /**
     * Get workflow status
     */
    const getStatus = useCallback(async (statusRunId) => {
        const response = await client.getWorkflowStatus(workflowId, statusRunId);
        return response.data;
    }, [client, workflowId]);
    /**
     * Cancel workflow execution
     */
    const cancel = useCallback(async (cancelRunId) => {
        stopPolling();
        await client.cancelWorkflow(workflowId, cancelRunId);
        setStatus("failed");
        setIsLoading(false);
    }, [client, workflowId, stopPolling]);
    /**
     * Clear error
     */
    const clearError = useCallback(() => {
        setError(null);
    }, []);
    // Cleanup on unmount
    useEffect(() => {
        return () => {
            stopPolling();
        };
    }, [stopPolling]);
    return {
        execute,
        resume,
        getStatus,
        cancel,
        runId,
        status,
        isLoading,
        result,
        error,
        clearError,
    };
}
// =============================================================================
// useVoice Hook
// =============================================================================
/**
 * React hook for voice interactions with NeuroLink
 *
 * Provides voice input (speech recognition) and output (text-to-speech)
 * capabilities with support for real-time conversation.
 *
 * @example Basic usage
 * ```tsx
 * function VoiceComponent() {
 *   const {
 *     startListening,
 *     stopListening,
 *     speak,
 *     isListening,
 *     isSpeaking,
 *     transcript,
 *     isSupported,
 *   } = useVoice({
 *     voice: 'en-US-Neural2-C',
 *     autoPlay: true,
 *   });
 *
 *   if (!isSupported) {
 *     return <p>Voice not supported in this browser</p>;
 *   }
 *
 *   return (
 *     <div>
 *       <button onClick={isListening ? stopListening : startListening}>
 *         {isListening ? 'Stop' : 'Start'} Listening
 *       </button>
 *       <p>Transcript: {transcript}</p>
 *       <button onClick={() => speak('Hello!')}>Speak</button>
 *     </div>
 *   );
 * }
 * ```
 */
export function useVoice(options = {}) {
    const { voice, language = "en-US", autoPlay = true, onSpeechStart, onSpeechEnd, onError, api = "/api/tts", enableSpeechRecognition = true, } = options;
    const [isListening, setIsListening] = useState(false);
    const [isSpeaking, setIsSpeaking] = useState(false);
    const [isProcessing, setIsProcessing] = useState(false);
    const [transcript, setTranscript] = useState("");
    const [response, setResponse] = useState(null);
    const [error, setError] = useState(null);
    const recognitionRef = useRef(null);
    const synthesisRef = useRef(null);
    // Check browser support
    const isSupported = useMemo(() => {
        if (typeof window === "undefined")
            return false;
        const hasSpeechRecognition = "SpeechRecognition" in window || "webkitSpeechRecognition" in window;
        const hasSpeechSynthesis = "speechSynthesis" in window;
        return hasSpeechRecognition || hasSpeechSynthesis;
    }, []);
    /**
     * Initialize speech recognition
     */
    const initRecognition = useCallback(() => {
        if (typeof window === "undefined")
            return null;
        const SpeechRecognitionCtor = window.SpeechRecognition ||
            window.webkitSpeechRecognition;
        if (!SpeechRecognitionCtor)
            return null;
        const recognition = new SpeechRecognitionCtor();
        recognition.continuous = true;
        recognition.interimResults = true;
        recognition.lang = language;
        recognition.onresult = (event) => {
            let finalTranscript = "";
            let interimTranscript = "";
            for (let i = event.resultIndex; i < event.results.length; i++) {
                const result = event.results[i];
                if (result.isFinal) {
                    finalTranscript += result[0].transcript;
                }
                else {
                    interimTranscript += result[0].transcript;
                }
            }
            setTranscript(finalTranscript || interimTranscript);
        };
        recognition.onerror = (event) => {
            const apiError = {
                code: "SPEECH_RECOGNITION_ERROR",
                message: event.error,
                status: 500,
            };
            setError(apiError);
            onError?.(apiError);
            setIsListening(false);
        };
        recognition.onend = () => {
            setIsListening(false);
        };
        return recognition;
    }, [language, onError]);
    /**
     * Start listening for voice input
     */
    const startListening = useCallback(() => {
        if (!enableSpeechRecognition)
            return;
        if (!recognitionRef.current) {
            recognitionRef.current = initRecognition();
        }
        if (recognitionRef.current) {
            setTranscript("");
            setError(null);
            recognitionRef.current.start();
            setIsListening(true);
        }
    }, [enableSpeechRecognition, initRecognition]);
    /**
     * Stop listening
     */
    const stopListening = useCallback(() => {
        if (recognitionRef.current) {
            recognitionRef.current.stop();
            setIsListening(false);
        }
    }, []);
    /**
     * Speak text using TTS
     */
    const speak = useCallback(async (text) => {
        if (typeof window === "undefined")
            return;
        setIsSpeaking(true);
        onSpeechStart?.();
        try {
            // Use Web Speech API for basic TTS
            if ("speechSynthesis" in window) {
                return new Promise((resolve, reject) => {
                    const utterance = new SpeechSynthesisUtterance(text);
                    utterance.lang = language;
                    if (voice) {
                        const voices = window.speechSynthesis.getVoices();
                        const selectedVoice = voices.find((v) => v.name.includes(voice));
                        if (selectedVoice) {
                            utterance.voice = selectedVoice;
                        }
                    }
                    utterance.onend = () => {
                        setIsSpeaking(false);
                        onSpeechEnd?.();
                        resolve();
                    };
                    utterance.onerror = (event) => {
                        setIsSpeaking(false);
                        const apiError = {
                            code: "SPEECH_SYNTHESIS_ERROR",
                            message: event.error,
                            status: 500,
                        };
                        setError(apiError);
                        onError?.(apiError);
                        reject(event);
                    };
                    synthesisRef.current = utterance;
                    window.speechSynthesis.speak(utterance);
                });
            }
        }
        catch (err) {
            setIsSpeaking(false);
            const apiError = {
                code: "TTS_ERROR",
                message: err.message,
                status: 500,
            };
            setError(apiError);
            onError?.(apiError);
        }
    }, [voice, language, onSpeechStart, onSpeechEnd, onError]);
    /**
     * Stop speaking
     */
    const stopSpeaking = useCallback(() => {
        if (typeof window !== "undefined" && "speechSynthesis" in window) {
            window.speechSynthesis.cancel();
            setIsSpeaking(false);
        }
    }, []);
    /**
     * Submit voice input and get response
     */
    const submit = useCallback(async (text) => {
        setIsProcessing(true);
        setError(null);
        try {
            const res = await fetch(api, {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({ text, voice }),
            });
            if (!res.ok) {
                const errorData = await res.json();
                throw errorData;
            }
            const data = await res.json();
            const responseText = data.response || data.content || text;
            setResponse(responseText);
            if (autoPlay) {
                await speak(responseText);
            }
            return responseText;
        }
        catch (err) {
            const apiError = err;
            setError(apiError);
            onError?.(apiError);
            throw err;
        }
        finally {
            setIsProcessing(false);
        }
    }, [api, voice, autoPlay, speak, onError]);
    // Cleanup on unmount
    useEffect(() => {
        return () => {
            recognitionRef.current?.stop();
            if (typeof window !== "undefined" && "speechSynthesis" in window) {
                window.speechSynthesis.cancel();
            }
        };
    }, []);
    return {
        startListening,
        stopListening,
        speak,
        stopSpeaking,
        submit,
        isListening,
        isSpeaking,
        isProcessing,
        transcript,
        response,
        error,
        isSupported,
    };
}
// =============================================================================
// useStream Hook
// =============================================================================
/**
 * React hook for streaming responses from NeuroLink
 *
 * @example
 * ```tsx
 * function StreamComponent() {
 *   const { start, stop, text, isStreaming } = useStream({
 *     api: '/api/stream',
 *   });
 *
 *   return (
 *     <div>
 *       <button onClick={() => start({ prompt: 'Tell me a story' })}>
 *         Start
 *       </button>
 *       <button onClick={stop} disabled={!isStreaming}>Stop</button>
 *       <p>{text}</p>
 *     </div>
 *   );
 * }
 * ```
 */
export function useStream(options = {}) {
    const { api = "/api/stream", callbacks } = options;
    const [text, setText] = useState("");
    const [events, setEvents] = useState([]);
    const [isStreaming, setIsStreaming] = useState(false);
    const [error, setError] = useState(null);
    const abortControllerRef = useRef(null);
    /**
     * Start streaming
     */
    const start = useCallback(async (streamOptions) => {
        setText("");
        setEvents([]);
        setError(null);
        setIsStreaming(true);
        abortControllerRef.current = new AbortController();
        try {
            const response = await fetch(api, {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    Accept: "text/event-stream",
                },
                body: JSON.stringify(streamOptions),
                signal: abortControllerRef.current.signal,
            });
            if (!response.ok) {
                const errorData = await response.json();
                throw errorData;
            }
            const reader = response.body?.getReader();
            if (!reader) {
                throw new Error("Response body is not readable");
            }
            const decoder = new TextDecoder();
            let buffer = "";
            let fullText = "";
            while (true) {
                const { done, value } = await reader.read();
                if (done)
                    break;
                buffer += decoder.decode(value, { stream: true });
                const lines = buffer.split("\n");
                buffer = lines.pop() ?? "";
                for (const line of lines) {
                    if (line.startsWith("data: ")) {
                        const data = line.slice(6);
                        if (data === "[DONE]") {
                            break;
                        }
                        try {
                            const event = JSON.parse(data);
                            setEvents((prev) => [...prev, event]);
                            if (event.type === "text" && event.content) {
                                fullText += event.content;
                                setText(fullText);
                                callbacks?.onText?.(event.content);
                            }
                            else if (event.type === "tool-call" && event.toolCall) {
                                callbacks?.onToolCall?.(event.toolCall);
                            }
                            else if (event.type === "tool-result" && event.toolResult) {
                                callbacks?.onToolResult?.(event.toolResult);
                            }
                            else if (event.type === "error" && event.error) {
                                callbacks?.onError?.(event.error);
                            }
                            else if (event.type === "metadata" && event.metadata) {
                                callbacks?.onMetadata?.(event.metadata);
                            }
                        }
                        catch {
                            // Ignore parse errors
                        }
                    }
                }
            }
            reader.releaseLock();
        }
        catch (err) {
            if (err.name === "AbortError") {
                return;
            }
            const apiError = err;
            setError(apiError);
            callbacks?.onError?.(apiError);
        }
        finally {
            setIsStreaming(false);
            abortControllerRef.current = null;
        }
    }, [api, callbacks]);
    /**
     * Stop streaming
     */
    const stop = useCallback(() => {
        abortControllerRef.current?.abort();
        setIsStreaming(false);
    }, []);
    // Cleanup on unmount
    useEffect(() => {
        return () => {
            abortControllerRef.current?.abort();
        };
    }, []);
    return {
        start,
        stop,
        text,
        events,
        isStreaming,
        error,
    };
}
// =============================================================================
// useTools Hook
// =============================================================================
/**
 * React hook for accessing and executing NeuroLink tools
 *
 * @example
 * ```tsx
 * function ToolsComponent() {
 *   const { tools, execute, isLoading, error } = useTools({
 *     category: 'data',
 *   });
 *
 *   return (
 *     <div>
 *       {tools.map(tool => (
 *         <div key={tool.name}>
 *           <h3>{tool.name}</h3>
 *           <p>{tool.description}</p>
 *           <button onClick={() => execute(tool.name, { input: 'test' })}>
 *             Execute
 *           </button>
 *         </div>
 *       ))}
 *     </div>
 *   );
 * }
 * ```
 */
export function useTools(options = {}) {
    const { category, serverId, refreshInterval } = options;
    const client = useNeuroLinkClient();
    const [tools, setTools] = useState([]);
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState(null);
    /**
     * Refresh tool list
     */
    const refresh = useCallback(async () => {
        setIsLoading(true);
        setError(null);
        try {
            const response = await client.listTools({ category, serverId });
            setTools(response.data);
        }
        catch (err) {
            const apiError = err;
            setError(apiError);
        }
        finally {
            setIsLoading(false);
        }
    }, [client, category, serverId]);
    /**
     * Execute a tool
     */
    const execute = useCallback(async (toolName, params) => {
        const response = await client.executeTool(toolName, params);
        return response.data;
    }, [client]);
    // Initial load
    useEffect(() => {
        refresh();
    }, [refresh]);
    // Auto-refresh interval
    useEffect(() => {
        if (refreshInterval && refreshInterval > 0) {
            const interval = setInterval(refresh, refreshInterval);
            return () => clearInterval(interval);
        }
        return undefined;
    }, [refreshInterval, refresh]);
    return {
        tools,
        execute,
        refresh,
        isLoading,
        error,
    };
}
//# sourceMappingURL=reactHooks.js.map