import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { Box, Text } from 'ink';
import { useEffect, useState, useRef } from 'react';
import { useUIActions } from '../contexts/UIActionsContext.js';
import { theme } from '../semantic-colors.js';
import { ShellExecutionService, } from '@google/gemini-cli-core';
import { cpLen, cpSlice, getCachedStringWidth } from '../utils/textUtils.js';
import {} from '../hooks/shellCommandProcessor.js';
import { Command, keyMatchers } from '../keyMatchers.js';
import { useKeypress } from '../hooks/useKeypress.js';
import { formatCommand } from '../utils/keybindingUtils.js';
import { ScrollableList, } from './shared/ScrollableList.js';
import { SCROLL_TO_ITEM_END } from './shared/VirtualizedList.js';
import { RadioButtonSelect, } from './shared/RadioButtonSelect.js';
const CONTENT_PADDING_X = 1;
const BORDER_WIDTH = 2; // Left and Right border
const HEADER_HEIGHT = 3; // 2 for border, 1 for header
const TAB_DISPLAY_HORIZONTAL_PADDING = 4;
const formatShellCommandForDisplay = (command, maxWidth) => {
    const commandFirstLine = command.split('\n')[0];
    return cpLen(commandFirstLine) > maxWidth
        ? `${cpSlice(commandFirstLine, 0, maxWidth - 3)}...`
        : commandFirstLine;
};
export const BackgroundShellDisplay = ({ shells, activePid, width, height, isFocused, isListOpenProp, }) => {
    const { dismissBackgroundShell, setActiveBackgroundShellPid, setIsBackgroundShellListOpen, } = useUIActions();
    const activeShell = shells.get(activePid);
    const [output, setOutput] = useState(activeShell?.output || '');
    const [highlightedPid, setHighlightedPid] = useState(activePid);
    const outputRef = useRef(null);
    const subscribedRef = useRef(false);
    useEffect(() => {
        if (!activePid)
            return;
        const ptyWidth = Math.max(1, width - BORDER_WIDTH - CONTENT_PADDING_X * 2);
        const ptyHeight = Math.max(1, height - HEADER_HEIGHT);
        ShellExecutionService.resizePty(activePid, ptyWidth, ptyHeight);
    }, [activePid, width, height]);
    useEffect(() => {
        if (!activePid) {
            setOutput('');
            return;
        }
        // Set initial output from the shell object
        const shell = shells.get(activePid);
        if (shell) {
            setOutput(shell.output);
        }
        subscribedRef.current = false;
        // Subscribe to live updates for the active shell
        const unsubscribe = ShellExecutionService.subscribe(activePid, (event) => {
            if (event.type === 'data') {
                if (typeof event.chunk === 'string') {
                    if (!subscribedRef.current) {
                        // Initial synchronous update contains full history
                        setOutput(event.chunk);
                    }
                    else {
                        // Subsequent updates are deltas for child_process
                        setOutput((prev) => typeof prev === 'string' ? prev + event.chunk : event.chunk);
                    }
                }
                else {
                    // PTY always sends full AnsiOutput
                    setOutput(event.chunk);
                }
            }
        });
        subscribedRef.current = true;
        return () => {
            unsubscribe();
            subscribedRef.current = false;
        };
    }, [activePid, shells]);
    // Sync highlightedPid with activePid when list opens
    useEffect(() => {
        if (isListOpenProp) {
            setHighlightedPid(activePid);
        }
    }, [isListOpenProp, activePid]);
    useKeypress((key) => {
        if (!activeShell)
            return;
        if (isListOpenProp) {
            // Navigation (Up/Down/Enter) is handled by RadioButtonSelect
            // We only handle special keys not consumed by RadioButtonSelect or overriding them if needed
            // RadioButtonSelect handles Enter -> onSelect
            if (keyMatchers[Command.BACKGROUND_SHELL_ESCAPE](key)) {
                setIsBackgroundShellListOpen(false);
                return true;
            }
            if (keyMatchers[Command.KILL_BACKGROUND_SHELL](key)) {
                if (highlightedPid) {
                    dismissBackgroundShell(highlightedPid);
                    // If we killed the active one, the list might update via props
                }
                return true;
            }
            if (keyMatchers[Command.TOGGLE_BACKGROUND_SHELL_LIST](key)) {
                if (highlightedPid) {
                    setActiveBackgroundShellPid(highlightedPid);
                }
                setIsBackgroundShellListOpen(false);
                return true;
            }
            return false;
        }
        if (keyMatchers[Command.TOGGLE_BACKGROUND_SHELL](key)) {
            return false;
        }
        if (keyMatchers[Command.KILL_BACKGROUND_SHELL](key)) {
            dismissBackgroundShell(activeShell.pid);
            return true;
        }
        if (keyMatchers[Command.TOGGLE_BACKGROUND_SHELL_LIST](key)) {
            setIsBackgroundShellListOpen(true);
            return true;
        }
        if (keyMatchers[Command.BACKGROUND_SHELL_SELECT](key)) {
            ShellExecutionService.writeToPty(activeShell.pid, '\r');
            return true;
        }
        else if (keyMatchers[Command.DELETE_CHAR_LEFT](key)) {
            ShellExecutionService.writeToPty(activeShell.pid, '\b');
            return true;
        }
        else if (key.sequence) {
            ShellExecutionService.writeToPty(activeShell.pid, key.sequence);
            return true;
        }
        return false;
    }, { isActive: isFocused && !!activeShell });
    const helpTextParts = [
        { label: 'Close', command: Command.TOGGLE_BACKGROUND_SHELL },
        { label: 'Kill', command: Command.KILL_BACKGROUND_SHELL },
        { label: 'List', command: Command.TOGGLE_BACKGROUND_SHELL_LIST },
    ];
    const helpTextStr = helpTextParts
        .map((p) => `${p.label} (${formatCommand(p.command)})`)
        .join(' | ');
    const renderHelpText = () => (_jsx(Text, { children: helpTextParts.map((p, i) => (_jsxs(Text, { children: [i > 0 ? ' | ' : '', p.label, " (", _jsx(Text, { color: theme.text.accent, children: formatCommand(p.command) }), ")"] }, p.label))) }));
    const renderTabs = () => {
        const shellList = Array.from(shells.values()).filter((s) => s.status === 'running');
        const pidInfoWidth = getCachedStringWidth(` (PID: ${activePid}) ${isFocused ? '(Focused)' : ''}`);
        const availableWidth = width -
            TAB_DISPLAY_HORIZONTAL_PADDING -
            getCachedStringWidth(helpTextStr) -
            pidInfoWidth;
        let currentWidth = 0;
        const tabs = [];
        for (let i = 0; i < shellList.length; i++) {
            const shell = shellList[i];
            // Account for " i: " (length 4 if i < 9) and spaces (length 2)
            const labelOverhead = 4 + (i + 1).toString().length;
            const maxTabLabelLength = Math.max(1, Math.floor(availableWidth / shellList.length) - labelOverhead);
            const truncatedCommand = formatShellCommandForDisplay(shell.command, maxTabLabelLength);
            const label = ` ${i + 1}: ${truncatedCommand} `;
            const labelWidth = getCachedStringWidth(label);
            // If this is the only shell, we MUST show it (truncated if necessary)
            // even if it exceeds availableWidth, as there are no alternatives.
            if (i > 0 && currentWidth + labelWidth > availableWidth) {
                break;
            }
            const isActive = shell.pid === activePid;
            tabs.push(_jsx(Text, { color: isActive ? theme.text.primary : theme.text.secondary, bold: isActive, children: label }, shell.pid));
            currentWidth += labelWidth;
        }
        if (shellList.length > tabs.length && !isListOpenProp) {
            const overflowLabel = ` ... (${formatCommand(Command.TOGGLE_BACKGROUND_SHELL_LIST)}) `;
            const overflowWidth = getCachedStringWidth(overflowLabel);
            // If we only have one tab, ensure we don't show the overflow if it's too cramped
            // We want at least 10 chars for the overflow or we favor the first tab.
            const shouldShowOverflow = tabs.length > 1 || availableWidth - currentWidth >= overflowWidth;
            if (shouldShowOverflow) {
                tabs.push(_jsx(Text, { color: theme.status.warning, bold: true, children: overflowLabel }, "overflow"));
            }
        }
        return tabs;
    };
    const renderProcessList = () => {
        const maxCommandLength = Math.max(0, width - BORDER_WIDTH - CONTENT_PADDING_X * 2 - 10);
        const items = Array.from(shells.values()).map((shell, index) => {
            const truncatedCommand = formatShellCommandForDisplay(shell.command, maxCommandLength);
            let label = `${index + 1}: ${truncatedCommand} (PID: ${shell.pid})`;
            if (shell.status === 'exited') {
                label += ` (Exit Code: ${shell.exitCode})`;
            }
            return {
                key: shell.pid.toString(),
                value: shell.pid,
                label,
            };
        });
        const initialIndex = items.findIndex((item) => item.value === activePid);
        return (_jsxs(Box, { flexDirection: "column", height: "100%", width: "100%", children: [_jsx(Box, { flexShrink: 0, marginBottom: 1, paddingTop: 1, children: _jsx(Text, { bold: true, children: `Select Process (${formatCommand(Command.BACKGROUND_SHELL_SELECT)} to select, ${formatCommand(Command.KILL_BACKGROUND_SHELL)} to kill, ${formatCommand(Command.BACKGROUND_SHELL_ESCAPE)} to cancel):` }) }), _jsx(Box, { flexGrow: 1, width: "100%", children: _jsx(RadioButtonSelect, { items: items, initialIndex: initialIndex >= 0 ? initialIndex : 0, onSelect: (pid) => {
                            setActiveBackgroundShellPid(pid);
                            setIsBackgroundShellListOpen(false);
                        }, onHighlight: (pid) => setHighlightedPid(pid), isFocused: isFocused, maxItemsToShow: Math.max(1, height - HEADER_HEIGHT - 3), renderItem: (item, { isSelected: _isSelected, titleColor: _titleColor }) => {
                            // Custom render to handle exit code coloring if needed,
                            // or just use default. The default RadioButtonSelect renderer
                            // handles standard label.
                            // But we want to color exit code differently?
                            // The previous implementation colored exit code green/red.
                            // Let's reimplement that.
                            // We need access to shell details here.
                            // We can put shell details in the item or lookup.
                            // Lookup from shells map.
                            const shell = shells.get(item.value);
                            if (!shell)
                                return _jsx(Text, { children: item.label });
                            const truncatedCommand = formatShellCommandForDisplay(shell.command, maxCommandLength);
                            return (_jsxs(Text, { children: [truncatedCommand, " (PID: ", shell.pid, ")", shell.status === 'exited' ? (_jsxs(Text, { color: shell.exitCode === 0
                                            ? theme.status.success
                                            : theme.status.error, children: [' ', "(Exit Code: ", shell.exitCode, ")"] })) : null] }));
                        } }) })] }));
    };
    const renderOutput = () => {
        const lines = typeof output === 'string' ? output.split('\n') : output;
        return (_jsx(ScrollableList, { ref: outputRef, data: lines, renderItem: ({ item: line, index }) => {
                if (typeof line === 'string') {
                    return _jsx(Text, { children: line }, index);
                }
                return (_jsx(Text, { wrap: "truncate", children: line.length > 0
                        ? line.map((token, tokenIndex) => (_jsx(Text, { color: token.fg, backgroundColor: token.bg, inverse: token.inverse, dimColor: token.dim, bold: token.bold, italic: token.italic, underline: token.underline, children: token.text }, tokenIndex)))
                        : null }, index));
            }, estimatedItemHeight: () => 1, keyExtractor: (_, index) => index.toString(), hasFocus: isFocused, initialScrollIndex: SCROLL_TO_ITEM_END }));
    };
    return (_jsxs(Box, { flexDirection: "column", height: "100%", width: "100%", borderStyle: "single", borderColor: isFocused ? theme.border.focused : undefined, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", borderStyle: "single", borderBottom: false, borderLeft: false, borderRight: false, borderTop: false, paddingX: 1, borderColor: isFocused ? theme.border.focused : undefined, children: [_jsxs(Box, { flexDirection: "row", children: [renderTabs(), _jsxs(Text, { bold: true, children: [' ', "(PID: ", activeShell?.pid, ") ", isFocused ? '(Focused)' : ''] })] }), renderHelpText()] }), _jsx(Box, { flexGrow: 1, overflow: "hidden", paddingX: CONTENT_PADDING_X, children: isListOpenProp ? renderProcessList() : renderOutput() })] }));
};
//# sourceMappingURL=BackgroundShellDisplay.js.map