import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
 * @license
 * Copyright 2025 Vybestack LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import React, { useState } from 'react';
import { Box, Text } from 'ink';
import { ToolCallStatus } from '../../types.js';
import { DiffRenderer } from './DiffRenderer.js';
import { Colors } from '../../colors.js';
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
import { GeminiRespondingSpinner } from '../GeminiRespondingSpinner.js';
import { MaxSizedBox } from '../shared/MaxSizedBox.js';
import { SHELL_COMMAND_NAME, SHELL_NAME, TOOL_STATUS, } from '../../constants.js';
import { useKeypress } from '../../hooks/useKeypress.js';
import { stripShellMarkers } from '@vybestack/llxprt-code-core';
const STATIC_HEIGHT = 1;
const RESERVED_LINE_COUNT = 5; // for tool name, status, padding etc.
const STATUS_INDICATOR_WIDTH = 3;
const MIN_LINES_SHOWN = 2; // show at least this many lines
// Large threshold to ensure we don't cause performance issues for very large
// outputs that will get truncated further MaxSizedBox anyway.
const MAXIMUM_RESULT_DISPLAY_CHARACTERS = 1000000;
export const ToolMessage = ({ name, description, resultDisplay, status, availableTerminalHeight, terminalWidth, emphasis = 'medium', renderOutputAsMarkdown = true, isFocused = true, }) => {
    const availableHeight = availableTerminalHeight
        ? Math.max(availableTerminalHeight - STATIC_HEIGHT - RESERVED_LINE_COUNT, MIN_LINES_SHOWN + 1)
        : undefined;
    // Long tool call response in MarkdownDisplay doesn't respect availableTerminalHeight properly,
    // we're forcing it to not render as markdown when the response is too long, it will fallback
    // to render as plain text, which is contained within the terminal using MaxSizedBox
    if (availableHeight) {
        renderOutputAsMarkdown = false;
    }
    const [isDetailsVisible, setIsDetailsVisible] = useState(false);
    useKeypress((key) => {
        // Handle 'ctrl' + 'r' key for full-command toggle while executing
        if (key.ctrl && key.name === 'r') {
            if (status === ToolCallStatus.Executing) {
                setIsDetailsVisible((prev) => !prev);
            }
        }
    }, { isActive: isFocused });
    const childWidth = terminalWidth - 3; // account for padding.
    if (typeof resultDisplay === 'string') {
        if (resultDisplay.length > MAXIMUM_RESULT_DISPLAY_CHARACTERS) {
            // Truncate the result display to fit within the available width.
            resultDisplay =
                '...' + resultDisplay.slice(-MAXIMUM_RESULT_DISPLAY_CHARACTERS);
        }
    }
    // Build a filtered version for visual rendering (hide runtime markers).
    const visualResultDisplay = typeof resultDisplay === 'string'
        ? stripShellMarkers(resultDisplay)
        : resultDisplay;
    // Prefer explicit runtime markers, fallback to best-effort inference for shell chains.
    const deriveCurrentSubcommand = () => {
        if (status !== ToolCallStatus.Executing)
            return null;
        if (!description)
            return null;
        const outputString = typeof resultDisplay === 'string' ? resultDisplay : undefined;
        if (outputString) {
            const lines = outputString
                .split('\n')
                .map((l) => l.trim())
                .filter((l) => l.length > 0);
            for (let i = lines.length - 1; i >= 0; i--) {
                const line = lines[i];
                const marker = '__LLXPRT_CMD__:';
                const idx = line.indexOf(marker);
                if (idx !== -1) {
                    const seg = line.slice(idx + marker.length).trim();
                    if (seg)
                        return seg;
                }
            }
        }
        // Fallback: parse description if no explicit markers found.
        // Limitations: This heuristic assumes:
        // - Simple && chains (does not handle quoted strings, escaped delimiters, or other operators)
        // - Optional echo commands for progress tracking
        // - Standard metadata format in description (' [in dir]' and ' (desc...)')
        // May produce incorrect results for:
        // - Commands with parentheses or brackets in unexpected positions
        // - Non-echo-based command chains
        // - Multiple echo statements with the same text
        // - Complex shell syntax (if/for/while loops, functions, etc.)
        // Strip optional metadata appended by getDescription: ' [in dir]' and ' (desc...)'
        let raw = description;
        const paren = raw.indexOf(' (');
        const bracket = raw.indexOf(' [in ');
        const cut = paren === -1
            ? bracket
            : bracket === -1
                ? paren
                : Math.min(paren, bracket);
        if (cut > 0)
            raw = raw.slice(0, cut);
        const segments = raw
            .split('&&')
            .map((s) => s.trim())
            .filter((s) => s.length > 0);
        if (segments.length === 0)
            return null;
        const lastLine = outputString
            ?.split('\n')
            .map((l) => l.trim())
            .filter((l) => l.length > 0)
            .pop() ?? '';
        const extractEchoText = (cmd) => {
            const m = cmd.match(/^\s*echo\s+(.*)$/i);
            if (!m)
                return null;
            let text = m[1].trim();
            if ((text.startsWith('"') && text.endsWith('"')) ||
                (text.startsWith("'") && text.endsWith("'"))) {
                text = text.slice(1, -1);
            }
            return text;
        };
        let idx = 0;
        if (lastLine) {
            for (let i = 0; i < segments.length; i++) {
                const t = extractEchoText(segments[i]);
                if (t && t === lastLine) {
                    idx = Math.min(i + 1, segments.length - 1);
                }
            }
        }
        return segments[idx] ?? null;
    };
    const currentSubcommand = isDetailsVisible && status === ToolCallStatus.Executing
        ? deriveCurrentSubcommand()
        : null;
    return (_jsxs(Box, { paddingX: 1, paddingY: 0, flexDirection: "column", children: [_jsxs(Box, { minHeight: 1, children: [_jsx(ToolStatusIndicator, { status: status, name: name }), _jsx(ToolInfo, { name: name, status: status, description: description, emphasis: emphasis }), emphasis === 'high' && _jsx(TrailingIndicator, {})] }), status === ToolCallStatus.Executing && !isDetailsVisible && (_jsx(Box, { paddingLeft: STATUS_INDICATOR_WIDTH, marginTop: 1, width: "100%", children: _jsx(Text, { color: Colors.Gray, dimColor: true, children: "Press 'ctrl+r' to show running command" }) })), currentSubcommand && (_jsx(Box, { paddingLeft: STATUS_INDICATOR_WIDTH, marginTop: 1, width: "100%", children: _jsxs(Text, { color: Colors.AccentCyan, children: ["Running: ", _jsx(Text, { color: Colors.Foreground, children: currentSubcommand })] }) })), resultDisplay && (_jsx(Box, { paddingLeft: STATUS_INDICATOR_WIDTH, width: "100%", marginTop: 1, children: _jsxs(Box, { flexDirection: "column", children: [typeof resultDisplay === 'string' && renderOutputAsMarkdown && (_jsx(Box, { flexDirection: "column", children: _jsx(MarkdownDisplay, { text: visualResultDisplay, isPending: false, availableTerminalHeight: availableHeight, terminalWidth: childWidth }) })), typeof resultDisplay === 'string' && !renderOutputAsMarkdown && (_jsx(MaxSizedBox, { maxHeight: availableHeight, maxWidth: childWidth, children: _jsx(Box, { children: _jsx(Text, { color: Colors.Foreground, wrap: "wrap", children: visualResultDisplay }) }) })), typeof resultDisplay !== 'string' && (_jsx(DiffRenderer, { diffContent: resultDisplay.fileDiff, filename: resultDisplay.fileName, availableTerminalHeight: availableHeight, terminalWidth: childWidth }))] }) }))] }));
};
const ToolStatusIndicator = ({ status, name, }) => {
    const isShell = name === SHELL_COMMAND_NAME || name === SHELL_NAME || false;
    const warningColor = isShell ? Colors.Foreground : Colors.AccentYellow;
    return (_jsxs(Box, { minWidth: STATUS_INDICATOR_WIDTH, children: [status === ToolCallStatus.Pending && (_jsx(Text, { color: Colors.AccentGreen, children: TOOL_STATUS.PENDING })), status === ToolCallStatus.Executing && (_jsx(GeminiRespondingSpinner, { spinnerType: "toggle", nonRespondingDisplay: TOOL_STATUS.EXECUTING })), status === ToolCallStatus.Success && (_jsx(Text, { color: Colors.AccentGreen, "aria-label": 'Success:', children: TOOL_STATUS.SUCCESS })), status === ToolCallStatus.Confirming && (_jsx(Text, { color: warningColor, "aria-label": 'Confirming:', children: TOOL_STATUS.CONFIRMING })), status === ToolCallStatus.Canceled && (_jsx(Text, { color: warningColor, "aria-label": 'Canceled:', bold: true, children: TOOL_STATUS.CANCELED })), status === ToolCallStatus.Error && (_jsx(Text, { color: Colors.AccentRed, "aria-label": 'Error:', bold: true, children: TOOL_STATUS.ERROR }))] }));
};
const ToolInfo = ({ name, description, status, emphasis, showFullDescription = false, }) => {
    const nameColor = React.useMemo(() => {
        switch (emphasis) {
            case 'high':
                return Colors.Foreground;
            case 'medium':
                return Colors.Foreground;
            case 'low':
                return Colors.Gray;
            default: {
                const exhaustiveCheck = emphasis;
                return exhaustiveCheck;
            }
        }
    }, [emphasis]);
    return (_jsx(Box, { children: _jsxs(Text, { wrap: showFullDescription ? 'wrap' : 'truncate-end', strikethrough: status === ToolCallStatus.Canceled, children: [_jsx(Text, { color: nameColor, bold: true, children: name }), ' ', _jsx(Text, { color: Colors.Gray, children: description })] }) }));
};
const TrailingIndicator = () => (_jsxs(Text, { color: Colors.Foreground, wrap: "truncate", children: [' ', "\u2190"] }));
//# sourceMappingURL=ToolMessage.js.map