// ==========================================
// ChatPane Kit — useStream Hook
// ==========================================
// SSE ReadableStream parser with rAF-batched state updates.
// Independently importable — no ChatPane dependency.
//
// Usage:
//   const { streamContent, streaming, startStream, stopStream } = useStream();
//   // Pass a Response from sw.api.channels.complete():
//   startStream(response);

const { useState, useRef, useCallback } = window.hooks;

/**
 * Hook that parses an SSE stream from a fetch Response.
 *
 * @returns {{ streamContent: string, streaming: boolean, startStream: (resp: Response) => void, stopStream: () => void }}
 */
export function useStream() {
    const [streamContent, setStreamContent] = useState('');
    const [streamThinking, setStreamThinking] = useState('');
    const [streaming, setStreaming] = useState(false);
    const contentRef = useRef('');
    const thinkingRef = useRef('');
    const abortRef = useRef(null);
    const rafRef = useRef(null);

    const _flush = useCallback(() => {
        setStreamContent(contentRef.current);
        setStreamThinking(thinkingRef.current);
        rafRef.current = null;
    }, []);

    const _scheduleFlush = useCallback(() => {
        if (!rafRef.current) {
            rafRef.current = requestAnimationFrame(_flush);
        }
    }, [_flush]);

    const stopStream = useCallback(() => {
        if (abortRef.current) {
            abortRef.current.abort();
            abortRef.current = null;
        }
        if (rafRef.current) {
            cancelAnimationFrame(rafRef.current);
            rafRef.current = null;
        }
        setStreamContent(contentRef.current);
        setStreamThinking(thinkingRef.current);
        setStreaming(false);
    }, []);

    const startStream = useCallback(async (response) => {
        // Reset
        contentRef.current = '';
        thinkingRef.current = '';
        setStreamContent('');
        setStreamThinking('');
        setStreaming(true);

        const controller = new AbortController();
        abortRef.current = controller;

        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let buffer = '';

        try {
            while (true) {
                const { done, value } = await reader.read();
                if (done) break;
                if (controller.signal.aborted) break;

                buffer += decoder.decode(value, { stream: true });
                const lines = buffer.split('\n');
                buffer = lines.pop() || '';

                for (const line of lines) {
                    if (!line.startsWith('data: ')) continue;
                    const data = line.slice(6);
                    if (data === '[DONE]') continue;
                    try {
                        const parsed = JSON.parse(data).choices?.[0]?.delta;
                        if (parsed?.content) {
                            let chunk = parsed.content;
                            // Run pipe stream filters (extensions can transform or suppress chunks)
                            if (window.sw?.pipe?._runStream) {
                                const pipeCtx = window.sw.pipe._runStream({ content: chunk });
                                if (pipeCtx === null) continue;
                                chunk = pipeCtx.content;
                            }
                            contentRef.current += chunk;
                            _scheduleFlush();
                        }
                        if (parsed?.reasoning_content) {
                            thinkingRef.current += parsed.reasoning_content;
                            _scheduleFlush();
                        }
                    } catch (_) { /* skip malformed JSON */ }
                }
            }
        } catch (e) {
            if (e.name !== 'AbortError') {
                contentRef.current += '\n\n**[Stream error: ' + e.message + ']**';
            }
        }

        // Final flush
        if (rafRef.current) {
            cancelAnimationFrame(rafRef.current);
            rafRef.current = null;
        }
        setStreamContent(contentRef.current);
        setStreamThinking(thinkingRef.current);
        setStreaming(false);
        abortRef.current = null;
    }, [_scheduleFlush]);

    return { streamContent, streamThinking, streaming, startStream, stopStream };
}
