import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { ToolMessage } from './ToolMessage.js';
import { describe, it, expect, vi } from 'vitest';
import { StreamingState } from '../../types.js';
import { Text } from 'ink';
import { CoreToolCallStatus, Kind, } from '@google/gemini-cli-core';
import { renderWithProviders } from '../../../test-utils/render.js';
import { tryParseJSON } from '../../../utils/jsonoutput.js';
vi.mock('../GeminiRespondingSpinner.js', () => ({
    GeminiRespondingSpinner: () => _jsx(Text, { children: "MockRespondingSpinner" }),
}));
vi.mock('../TerminalOutput.js', () => ({
    TerminalOutput: function MockTerminalOutput({ cursor, }) {
        return (_jsxs(Text, { children: ["MockCursor:(", cursor?.x, ",", cursor?.y, ")"] }));
    },
}));
describe('<ToolMessage />', () => {
    const baseProps = {
        callId: 'tool-123',
        name: 'test-tool',
        description: 'A tool for testing',
        resultDisplay: 'Test result',
        status: CoreToolCallStatus.Success,
        terminalWidth: 80,
        confirmationDetails: undefined,
        emphasis: 'medium',
        isFirst: true,
        borderColor: 'green',
        borderDimColor: false,
    };
    const mockSetEmbeddedShellFocused = vi.fn();
    const uiActions = {
        setEmbeddedShellFocused: mockSetEmbeddedShellFocused,
    };
    // Helper to render with context
    const renderWithContext = (ui, streamingState) => renderWithProviders(ui, {
        uiActions,
        uiState: { streamingState },
        width: 80,
    });
    beforeEach(() => {
        vi.clearAllMocks();
        vi.useFakeTimers();
    });
    afterEach(() => {
        vi.useRealTimers();
    });
    it('renders basic tool information', async () => {
        const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps }), StreamingState.Idle);
        await waitUntilReady();
        const output = lastFrame();
        expect(output).toMatchSnapshot();
        unmount();
    });
    describe('JSON rendering', () => {
        it('pretty prints valid JSON', async () => {
            const testJSONstring = '{"a": 1, "b": [2, 3]}';
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, resultDisplay: testJSONstring, renderOutputAsMarkdown: false }), StreamingState.Idle);
            await waitUntilReady();
            const output = lastFrame();
            // Verify the JSON utility correctly parses the input
            expect(tryParseJSON(testJSONstring)).toBeTruthy();
            // Verify pretty-printed JSON appears in output (with proper indentation)
            expect(output).toContain('"a": 1');
            expect(output).toContain('"b": [');
            // Should not use markdown renderer for JSON
            unmount();
        });
        it('renders pretty JSON in ink frame', async () => {
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, resultDisplay: '{"a":1,"b":2}' }), StreamingState.Idle);
            await waitUntilReady();
            const frame = lastFrame();
            expect(frame).toMatchSnapshot();
            unmount();
        });
        it('uses JSON renderer even when renderOutputAsMarkdown=true is true', async () => {
            const testJSONstring = '{"a": 1, "b": [2, 3]}';
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, resultDisplay: testJSONstring, renderOutputAsMarkdown: true }), StreamingState.Idle);
            await waitUntilReady();
            const output = lastFrame();
            // Verify the JSON utility correctly parses the input
            expect(tryParseJSON(testJSONstring)).toBeTruthy();
            // Verify pretty-printed JSON appears in output
            expect(output).toContain('"a": 1');
            expect(output).toContain('"b": [');
            // Should not use markdown renderer for JSON even when renderOutputAsMarkdown=true
            unmount();
        });
        it('falls back to plain text for malformed JSON', async () => {
            const testJSONstring = 'a": 1, "b": [2, 3]}';
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, resultDisplay: testJSONstring, renderOutputAsMarkdown: false }), StreamingState.Idle);
            await waitUntilReady();
            const output = lastFrame();
            expect(tryParseJSON(testJSONstring)).toBeFalsy();
            expect(typeof output === 'string').toBeTruthy();
            unmount();
        });
        it('rejects mixed text + JSON renders as plain text', async () => {
            const testJSONstring = `{"result":  "count": 42,"items": ["apple", "banana"]},"meta": {"timestamp": "2025-09-28T12:34:56Z"}}End.`;
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, resultDisplay: testJSONstring, renderOutputAsMarkdown: false }), StreamingState.Idle);
            await waitUntilReady();
            const output = lastFrame();
            expect(tryParseJSON(testJSONstring)).toBeFalsy();
            expect(typeof output === 'string').toBeTruthy();
            unmount();
        });
        it('rejects ANSI-tained JSON renders as plain text', async () => {
            const testJSONstring = '\u001b[32mOK\u001b[0m {"status": "success", "data": {"id": 123, "values": [10, 20, 30]}}';
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, resultDisplay: testJSONstring, renderOutputAsMarkdown: false }), StreamingState.Idle);
            await waitUntilReady();
            const output = lastFrame();
            expect(tryParseJSON(testJSONstring)).toBeFalsy();
            expect(typeof output === 'string').toBeTruthy();
            unmount();
        });
        it('pretty printing 10kb JSON completes in <50ms', async () => {
            const large = '{"key": "' + 'x'.repeat(10000) + '"}';
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, resultDisplay: large, renderOutputAsMarkdown: false }), StreamingState.Idle);
            await waitUntilReady();
            const start = performance.now();
            lastFrame();
            expect(performance.now() - start).toBeLessThan(50);
            unmount();
        });
    });
    describe('ToolStatusIndicator rendering', () => {
        it('shows ✓ for Success status', async () => {
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.Success }), StreamingState.Idle);
            await waitUntilReady();
            expect(lastFrame()).toMatchSnapshot();
            unmount();
        });
        it('shows o for Pending status', async () => {
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.Scheduled }), StreamingState.Idle);
            await waitUntilReady();
            expect(lastFrame()).toMatchSnapshot();
            unmount();
        });
        it('shows ? for Confirming status', async () => {
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.AwaitingApproval }), StreamingState.Idle);
            await waitUntilReady();
            expect(lastFrame()).toMatchSnapshot();
            unmount();
        });
        it('shows - for Canceled status', async () => {
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.Cancelled }), StreamingState.Idle);
            await waitUntilReady();
            expect(lastFrame()).toMatchSnapshot();
            unmount();
        });
        it('shows x for Error status', async () => {
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.Error }), StreamingState.Idle);
            await waitUntilReady();
            expect(lastFrame()).toMatchSnapshot();
            unmount();
        });
        it('shows paused spinner for Executing status when streamingState is Idle', async () => {
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.Executing }), StreamingState.Idle);
            await waitUntilReady();
            expect(lastFrame()).toMatchSnapshot();
            unmount();
        });
        it('shows paused spinner for Executing status when streamingState is WaitingForConfirmation', async () => {
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.Executing }), StreamingState.WaitingForConfirmation);
            await waitUntilReady();
            expect(lastFrame()).toMatchSnapshot();
            unmount();
        });
        it('shows MockRespondingSpinner for Executing status when streamingState is Responding', async () => {
            const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.Executing }), StreamingState.Responding);
            await waitUntilReady();
            expect(lastFrame()).toMatchSnapshot();
            unmount();
        });
    });
    it('renders DiffRenderer for diff results', async () => {
        const diffResult = {
            fileDiff: '--- a/file.txt\n+++ b/file.txt\n@@ -1 +1 @@\n-old\n+new',
            fileName: 'file.txt',
            originalContent: 'old',
            newContent: 'new',
            filePath: 'file.txt',
        };
        const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, resultDisplay: diffResult }), StreamingState.Idle);
        await waitUntilReady();
        // Check that the output contains the MockDiff content as part of the whole message
        expect(lastFrame()).toMatchSnapshot();
        unmount();
    });
    it('renders emphasis correctly', async () => {
        const { lastFrame: highEmphasisFrame, waitUntilReady: waitUntilReadyHigh, unmount: unmountHigh, } = renderWithContext(_jsx(ToolMessage, { ...baseProps, emphasis: "high" }), StreamingState.Idle);
        await waitUntilReadyHigh();
        // Check for trailing indicator or specific color if applicable (Colors are not easily testable here)
        expect(highEmphasisFrame()).toMatchSnapshot();
        unmountHigh();
        const { lastFrame: lowEmphasisFrame, waitUntilReady: waitUntilReadyLow, unmount: unmountLow, } = renderWithContext(_jsx(ToolMessage, { ...baseProps, emphasis: "low" }), StreamingState.Idle);
        await waitUntilReadyLow();
        // For low emphasis, the name and description might be dimmed (check for dimColor if possible)
        // This is harder to assert directly in text output without color checks.
        // We can at least ensure it doesn't have the high emphasis indicator.
        expect(lowEmphasisFrame()).toMatchSnapshot();
        unmountLow();
    });
    it('renders AnsiOutputText for AnsiOutput results', async () => {
        const ansiResult = [
            [
                {
                    text: 'hello',
                    fg: '#ffffff',
                    bg: '#000000',
                    bold: false,
                    italic: false,
                    underline: false,
                    dim: false,
                    inverse: false,
                },
            ],
        ];
        const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, resultDisplay: ansiResult }), StreamingState.Idle);
        await waitUntilReady();
        expect(lastFrame()).toMatchSnapshot();
        unmount();
    });
    it('renders McpProgressIndicator with percentage and message for executing tools', async () => {
        const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.Executing, progress: 42, progressTotal: 100, progressMessage: "Working on it..." }), StreamingState.Responding);
        await waitUntilReady();
        const output = lastFrame();
        expect(output).toContain('42%');
        expect(output).toContain('Working on it...');
        expect(output).toContain('\u2588');
        expect(output).toContain('\u2591');
        expect(output).not.toContain('A tool for testing (Working on it... - 42%)');
        expect(output).toMatchSnapshot();
        unmount();
    });
    it('renders only percentage when progressMessage is missing', async () => {
        const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.Executing, progress: 75, progressTotal: 100 }), StreamingState.Responding);
        await waitUntilReady();
        const output = lastFrame();
        expect(output).toContain('75%');
        expect(output).toContain('\u2588');
        expect(output).toContain('\u2591');
        expect(output).not.toContain('A tool for testing (75%)');
        expect(output).toMatchSnapshot();
        unmount();
    });
    it('renders indeterminate progress when total is missing', async () => {
        const { lastFrame, waitUntilReady, unmount } = renderWithContext(_jsx(ToolMessage, { ...baseProps, status: CoreToolCallStatus.Executing, progress: 7 }), StreamingState.Responding);
        await waitUntilReady();
        const output = lastFrame();
        expect(output).toContain('7');
        expect(output).toContain('\u2588');
        expect(output).toContain('\u2591');
        expect(output).not.toContain('%');
        expect(output).toMatchSnapshot();
        unmount();
    });
    describe('Truncation', () => {
        it('applies truncation for Kind.Agent when availableTerminalHeight is provided', async () => {
            const multilineString = Array.from({ length: 30 }, (_, i) => `Line ${i + 1}`).join('\n');
            const { lastFrame, waitUntilReady, unmount } = renderWithProviders(_jsx(ToolMessage, { ...baseProps, kind: Kind.Agent, resultDisplay: multilineString, renderOutputAsMarkdown: false, availableTerminalHeight: 40 }), {
                uiActions,
                uiState: {
                    streamingState: StreamingState.Idle,
                    constrainHeight: true,
                },
                width: 80,
                useAlternateBuffer: false,
            });
            await waitUntilReady();
            const output = lastFrame();
            // Since kind=Kind.Agent and availableTerminalHeight is provided, it should truncate to SUBAGENT_MAX_LINES (15)
            // and show the FIRST lines (overflowDirection='bottom')
            expect(output).toContain('Line 1');
            expect(output).toContain('Line 14');
            expect(output).not.toContain('Line 16');
            expect(output).not.toContain('Line 30');
            unmount();
        });
        it('does NOT apply truncation for Kind.Agent when availableTerminalHeight is undefined', async () => {
            const multilineString = Array.from({ length: 30 }, (_, i) => `Line ${i + 1}`).join('\n');
            const { lastFrame, waitUntilReady, unmount } = renderWithProviders(_jsx(ToolMessage, { ...baseProps, kind: Kind.Agent, resultDisplay: multilineString, renderOutputAsMarkdown: false, availableTerminalHeight: undefined }), {
                uiActions,
                uiState: { streamingState: StreamingState.Idle },
                width: 80,
                useAlternateBuffer: false,
            });
            await waitUntilReady();
            const output = lastFrame();
            expect(output).toContain('Line 1');
            expect(output).toContain('Line 30');
            unmount();
        });
        it('does NOT apply truncation for Kind.Read', async () => {
            const multilineString = Array.from({ length: 30 }, (_, i) => `Line ${i + 1}`).join('\n');
            const { lastFrame, waitUntilReady, unmount } = renderWithProviders(_jsx(ToolMessage, { ...baseProps, kind: Kind.Read, resultDisplay: multilineString, renderOutputAsMarkdown: false }), {
                uiActions,
                uiState: { streamingState: StreamingState.Idle },
                width: 80,
                useAlternateBuffer: false,
            });
            await waitUntilReady();
            const output = lastFrame();
            expect(output).toContain('Line 1');
            expect(output).toContain('Line 30');
            unmount();
        });
    });
});
//# sourceMappingURL=ToolMessage.test.js.map