import { jsx, Fragment } from 'react/jsx-runtime';
import React__default, { createContext, useRef, useMemo, useContext } from 'react';
import { motion } from 'motion/react';
import { inlineTokenTransition, inlineTokenFadeIn } from './StreamingResponse.animations.js';

const WordAnimationContext = createContext(null);
const useWordAnimation = () => useContext(WordAnimationContext);
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Split a string into word tokens, preserving trailing whitespace on each */
const tokenize = (text) => {
    if (!text)
        return [];
    return text.match(/\S+\s*/g) || [];
};
/** Count prose words in a React children tree (recursive) */
const countWordsInChildren = (children) => {
    let count = 0;
    React__default.Children.forEach(children, (child) => {
        if (typeof child === "string") {
            count += tokenize(child).length;
        }
        else if (React__default.isValidElement(child)) {
            const props = child.props;
            if (props.children) {
                count += countWordsInChildren(props.children);
            }
        }
    });
    return count;
};
const WordAnimationProvider = ({ children, content, enabled, animateLastN = 12, hasCursor = false, }) => {
    const settledCountRef = useRef(0);
    // Count total prose words in the raw content
    const totalWords = useMemo(() => {
        // Strip the cursor character before counting
        const cleanContent = hasCursor ? content.replace(/\s*▏\s*$/, "") : content;
        return tokenize(cleanContent).length;
    }, [content, hasCursor]);
    // Update settled count — words that have aged out of the animation window
    if (totalWords > settledCountRef.current + animateLastN) {
        settledCountRef.current = totalWords - animateLastN;
    }
    // Mutable counter — reset each render, incremented as text nodes are processed
    const wordIndexRef = useRef(0);
    wordIndexRef.current = 0;
    const settledCount = settledCountRef.current;
    const contextValue = useMemo(() => {
        if (!enabled) {
            return {
                processChildren: (children) => children,
                skipChildren: () => { },
            };
        }
        /**
         * Process React children, wrapping recent words in motion.span
         * Preserves element structure (strong, em, a, etc.) by recursing into them
         */
        const processChildren = (children) => {
            return React__default.Children.map(children, (child) => {
                if (typeof child === "string") {
                    return processTextNode(child);
                }
                if (React__default.isValidElement(child)) {
                    const props = child.props;
                    if (props.children) {
                        // Recurse into inline elements (strong, em, a, del, code, etc.)
                        const processed = processChildren(props.children);
                        return React__default.cloneElement(child, {}, processed);
                    }
                }
                return child;
            });
        };
        /**
         * Process a text string node — split into words, settled words become plain,
         * recent words become motion.span with fade-in
         */
        const processTextNode = (text) => {
            const tokens = tokenize(text);
            if (tokens.length === 0)
                return text;
            const result = [];
            let staticBuffer = "";
            for (const token of tokens) {
                const currentIndex = wordIndexRef.current;
                wordIndexRef.current++;
                // Check if this is the cursor character
                if (token.trim() === "▏") {
                    // Flush static buffer
                    if (staticBuffer) {
                        result.push(staticBuffer);
                        staticBuffer = "";
                    }
                    result.push(jsx("span", { className: "animate-pulse font-light", "aria-hidden": "true", children: "\u258F" }, "streaming-cursor"));
                    continue;
                }
                if (currentIndex < settledCount) {
                    // Settled word — accumulate into static string
                    staticBuffer += token;
                }
                else {
                    // Flush static buffer before animated tokens
                    if (staticBuffer) {
                        result.push(staticBuffer);
                        staticBuffer = "";
                    }
                    // Animated word
                    result.push(jsx(motion.span, { variants: inlineTokenFadeIn, initial: "hidden", animate: "visible", transition: inlineTokenTransition, style: { display: "inline" }, children: token }, `word-${currentIndex}-${token.trim()}`));
                }
            }
            // Flush any remaining static buffer
            if (staticBuffer) {
                result.push(staticBuffer);
            }
            return result.length === 1 ? result[0] : jsx(Fragment, { children: result });
        };
        /**
         * Count words in children without producing animated output.
         * Used for non-prose elements (code blocks, tables) to keep counter in sync.
         */
        const skipChildren = (children) => {
            const count = countWordsInChildren(children);
            wordIndexRef.current += count;
        };
        return { processChildren, skipChildren };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [enabled, settledCount, totalWords]);
    return (jsx(WordAnimationContext.Provider, { value: contextValue, children: children }));
};
WordAnimationProvider.displayName = "WordAnimationProvider";

export { WordAnimationProvider, useWordAnimation };
