import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { render as inkRender } from 'ink-testing-library';
import { Box } from 'ink';
import { vi } from 'vitest';
import { act, useState } from 'react';
import os from 'node:os';
import { LoadedSettings } from '../config/settings.js';
import { KeypressProvider } from '../ui/contexts/KeypressContext.js';
import { SettingsContext } from '../ui/contexts/SettingsContext.js';
import { ShellFocusContext } from '../ui/contexts/ShellFocusContext.js';
import { UIStateContext } from '../ui/contexts/UIStateContext.js';
import { ConfigContext } from '../ui/contexts/ConfigContext.js';
import { VimModeProvider } from '../ui/contexts/VimModeContext.js';
import { MouseProvider } from '../ui/contexts/MouseContext.js';
import { ScrollProvider } from '../ui/contexts/ScrollProvider.js';
import { StreamingContext } from '../ui/contexts/StreamingContext.js';
import { UIActionsContext, } from '../ui/contexts/UIActionsContext.js';
import { StreamingState } from '../ui/types.js';
import { ToolActionsProvider } from '../ui/contexts/ToolActionsContext.js';
import { AskUserActionsProvider } from '../ui/contexts/AskUserActionsContext.js';
import { makeFakeConfig } from '@google/gemini-cli-core';
import { FakePersistentState } from './persistentStateFake.js';
import { AppContext } from '../ui/contexts/AppContext.js';
export const persistentStateMock = new FakePersistentState();
vi.mock('../utils/persistentState.js', () => ({
    persistentState: persistentStateMock,
}));
vi.mock('../ui/utils/terminalUtils.js', () => ({
    isLowColorDepth: vi.fn(() => false),
    getColorDepth: vi.fn(() => 24),
    isITerm2: vi.fn(() => false),
}));
// Wrapper around ink-testing-library's render that ensures act() is called
export const render = (tree, terminalWidth) => {
    let renderResult = undefined;
    act(() => {
        renderResult = inkRender(tree);
    });
    if (terminalWidth !== undefined && renderResult?.stdout) {
        // Override the columns getter on the stdout instance provided by ink-testing-library
        Object.defineProperty(renderResult.stdout, 'columns', {
            get: () => terminalWidth,
            configurable: true,
        });
        // Trigger a rerender so Ink can pick up the new terminal width
        act(() => {
            renderResult.rerender(tree);
        });
    }
    const originalUnmount = renderResult.unmount;
    const originalRerender = renderResult.rerender;
    return {
        ...renderResult,
        unmount: () => {
            act(() => {
                originalUnmount();
            });
        },
        rerender: (newTree) => {
            act(() => {
                originalRerender(newTree);
            });
        },
    };
};
export const simulateClick = async (stdin, col, row, button = 0) => {
    // Terminal mouse events are 1-based, so convert if necessary.
    const mouseEventString = `\x1b[<${button};${col};${row}M`;
    await act(async () => {
        stdin.write(mouseEventString);
    });
};
let mockConfigInternal;
const getMockConfigInternal = () => {
    if (!mockConfigInternal) {
        mockConfigInternal = makeFakeConfig({
            targetDir: os.tmpdir(),
            enableEventDrivenScheduler: true,
        });
    }
    return mockConfigInternal;
};
const configProxy = new Proxy({}, {
    get(_target, prop) {
        if (prop === 'getTargetDir') {
            return () => '/Users/test/project/foo/bar/and/some/more/directories/to/make/it/long';
        }
        const internal = getMockConfigInternal();
        if (prop in internal) {
            return internal[prop];
        }
        throw new Error(`mockConfig does not have property ${String(prop)}`);
    },
});
export const mockSettings = new LoadedSettings({ path: '', settings: {}, originalSettings: {} }, { path: '', settings: {}, originalSettings: {} }, { path: '', settings: {}, originalSettings: {} }, { path: '', settings: {}, originalSettings: {} }, true, []);
export const createMockSettings = (overrides) => {
    const settings = overrides;
    return new LoadedSettings({ path: '', settings: {}, originalSettings: {} }, { path: '', settings: {}, originalSettings: {} }, { path: '', settings, originalSettings: settings }, { path: '', settings: {}, originalSettings: {} }, true, []);
};
// A minimal mock UIState to satisfy the context provider.
// Tests that need specific UIState values should provide their own.
const baseMockUiState = {
    renderMarkdown: true,
    streamingState: StreamingState.Idle,
    terminalWidth: 120,
    terminalHeight: 40,
    currentModel: 'gemini-pro',
    terminalBackgroundColor: undefined,
};
export const mockAppState = {
    version: '1.2.3',
    startupWarnings: [],
};
const mockUIActions = {
    handleThemeSelect: vi.fn(),
    closeThemeDialog: vi.fn(),
    handleThemeHighlight: vi.fn(),
    handleAuthSelect: vi.fn(),
    setAuthState: vi.fn(),
    onAuthError: vi.fn(),
    handleEditorSelect: vi.fn(),
    exitEditorDialog: vi.fn(),
    exitPrivacyNotice: vi.fn(),
    closeSettingsDialog: vi.fn(),
    closeModelDialog: vi.fn(),
    openAgentConfigDialog: vi.fn(),
    closeAgentConfigDialog: vi.fn(),
    openPermissionsDialog: vi.fn(),
    openSessionBrowser: vi.fn(),
    closeSessionBrowser: vi.fn(),
    handleResumeSession: vi.fn(),
    handleDeleteSession: vi.fn(),
    closePermissionsDialog: vi.fn(),
    setShellModeActive: vi.fn(),
    vimHandleInput: vi.fn(),
    handleIdePromptComplete: vi.fn(),
    handleFolderTrustSelect: vi.fn(),
    setConstrainHeight: vi.fn(),
    onEscapePromptChange: vi.fn(),
    refreshStatic: vi.fn(),
    handleFinalSubmit: vi.fn(),
    handleClearScreen: vi.fn(),
    handleProQuotaChoice: vi.fn(),
    handleValidationChoice: vi.fn(),
    setQueueErrorMessage: vi.fn(),
    popAllMessages: vi.fn(),
    handleApiKeySubmit: vi.fn(),
    handleApiKeyCancel: vi.fn(),
    setBannerVisible: vi.fn(),
    setEmbeddedShellFocused: vi.fn(),
    setAuthContext: vi.fn(),
    handleRestart: vi.fn(),
    handleNewAgentsSelect: vi.fn(),
};
export const renderWithProviders = (component, { shellFocus = true, settings = mockSettings, uiState: providedUiState, width, mouseEventsEnabled = false, config = configProxy, useAlternateBuffer = true, uiActions, persistentState, appState = mockAppState, } = {}) => {
    const baseState = new Proxy({ ...baseMockUiState, ...providedUiState }, {
        get(target, prop) {
            if (prop in target) {
                return target[prop];
            }
            // For properties not in the base mock or provided state,
            // we'll check the original proxy to see if it's a defined but
            // unprovided property, and if not, throw.
            if (prop in baseMockUiState) {
                return baseMockUiState[prop];
            }
            throw new Error(`mockUiState does not have property ${String(prop)}`);
        },
    });
    if (persistentState?.get) {
        persistentStateMock.get.mockImplementation(persistentState.get);
    }
    if (persistentState?.set) {
        persistentStateMock.set.mockImplementation(persistentState.set);
    }
    persistentStateMock.mockClear();
    const terminalWidth = width ?? baseState.terminalWidth;
    let finalSettings = settings;
    if (useAlternateBuffer !== undefined) {
        finalSettings = createMockSettings({
            ...settings.merged,
            ui: {
                ...settings.merged.ui,
                useAlternateBuffer,
            },
        });
    }
    const mainAreaWidth = terminalWidth;
    const finalUiState = {
        ...baseState,
        terminalWidth,
        mainAreaWidth,
    };
    const finalUIActions = { ...mockUIActions, ...uiActions };
    const allToolCalls = (finalUiState.pendingHistoryItems || [])
        .filter((item) => item.type === 'tool_group')
        .flatMap((item) => item.tools);
    const renderResult = render(_jsx(AppContext.Provider, { value: appState, children: _jsx(ConfigContext.Provider, { value: config, children: _jsx(SettingsContext.Provider, { value: finalSettings, children: _jsx(UIStateContext.Provider, { value: finalUiState, children: _jsx(VimModeProvider, { settings: finalSettings, children: _jsx(ShellFocusContext.Provider, { value: shellFocus, children: _jsx(StreamingContext.Provider, { value: finalUiState.streamingState, children: _jsx(UIActionsContext.Provider, { value: finalUIActions, children: _jsx(ToolActionsProvider, { config: config, toolCalls: allToolCalls, children: _jsx(AskUserActionsProvider, { request: null, onSubmit: vi.fn(), onCancel: vi.fn(), children: _jsx(KeypressProvider, { children: _jsx(MouseProvider, { mouseEventsEnabled: mouseEventsEnabled, children: _jsx(ScrollProvider, { children: _jsx(Box, { width: terminalWidth, flexShrink: 0, flexGrow: 0, flexDirection: "column", children: component }) }) }) }) }) }) }) }) }) }) }) }) }) }), terminalWidth);
    return { ...renderResult, simulateClick };
};
export function renderHook(renderCallback, options) {
    const result = { current: undefined };
    let currentProps = options?.initialProps;
    function TestComponent({ renderCallback, props, }) {
        result.current = renderCallback(props);
        return null;
    }
    const Wrapper = options?.wrapper || (({ children }) => _jsx(_Fragment, { children: children }));
    let inkRerender = () => { };
    let unmount = () => { };
    act(() => {
        const renderResult = render(_jsx(Wrapper, { children: _jsx(TestComponent, { renderCallback: renderCallback, props: currentProps }) }));
        inkRerender = renderResult.rerender;
        unmount = renderResult.unmount;
    });
    function rerender(props) {
        if (arguments.length > 0) {
            currentProps = props;
        }
        act(() => {
            inkRerender(_jsx(Wrapper, { children: _jsx(TestComponent, { renderCallback: renderCallback, props: currentProps }) }));
        });
    }
    return { result, rerender, unmount };
}
export function renderHookWithProviders(renderCallback, options = {}) {
    const result = { current: undefined };
    let setPropsFn;
    let forceUpdateFn;
    function TestComponent({ initialProps }) {
        const [props, setProps] = useState(initialProps);
        const [, forceUpdate] = useState(0);
        setPropsFn = setProps;
        forceUpdateFn = () => forceUpdate((n) => n + 1);
        result.current = renderCallback(props);
        return null;
    }
    const Wrapper = options.wrapper || (({ children }) => _jsx(_Fragment, { children: children }));
    let renderResult;
    act(() => {
        renderResult = renderWithProviders(_jsx(Wrapper, { children: _jsx(TestComponent, { initialProps: options.initialProps }) }), options);
    });
    function rerender(newProps) {
        act(() => {
            if (arguments.length > 0 && setPropsFn) {
                setPropsFn(newProps);
            }
            else if (forceUpdateFn) {
                forceUpdateFn();
            }
        });
    }
    return {
        result,
        rerender,
        unmount: () => {
            act(() => {
                renderResult.unmount();
            });
        },
    };
}
//# sourceMappingURL=render.js.map