/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { useCallback, useEffect, useReducer, useRef, startTransition, } from 'react';
import { coreEvents, CoreEvent, } from '@google/gemini-cli-core';
function consoleMessagesReducer(state, action) {
    const MAX_CONSOLE_MESSAGES = 1000;
    switch (action.type) {
        case 'ADD_MESSAGES': {
            const newMessages = [...state];
            for (const queuedMessage of action.payload) {
                const lastMessage = newMessages[newMessages.length - 1];
                if (lastMessage &&
                    lastMessage.type === queuedMessage.type &&
                    lastMessage.content === queuedMessage.content) {
                    // Create a new object for the last message to ensure React detects
                    // the change, preventing mutation of the existing state object.
                    newMessages[newMessages.length - 1] = {
                        ...lastMessage,
                        count: lastMessage.count + 1,
                    };
                }
                else {
                    newMessages.push({ ...queuedMessage, count: 1 });
                }
            }
            // Limit the number of messages to prevent memory issues
            if (newMessages.length > MAX_CONSOLE_MESSAGES) {
                return newMessages.slice(newMessages.length - MAX_CONSOLE_MESSAGES);
            }
            return newMessages;
        }
        case 'CLEAR':
            return [];
        default:
            return state;
    }
}
export function useConsoleMessages() {
    const [consoleMessages, dispatch] = useReducer(consoleMessagesReducer, []);
    const messageQueueRef = useRef([]);
    const timeoutRef = useRef(null);
    const isProcessingRef = useRef(false);
    const processQueue = useCallback(() => {
        if (messageQueueRef.current.length > 0) {
            isProcessingRef.current = true;
            const messagesToProcess = messageQueueRef.current;
            messageQueueRef.current = [];
            startTransition(() => {
                dispatch({ type: 'ADD_MESSAGES', payload: messagesToProcess });
            });
        }
        timeoutRef.current = null;
    }, []);
    const handleNewMessage = useCallback((message) => {
        messageQueueRef.current.push(message);
        if (!isProcessingRef.current && !timeoutRef.current) {
            // Batch updates using a timeout. 50ms is a reasonable delay to batch
            // rapid-fire messages without noticeable lag while avoiding React update
            // queue flooding.
            timeoutRef.current = setTimeout(processQueue, 50);
        }
    }, [processQueue]);
    // Once the updated consoleMessages have been committed to the screen,
    // we can safely process the next batch of queued messages if any exist.
    // This completely eliminates overlapping concurrent updates to this state.
    useEffect(() => {
        isProcessingRef.current = false;
        if (messageQueueRef.current.length > 0 && !timeoutRef.current) {
            timeoutRef.current = setTimeout(processQueue, 50);
        }
    }, [consoleMessages, processQueue]);
    useEffect(() => {
        const handleConsoleLog = (payload) => {
            let content = payload.content;
            const MAX_CONSOLE_MSG_LENGTH = 10000;
            if (content.length > MAX_CONSOLE_MSG_LENGTH) {
                content =
                    content.slice(0, MAX_CONSOLE_MSG_LENGTH) +
                        `... [Truncated ${content.length - MAX_CONSOLE_MSG_LENGTH} characters]`;
            }
            handleNewMessage({
                type: payload.type,
                content,
                count: 1,
            });
        };
        const handleOutput = (payload) => {
            let content = typeof payload.chunk === 'string'
                ? payload.chunk
                : new TextDecoder().decode(payload.chunk);
            const MAX_OUTPUT_CHUNK_LENGTH = 10000;
            if (content.length > MAX_OUTPUT_CHUNK_LENGTH) {
                content =
                    content.slice(0, MAX_OUTPUT_CHUNK_LENGTH) +
                        `... [Truncated ${content.length - MAX_OUTPUT_CHUNK_LENGTH} characters]`;
            }
            // It would be nice if we could show stderr as 'warn' but unfortunately
            // we log non warning info to stderr before the app starts so that would
            // be misleading.
            handleNewMessage({ type: 'log', content, count: 1 });
        };
        coreEvents.on(CoreEvent.ConsoleLog, handleConsoleLog);
        coreEvents.on(CoreEvent.Output, handleOutput);
        return () => {
            coreEvents.off(CoreEvent.ConsoleLog, handleConsoleLog);
            coreEvents.off(CoreEvent.Output, handleOutput);
        };
    }, [handleNewMessage]);
    const clearConsoleMessages = useCallback(() => {
        if (timeoutRef.current) {
            clearTimeout(timeoutRef.current);
            timeoutRef.current = null;
        }
        messageQueueRef.current = [];
        isProcessingRef.current = true;
        startTransition(() => {
            dispatch({ type: 'CLEAR' });
        });
    }, []);
    // Cleanup on unmount
    useEffect(() => () => {
        if (timeoutRef.current) {
            clearTimeout(timeoutRef.current);
        }
    }, []);
    return { consoleMessages, clearConsoleMessages };
}
//# sourceMappingURL=useConsoleMessages.js.map