/**
 * @license
 * Copyright 2025 Vybestack LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { main, setupUnhandledRejectionHandler, validateDnsResolutionOrder, startInteractiveUI, formatNonInteractiveError, } from './gemini.js';
import { 
// SettingsFile, // Currently unused
loadSettings, } from './config/settings.js';
import { loadCliConfig, parseArguments } from './config/config.js';
import { appEvents, AppEvent } from './utils/events.js';
import { FatalConfigError, OutputFormat } from '@vybestack/llxprt-code-core';
import { dynamicSettingsRegistry } from './utils/dynamicSettings.js';
import { shouldRelaunchForMemory, isDebugMode } from './utils/bootstrap.js';
import { relaunchAppInChildProcess } from './utils/relaunch.js';
// Custom error to identify mock process.exit calls
class MockProcessExitError extends Error {
    code;
    constructor(code) {
        super('PROCESS_EXIT_MOCKED');
        this.code = code;
        this.name = 'MockProcessExitError';
    }
}
// Mock dependencies
vi.mock('./config/settings.js', () => ({
    loadSettings: vi.fn().mockReturnValue({
        merged: {
            advanced: {},
            security: { auth: {} },
            ui: { useAlternateBuffer: true },
        },
        setValue: vi.fn(),
        forScope: () => ({ settings: {}, originalSettings: {}, path: '' }),
        errors: [],
    }),
    migrateDeprecatedSettings: vi.fn(),
    SettingScope: {
        User: 'user',
        Workspace: 'workspace',
        System: 'system',
        SystemDefaults: 'system-defaults',
    },
}));
vi.mock('./ui/utils/terminalCapabilityManager.js', () => ({
    terminalCapabilityManager: {
        detectCapabilities: vi.fn(),
        getTerminalBackgroundColor: vi.fn(),
    },
}));
vi.mock('./config/config.js', () => ({
    loadCliConfig: vi.fn().mockResolvedValue({
        getSandbox: vi.fn(() => false),
        getQuestion: vi.fn(() => ''),
        getProvider: vi.fn(() => undefined),
    }),
    parseArguments: vi.fn().mockResolvedValue({
        model: undefined,
        sandbox: undefined,
        sandboxImage: undefined,
        debug: undefined,
        experimentalUi: false,
        experimentalAcp: false,
        promptInteractive: undefined,
        prompt: undefined,
        promptWords: [],
    }),
}));
vi.mock('read-package-up', () => ({
    readPackageUp: vi.fn().mockResolvedValue({
        packageJson: { name: 'test-pkg', version: 'test-version' },
        path: '/fake/path/package.json',
    }),
}));
vi.mock('update-notifier', () => ({
    default: vi.fn(() => ({
        notify: vi.fn(),
    })),
}));
vi.mock('./utils/events.js', async (importOriginal) => {
    const actual = await importOriginal();
    return {
        ...actual,
        appEvents: {
            emit: vi.fn(),
        },
    };
});
vi.mock('./utils/sandbox.js', () => ({
    sandbox_command: vi.fn(() => ''), // Default to no sandbox command
    start_sandbox: vi.fn(() => Promise.resolve(0)), // Mock as an async function that resolves
}));
// Mock bootstrap utilities for deferred init tests
vi.mock('./utils/bootstrap.js', async (importOriginal) => {
    const actual = await importOriginal();
    return {
        ...actual,
        shouldRelaunchForMemory: vi.fn(() => []),
        isDebugMode: vi.fn(() => false),
    };
});
vi.mock('./utils/relaunch.js', () => ({
    relaunchAppInChildProcess: vi.fn().mockResolvedValue(0),
}));
vi.mock('./ui/utils/mouse.js', () => ({
    enableMouseEvents: vi.fn(),
    disableMouseEvents: vi.fn(),
    parseMouseEvent: vi.fn(),
    isIncompleteMouseSequence: vi.fn(),
    isMouseEventsActive: vi.fn(() => false),
    setMouseEventsActive: vi.fn(() => false),
    ENABLE_MOUSE_EVENTS: '',
    DISABLE_MOUSE_EVENTS: '',
}));
// Mock writeToStdout/writeToStderr so exit-handler tests can observe calls
// (the real writeToStdout captures process.stdout.write at module load and
// would bypass vi.spyOn on process.stdout.write).
const { mockWriteToStdout } = vi.hoisted(() => ({
    mockWriteToStdout: vi.fn().mockReturnValue(true),
}));
vi.mock('@vybestack/llxprt-code-core', async (importOriginal) => {
    const actual = await importOriginal();
    return {
        ...actual,
        writeToStdout: mockWriteToStdout,
        writeToStderr: vi.fn().mockReturnValue(true),
        patchStdio: vi.fn(() => vi.fn()),
    };
});
describe('gemini.tsx main function', () => {
    let loadSettingsMock;
    let originalEnvGeminiSandbox;
    let originalEnvSandbox;
    let initialUnhandledRejectionListeners = [];
    const processExitSpy = vi
        .spyOn(process, 'exit')
        .mockImplementation((code) => {
        throw new MockProcessExitError(code);
    });
    beforeEach(() => {
        loadSettingsMock = vi.mocked(loadSettings);
        dynamicSettingsRegistry.reset();
        // Store and clear sandbox-related env variables to ensure a consistent test environment
        originalEnvGeminiSandbox = process.env.LLXPRT_SANDBOX;
        originalEnvSandbox = process.env.SANDBOX;
        delete process.env.LLXPRT_SANDBOX;
        delete process.env.SANDBOX;
        initialUnhandledRejectionListeners =
            process.listeners('unhandledRejection');
    });
    afterEach(() => {
        // Restore original env variables
        if (originalEnvGeminiSandbox !== undefined) {
            process.env.LLXPRT_SANDBOX = originalEnvGeminiSandbox;
        }
        else {
            delete process.env.LLXPRT_SANDBOX;
        }
        if (originalEnvSandbox !== undefined) {
            process.env.SANDBOX = originalEnvSandbox;
        }
        else {
            delete process.env.SANDBOX;
        }
        const currentListeners = process.listeners('unhandledRejection');
        const addedListener = currentListeners.find((listener) => !initialUnhandledRejectionListeners.includes(listener));
        if (addedListener) {
            process.removeListener('unhandledRejection', addedListener);
        }
        vi.restoreAllMocks();
    });
    it('should throw InvalidConfigurationError if settings have errors', async () => {
        // Settings errors now throw directly from loadSettings
        loadSettingsMock.mockImplementation(() => {
            throw new FatalConfigError('Test settings error');
        });
        await expect(main()).rejects.toThrow(FatalConfigError);
    });
    it('should log unhandled promise rejections and open debug console on first error', async () => {
        const appEventsMock = vi.mocked(appEvents);
        const rejectionError = new Error('Test unhandled rejection');
        setupUnhandledRejectionHandler();
        // Simulate an unhandled rejection.
        // We are not using Promise.reject here as vitest will catch it.
        // Instead we will dispatch the event manually.
        process.emit('unhandledRejection', rejectionError, Promise.resolve());
        // We need to wait for the rejection handler to be called.
        await new Promise(process.nextTick);
        expect(appEventsMock.emit).toHaveBeenCalledWith(AppEvent.OpenDebugConsole);
        expect(appEventsMock.emit).toHaveBeenCalledWith(AppEvent.LogError, expect.stringContaining('Unhandled Promise Rejection'));
        expect(appEventsMock.emit).toHaveBeenCalledWith(AppEvent.LogError, expect.stringContaining('Please file a bug report using the /bug tool.'));
        // Simulate a second rejection
        const secondRejectionError = new Error('Second test unhandled rejection');
        process.emit('unhandledRejection', secondRejectionError, Promise.resolve());
        await new Promise(process.nextTick);
        // Ensure emit was only called once for OpenDebugConsole
        const openDebugConsoleCalls = appEventsMock.emit.mock.calls.filter((call) => call[0] === AppEvent.OpenDebugConsole);
        expect(openDebugConsoleCalls.length).toBe(1);
        // Avoid the process.exit error from being thrown.
        processExitSpy.mockRestore();
    });
    it('initializes content generator config before interactive provider usage', async () => {
        const providerManager = {
            getActiveProvider: vi.fn().mockReturnValue({ name: 'gemini' }),
            getActiveProviderName: vi.fn().mockReturnValue('gemini'),
            getServerToolsProvider: vi.fn().mockReturnValue(null),
        };
        const mockConfig = {
            initialize: vi.fn().mockResolvedValue(undefined),
            refreshAuth: vi.fn().mockResolvedValue(undefined),
            getProvider: vi.fn(() => undefined),
            getProviderManager: vi.fn(() => providerManager),
            getConversationLoggingEnabled: vi.fn(() => false),
            getMcpServers: vi.fn(() => ({})),
            getDebugMode: vi.fn(() => false),
            getIdeMode: vi.fn(() => false),
            getIdeClient: vi.fn(() => null),
            getListExtensions: vi.fn(() => false),
            getOutputFormat: vi.fn(() => OutputFormat.TEXT),
            getToolRegistryInfo: vi.fn(() => ({
                registered: [],
                unregistered: [],
            })),
            getSandbox: vi.fn(() => false),
            getModel: vi.fn(() => 'gemini-2.5-pro'),
            getProjectRoot: vi.fn(() => '/tmp/project'),
            isInteractive: vi.fn(() => true),
            getSessionId: vi.fn(() => 'session-1'),
            getQuestion: vi.fn(() => ''),
            isContinueSession: vi.fn(() => false),
            getExperimentalZedIntegration: vi.fn(() => false),
            getZedIntegrationEnabled: vi.fn(() => false),
            getTrustedFolder: vi.fn(() => true),
            getScreenReader: vi.fn(() => false),
            storage: {},
            getProjectTempDir: vi.fn(() => '/tmp/project-temp'),
            getContinueSessionRef: vi.fn(() => null),
            getWorkspaceContext: vi.fn(() => ({
                getDirectories: () => ['/tmp/project'],
            })),
            setTerminalBackground: vi.fn(),
            getTerminalBackground: vi.fn(() => undefined),
        };
        const loadSettingsMock = vi.mocked(loadSettings);
        loadSettingsMock.mockReturnValue({
            merged: {
                ui: { autoConfigureMaxOldSpaceSize: false },
            },
            setValue: vi.fn(),
            forScope: () => ({ settings: {}, originalSettings: {}, path: '' }),
            errors: [],
        });
        vi.mocked(loadCliConfig).mockResolvedValueOnce(mockConfig);
        vi.mocked(parseArguments).mockResolvedValueOnce({
            model: undefined,
            sandbox: undefined,
            sandboxImage: undefined,
            sandboxEngine: undefined,
            sandboxProfileLoad: undefined,
            debug: undefined,
            prompt: undefined,
            promptInteractive: undefined,
            outputFormat: undefined,
            showMemoryUsage: undefined,
            yolo: undefined,
            approvalMode: undefined,
            telemetry: undefined,
            checkpointing: undefined,
            telemetryTarget: undefined,
            telemetryOtlpEndpoint: undefined,
            telemetryLogPrompts: undefined,
            telemetryOutfile: undefined,
            allowedMcpServerNames: undefined,
            allowedTools: undefined,
            experimentalAcp: false,
            experimentalUi: false,
            extensions: undefined,
            listExtensions: undefined,
            provider: undefined,
            key: undefined,
            keyfile: undefined,
            baseurl: undefined,
            proxy: undefined,
            includeDirectories: undefined,
            profileLoad: undefined,
            loadMemoryFromIncludeDirectories: undefined,
            ideMode: undefined,
            screenReader: undefined,
            sessionSummary: undefined,
            dumponerror: undefined,
            promptWords: [],
            query: undefined,
            set: undefined,
            continue: undefined,
            nobrowser: undefined,
            listSessions: undefined,
            deleteSession: undefined,
        });
        const originalIsTTY = process.stdin.isTTY;
        const originalSetRawMode = process.stdin.setRawMode;
        Object.defineProperty(process.stdin, 'isTTY', {
            value: true,
            configurable: true,
        });
        Object.defineProperty(process.stdin, 'setRawMode', {
            value: vi.fn(),
            configurable: true,
        });
        try {
            await main();
        }
        finally {
            Object.defineProperty(process.stdin, 'isTTY', {
                value: originalIsTTY,
                configurable: true,
            });
            Object.defineProperty(process.stdin, 'setRawMode', {
                value: originalSetRawMode,
                configurable: true,
            });
        }
        const { render } = await import('ink');
        expect(vi.mocked(render)).toHaveBeenCalledTimes(1);
        // Avoid the process.exit error from being thrown in later tests.
        processExitSpy.mockRestore();
    });
    it('formats non-interactive errors with readable details for objects', () => {
        const error = {
            message: 'Request failed',
            status: 404,
            error: { message: 'Not Found', code: 404 },
        };
        expect(formatNonInteractiveError(error)).toContain('Request failed');
        expect(formatNonInteractiveError(error)).not.toContain('[object Object]');
    });
    it('should call setupTerminalAndTheme when isInteractive is true', async () => {
        const { setupTerminalAndTheme } = await import('./utils/terminalTheme.js');
        const providerManager = {
            getActiveProvider: vi.fn().mockReturnValue({ name: 'gemini' }),
            getActiveProviderName: vi.fn().mockReturnValue('gemini'),
            getServerToolsProvider: vi.fn().mockReturnValue(null),
        };
        const mockConfig = {
            initialize: vi.fn().mockResolvedValue(undefined),
            refreshAuth: vi.fn().mockResolvedValue(undefined),
            getProvider: vi.fn(() => undefined),
            getProviderManager: vi.fn(() => providerManager),
            getConversationLoggingEnabled: vi.fn(() => false),
            getMcpServers: vi.fn(() => ({})),
            getDebugMode: vi.fn(() => false),
            getIdeMode: vi.fn(() => false),
            getIdeClient: vi.fn(() => null),
            getListExtensions: vi.fn(() => false),
            getOutputFormat: vi.fn(() => OutputFormat.TEXT),
            getToolRegistryInfo: vi.fn(() => ({
                registered: [],
                unregistered: [],
            })),
            getSandbox: vi.fn(() => false),
            getModel: vi.fn(() => 'gemini-2.5-pro'),
            getProjectRoot: vi.fn(() => '/tmp/project'),
            isInteractive: vi.fn(() => true),
            getSessionId: vi.fn(() => 'session-1'),
            getQuestion: vi.fn(() => ''),
            isContinueSession: vi.fn(() => false),
            getExperimentalZedIntegration: vi.fn(() => false),
            getZedIntegrationEnabled: vi.fn(() => false),
            getTrustedFolder: vi.fn(() => true),
            getScreenReader: vi.fn(() => false),
            storage: {},
            getProjectTempDir: vi.fn(() => '/tmp/project-temp'),
            getContinueSessionRef: vi.fn(() => null),
            getWorkspaceContext: vi.fn(() => ({
                getDirectories: () => ['/tmp/project'],
            })),
            setTerminalBackground: vi.fn(),
            getTerminalBackground: vi.fn(() => undefined),
        };
        const loadSettingsMock = vi.mocked(loadSettings);
        loadSettingsMock.mockReturnValue({
            merged: {
                ui: { autoConfigureMaxOldSpaceSize: false },
            },
            setValue: vi.fn(),
            forScope: () => ({ settings: {}, originalSettings: {}, path: '' }),
            errors: [],
        });
        vi.mocked(loadCliConfig).mockResolvedValueOnce(mockConfig);
        vi.mocked(parseArguments).mockResolvedValueOnce({
            model: undefined,
            sandbox: undefined,
            sandboxImage: undefined,
            sandboxEngine: undefined,
            sandboxProfileLoad: undefined,
            debug: undefined,
            prompt: undefined,
            promptInteractive: undefined,
            outputFormat: undefined,
            showMemoryUsage: undefined,
            yolo: undefined,
            approvalMode: undefined,
            telemetry: undefined,
            checkpointing: undefined,
            telemetryTarget: undefined,
            telemetryOtlpEndpoint: undefined,
            telemetryLogPrompts: undefined,
            telemetryOutfile: undefined,
            allowedMcpServerNames: undefined,
            allowedTools: undefined,
            experimentalAcp: false,
            experimentalUi: false,
            extensions: undefined,
            listExtensions: undefined,
            provider: undefined,
            key: undefined,
            keyfile: undefined,
            baseurl: undefined,
            proxy: undefined,
            includeDirectories: undefined,
            profileLoad: undefined,
            loadMemoryFromIncludeDirectories: undefined,
            ideMode: undefined,
            screenReader: undefined,
            sessionSummary: undefined,
            dumponerror: undefined,
            promptWords: [],
            query: undefined,
            set: undefined,
            continue: undefined,
            nobrowser: undefined,
            listSessions: undefined,
            deleteSession: undefined,
        });
        const originalIsTTY = process.stdin.isTTY;
        const originalSetRawMode = process.stdin.setRawMode;
        Object.defineProperty(process.stdin, 'isTTY', {
            value: true,
            configurable: true,
        });
        Object.defineProperty(process.stdin, 'setRawMode', {
            value: vi.fn(),
            configurable: true,
        });
        try {
            await main();
        }
        finally {
            Object.defineProperty(process.stdin, 'isTTY', {
                value: originalIsTTY,
                configurable: true,
            });
            Object.defineProperty(process.stdin, 'setRawMode', {
                value: originalSetRawMode,
                configurable: true,
            });
        }
        expect(setupTerminalAndTheme).toHaveBeenCalledTimes(1);
        expect(setupTerminalAndTheme).toHaveBeenCalledWith(mockConfig, expect.objectContaining({ merged: expect.anything() }));
        processExitSpy.mockRestore();
    });
});
describe('gemini.tsx deferred initialization', () => {
    const originalEnv = { ...process.env };
    beforeEach(() => {
        vi.clearAllMocks();
        process.env = { ...originalEnv };
        delete process.env.SANDBOX;
        delete process.env.LLXPRT_CODE_NO_RELAUNCH;
    });
    afterEach(() => {
        process.env = originalEnv;
    });
    it('should check for memory relaunch BEFORE loading settings when relaunch needed', async () => {
        const shouldRelaunchMock = vi.mocked(shouldRelaunchForMemory);
        const relaunchMock = vi.mocked(relaunchAppInChildProcess);
        const loadSettingsMock = vi.mocked(loadSettings);
        const loadCliConfigMock = vi.mocked(loadCliConfig);
        // Simulate need for relaunch
        shouldRelaunchMock.mockReturnValue(['--max-old-space-size=4096']);
        relaunchMock.mockResolvedValue(0);
        loadSettingsMock.mockReturnValue({
            merged: {
                ui: { autoConfigureMaxOldSpaceSize: true },
            },
            setValue: vi.fn(),
            forScope: () => ({ settings: {}, originalSettings: {}, path: '' }),
            errors: [],
        });
        const processExitSpy = vi.spyOn(process, 'exit').mockImplementation(() => {
            throw new Error('PROCESS_EXIT');
        });
        let caughtError = null;
        try {
            await main();
        }
        catch (e) {
            caughtError = e;
        }
        // Expected to throw from process.exit mock
        expect(caughtError?.message).toBe('PROCESS_EXIT');
        // Verify relaunch check happened
        expect(shouldRelaunchMock).toHaveBeenCalled();
        expect(relaunchMock).toHaveBeenCalledWith(['--max-old-space-size=4096']);
        // Verify loadCliConfig was NOT called
        expect(loadCliConfigMock).not.toHaveBeenCalled();
        processExitSpy.mockRestore();
    });
    it('should NOT relaunch when already in sandbox environment', async () => {
        process.env.SANDBOX = 'true';
        const shouldRelaunchMock = vi.mocked(shouldRelaunchForMemory);
        const relaunchMock = vi.mocked(relaunchAppInChildProcess);
        const loadSettingsMock = vi.mocked(loadSettings);
        // Even if memory check says relaunch needed
        shouldRelaunchMock.mockReturnValue(['--max-old-space-size=4096']);
        // Mock settings to prevent further execution
        loadSettingsMock.mockImplementation(() => {
            throw new Error('SETTINGS_LOADED');
        });
        let caughtError = null;
        try {
            await main();
        }
        catch (e) {
            caughtError = e;
        }
        // Should reach settings loading, not relaunch
        expect(caughtError?.message).toBe('SETTINGS_LOADED');
        // Relaunch should NOT happen in sandbox
        expect(relaunchMock).not.toHaveBeenCalled();
        // Settings should be loaded
        expect(loadSettingsMock).toHaveBeenCalled();
    });
    it('should proceed with normal initialization when no relaunch needed', async () => {
        const shouldRelaunchMock = vi.mocked(shouldRelaunchForMemory);
        const relaunchMock = vi.mocked(relaunchAppInChildProcess);
        const loadSettingsMock = vi.mocked(loadSettings);
        // No relaunch needed
        shouldRelaunchMock.mockReturnValue([]);
        // Mock settings to prevent further execution
        loadSettingsMock.mockImplementation(() => {
            throw new Error('SETTINGS_LOADED');
        });
        let caughtError = null;
        try {
            await main();
        }
        catch (e) {
            caughtError = e;
        }
        expect(caughtError?.message).toBe('SETTINGS_LOADED');
        // Verify no relaunch occurred
        expect(relaunchMock).not.toHaveBeenCalled();
        // Settings should be loaded
        expect(loadSettingsMock).toHaveBeenCalled();
    });
    it('should use isDebugMode for early debug detection', async () => {
        const isDebugModeMock = vi.mocked(isDebugMode);
        const shouldRelaunchMock = vi.mocked(shouldRelaunchForMemory);
        const loadSettingsMock = vi.mocked(loadSettings);
        isDebugModeMock.mockReturnValue(true);
        shouldRelaunchMock.mockReturnValue([]);
        loadSettingsMock.mockReturnValue({
            merged: {
                ui: { autoConfigureMaxOldSpaceSize: true },
            },
            setValue: vi.fn(),
            forScope: () => ({ settings: {}, originalSettings: {}, path: '' }),
            errors: [],
        });
        try {
            await main();
        }
        catch (_e) {
            // Expected
        }
        // Debug mode should be detected
        expect(isDebugModeMock).toHaveBeenCalled();
        // And passed to shouldRelaunchForMemory
        expect(shouldRelaunchMock).toHaveBeenCalledWith(true);
    });
});
describe('validateDnsResolutionOrder', () => {
    let consoleWarnSpy;
    beforeEach(() => {
        consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
    });
    afterEach(() => {
        consoleWarnSpy.mockRestore();
    });
    it('should return "ipv4first" when the input is "ipv4first"', () => {
        expect(validateDnsResolutionOrder('ipv4first')).toBe('ipv4first');
        expect(consoleWarnSpy).not.toHaveBeenCalled();
    });
    it('should return "verbatim" when the input is "verbatim"', () => {
        expect(validateDnsResolutionOrder('verbatim')).toBe('verbatim');
        expect(consoleWarnSpy).not.toHaveBeenCalled();
    });
    it('should return the default "ipv4first" when the input is undefined', () => {
        expect(validateDnsResolutionOrder(undefined)).toBe('ipv4first');
        expect(consoleWarnSpy).not.toHaveBeenCalled();
    });
    it('should return the default "ipv4first" and log a warning for an invalid string', () => {
        expect(validateDnsResolutionOrder('invalid-value')).toBe('ipv4first');
        expect(consoleWarnSpy).toHaveBeenCalledExactlyOnceWith('Invalid value for dnsResolutionOrder in settings: "invalid-value". Using default "ipv4first".');
    });
});
describe('startInteractiveUI', () => {
    // Mock dependencies
    const mockConfig = {
        getProjectRoot: () => '/root',
        getScreenReader: () => false,
        getQuestion: () => '',
        isContinueSession: () => false,
        getSessionId: () => 'session-1',
        storage: {},
        getDebugMode: () => false,
        getTerminalBackground: () => undefined,
    };
    const mockSettings = {
        merged: {
            ui: {
                hideWindowTitle: false,
            },
        },
    };
    const mockStartupWarnings = ['warning1'];
    const mockWorkspaceRoot = '/root';
    vi.mock('./utils/version.js', () => ({
        getCliVersion: vi.fn(() => Promise.resolve('1.0.0')),
    }));
    vi.mock('./utils/terminalTheme.js', () => ({
        setupTerminalAndTheme: vi.fn(() => Promise.resolve(undefined)),
    }));
    vi.mock('./ui/utils/updateCheck.js', () => ({
        checkForUpdates: vi.fn(() => Promise.resolve(null)),
    }));
    vi.mock('./utils/cleanup.js', () => ({
        cleanupCheckpoints: vi.fn(() => Promise.resolve()),
        registerCleanup: vi.fn(),
        registerSyncCleanup: vi.fn(),
        runExitCleanup: vi.fn(),
    }));
    vi.mock('ink', () => ({
        render: vi.fn().mockReturnValue({ unmount: vi.fn() }),
    }));
    beforeEach(() => {
        vi.clearAllMocks();
    });
    it('should render the UI with proper React context and exitOnCtrlC disabled', async () => {
        const { render } = await import('ink');
        const renderSpy = vi.mocked(render);
        await startInteractiveUI(mockConfig, mockSettings, mockStartupWarnings, mockWorkspaceRoot);
        // Verify render was called with correct options
        expect(renderSpy).toHaveBeenCalledTimes(1);
        const [reactElement, options] = renderSpy.mock.calls[0];
        // Verify render options
        expect(options).toEqual(expect.objectContaining({
            exitOnCtrlC: false,
        }));
        // Verify React element structure is valid (but don't deep dive into JSX internals)
        expect(reactElement).toBeDefined();
    });
    it('should perform all startup tasks in correct order', async () => {
        const { getCliVersion } = await import('./utils/version.js');
        const { checkForUpdates } = await import('./ui/utils/updateCheck.js');
        const { registerCleanup } = await import('./utils/cleanup.js');
        await startInteractiveUI(mockConfig, mockSettings, mockStartupWarnings, mockWorkspaceRoot);
        // Verify all startup tasks were called
        expect(getCliVersion).toHaveBeenCalledTimes(1);
        expect(registerCleanup).toHaveBeenCalledTimes(1);
        // Verify cleanup handler is registered with unmount function
        const cleanupFn = vi.mocked(registerCleanup).mock.calls[0][0];
        expect(typeof cleanupFn).toBe('function');
        // checkForUpdates should be called asynchronously (not waited for)
        // We need a small delay to let it execute
        await new Promise((resolve) => setTimeout(resolve, 0));
        expect(checkForUpdates).toHaveBeenCalledTimes(1);
    });
    it('should register exit handler that disables bracketed paste and focus tracking', async () => {
        const exitHandlers = [];
        const processOnSpy = vi
            .spyOn(process, 'on')
            .mockImplementation((event, handler) => {
            if (event === 'exit') {
                exitHandlers.push(handler);
            }
            return process;
        });
        // Ensure isTTY is true so the guard passes
        const originalIsTTY = process.stdout.isTTY;
        Object.defineProperty(process.stdout, 'isTTY', {
            value: true,
            configurable: true,
        });
        const mouseEnabledConfig = {
            ...mockConfig,
            getScreenReader: () => false,
        };
        const mouseEnabledSettings = {
            merged: {
                ui: {
                    hideWindowTitle: true,
                    useAlternateBuffer: true,
                    enableMouseEvents: true,
                },
            },
        };
        try {
            mockWriteToStdout.mockClear();
            await startInteractiveUI(mouseEnabledConfig, mouseEnabledSettings, mockStartupWarnings, mockWorkspaceRoot);
            // Fire all exit handlers
            for (const handler of exitHandlers) {
                handler();
            }
            // Verify bracketed paste disabled (via mocked writeToStdout)
            expect(mockWriteToStdout).toHaveBeenCalledWith(expect.stringContaining('\x1b[?2004l'));
            // Verify focus tracking disabled
            expect(mockWriteToStdout).toHaveBeenCalledWith(expect.stringContaining('\x1b[?1004l'));
        }
        finally {
            Object.defineProperty(process.stdout, 'isTTY', {
                value: originalIsTTY,
                configurable: true,
            });
            processOnSpy.mockRestore();
        }
    });
    it('should not write terminal escape sequences on exit when stdout is not a TTY', async () => {
        const exitHandlers = [];
        const processOnSpy = vi
            .spyOn(process, 'on')
            .mockImplementation((event, handler) => {
            if (event === 'exit') {
                exitHandlers.push(handler);
            }
            return process;
        });
        const originalIsTTY = process.stdout.isTTY;
        Object.defineProperty(process.stdout, 'isTTY', {
            value: false,
            configurable: true,
        });
        const mouseEnabledConfig = {
            ...mockConfig,
            getScreenReader: () => false,
        };
        const mouseEnabledSettings = {
            merged: {
                ui: {
                    hideWindowTitle: true,
                    useAlternateBuffer: true,
                    enableMouseEvents: true,
                },
            },
        };
        try {
            mockWriteToStdout.mockClear();
            await startInteractiveUI(mouseEnabledConfig, mouseEnabledSettings, mockStartupWarnings, mockWorkspaceRoot);
            // Clear any writes from render setup
            mockWriteToStdout.mockClear();
            // Fire all exit handlers
            for (const handler of exitHandlers) {
                handler();
            }
            // When not a TTY, should not write any escape sequences via writeToStdout
            const calls = mockWriteToStdout.mock.calls.map((c) => c[0]);
            const hasEscapeSeq = calls.some((arg) => typeof arg === 'string' && arg.includes('\x1b['));
            expect(hasEscapeSeq).toBe(false);
        }
        finally {
            Object.defineProperty(process.stdout, 'isTTY', {
                value: originalIsTTY,
                configurable: true,
            });
            processOnSpy.mockRestore();
        }
    });
});
//# sourceMappingURL=gemini.test.js.map