import { jsx as _jsx } from "react/jsx-runtime";
/**
 * @license
 * Copyright 2025 HopCode Team
 * SPDX-License-Identifier: Apache-2.0
 */
import { render, cleanup } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { ModelDialog } from './ModelDialog.js';
import { useKeypress } from '../hooks/useKeypress.js';
import { DescriptiveRadioButtonSelect } from './shared/DescriptiveRadioButtonSelect.js';
import { ConfigContext } from '../contexts/ConfigContext.js';
import { SettingsContext } from '../contexts/SettingsContext.js';
import { AuthType, DEFAULT_HOPCODE_MODEL } from '@hoptrendy/hopcode-core';
import { SettingScope } from '../../config/settings.js';
import { getFilteredHopCodeModels } from '../models/availableModels.js';
vi.mock('../hooks/useKeypress.js', () => ({
    useKeypress: vi.fn(),
}));
const mockedUseKeypress = vi.mocked(useKeypress);
vi.mock('./shared/DescriptiveRadioButtonSelect.js', () => ({
    DescriptiveRadioButtonSelect: vi.fn(() => null),
}));
// Helper to create getAvailableModelsForAuthType mock
const createMockGetAvailableModelsForAuthType = () => vi.fn((t) => {
    if (t === AuthType.HOPCODE_OAUTH) {
        return getFilteredHopCodeModels().map((m) => ({
            id: m.id,
            label: m.label,
            authType: AuthType.HOPCODE_OAUTH,
        }));
    }
    return [];
});
const mockedSelect = vi.mocked(DescriptiveRadioButtonSelect);
const renderComponent = (props = {}, contextValue = undefined) => {
    const defaultProps = {
        onClose: vi.fn(),
    };
    const combinedProps = { ...defaultProps, ...props };
    const mockSettings = {
        isTrusted: true,
        user: { settings: {} },
        workspace: { settings: {} },
        setValue: vi.fn(),
    };
    const mockConfig = {
        // --- Functions used by ModelDialog ---
        getModel: vi.fn(() => DEFAULT_HOPCODE_MODEL),
        setModel: vi.fn().mockResolvedValue(undefined),
        switchModel: vi.fn().mockResolvedValue(undefined),
        getAuthType: vi.fn(() => 'hopcode-oauth'),
        getAllConfiguredModels: vi.fn(() => getFilteredHopCodeModels().map((m) => ({
            id: m.id,
            label: m.label,
            description: m.description || '',
            authType: AuthType.HOPCODE_OAUTH,
        }))),
        getModelsConfig: vi.fn(() => ({
            getGenerationConfig: vi.fn(() => ({ baseUrl: undefined })),
        })),
        getActiveRuntimeModelSnapshot: vi.fn(() => undefined),
        // --- Functions used by ClearcutLogger ---
        getUsageStatisticsEnabled: vi.fn(() => true),
        getSessionId: vi.fn(() => 'mock-session-id'),
        getDebugMode: vi.fn(() => false),
        getContentGeneratorConfig: vi.fn(() => ({
            authType: AuthType.HOPCODE_OAUTH,
            model: DEFAULT_HOPCODE_MODEL,
        })),
        getUseModelRouter: vi.fn(() => false),
        getProxy: vi.fn(() => undefined),
        // --- Spread test-specific overrides ---
        ...(contextValue ?? {}),
    };
    const renderResult = render(_jsx(SettingsContext.Provider, { value: mockSettings, children: _jsx(ConfigContext.Provider, { value: mockConfig, children: _jsx(ModelDialog, { ...combinedProps }) }) }));
    return {
        ...renderResult,
        props: combinedProps,
        mockConfig,
        mockSettings,
    };
};
describe('<ModelDialog />', () => {
    beforeEach(() => {
        vi.clearAllMocks();
        // Ensure env-based fallback models don't leak into this suite from the developer environment.
        delete process.env['OPENAI_MODEL'];
        delete process.env['ANTHROPIC_MODEL'];
    });
    afterEach(() => {
        cleanup();
    });
    it('renders the title', () => {
        const { getByText } = renderComponent();
        expect(getByText('Select Model')).toBeDefined();
    });
    it('passes all model options to DescriptiveRadioButtonSelect', () => {
        renderComponent();
        // There may be multiple renders due to internal state updates; check the first call
        const props = mockedSelect.mock.calls[0][0];
        const items = props.items;
        expect(items.length).toBeGreaterThan(0);
        // Verify the first item uses valid authType::modelId format
        expect(props.items[0].value).toContain('::');
        expect(props.showNumbers).toBe(true);
    });
    it('initializes with the model from ConfigContext', () => {
        const mockGetModel = vi.fn(() => DEFAULT_HOPCODE_MODEL);
        renderComponent({}, {
            getModel: mockGetModel,
            getAvailableModelsForAuthType: createMockGetAvailableModelsForAuthType(),
        });
        expect(mockGetModel).toHaveBeenCalled();
        // Calculate expected index dynamically based on model list
        const hopcodeModels = getFilteredHopCodeModels();
        const expectedIndex = hopcodeModels.findIndex((m) => m.id === DEFAULT_HOPCODE_MODEL);
        expect(mockedSelect).toHaveBeenCalledWith(expect.objectContaining({
            initialIndex: expectedIndex,
        }), undefined);
    });
    it('initializes with default coder model if context is not provided', () => {
        renderComponent({}, undefined);
        expect(mockedSelect).toHaveBeenCalledWith(expect.objectContaining({
            initialIndex: 0,
        }), undefined);
    });
    it('initializes with default coder model if getModel returns undefined', () => {
        const mockGetModel = vi.fn(() => undefined);
        renderComponent({}, {
            getModel: mockGetModel,
            getAvailableModelsForAuthType: createMockGetAvailableModelsForAuthType(),
        });
        expect(mockGetModel).toHaveBeenCalled();
        // When getModel returns undefined, preferredModel falls back to DEFAULT_HOPCODE_MODEL
        // which has index 0, so initialIndex should be 0. Use the last call to be resilient
        // to internal re-render patterns.
        const lastCall = mockedSelect.mock.calls[mockedSelect.mock.calls.length - 1];
        expect(lastCall[0]).toEqual(expect.objectContaining({
            initialIndex: 0,
        }));
    });
    it('blocks hopcode-oauth model selection with an error message (discontinued)', async () => {
        const { props, mockConfig } = renderComponent({}, {
            getAvailableModelsForAuthType: vi.fn((t) => {
                if (t === AuthType.HOPCODE_OAUTH) {
                    return getFilteredHopCodeModels().map((m) => ({
                        id: m.id,
                        label: m.label,
                        authType: AuthType.HOPCODE_OAUTH,
                    }));
                }
                return [];
            }),
        });
        const childOnSelect = mockedSelect.mock.calls[0][0].onSelect;
        expect(childOnSelect).toBeDefined();
        await childOnSelect(`${AuthType.HOPCODE_OAUTH}::${DEFAULT_HOPCODE_MODEL}`);
        // hopcode-oauth is discontinued — switchModel should NOT be called
        expect(mockConfig?.switchModel).not.toHaveBeenCalled();
        // Dialog should NOT close (user stays in the dialog to see the error)
        expect(props.onClose).not.toHaveBeenCalled();
    });
    it('calls config.switchModel and onClose when selecting a non-OAuth model', async () => {
        const switchModel = vi.fn().mockResolvedValue(undefined);
        const getAuthType = vi.fn(() => AuthType.USE_OPENAI);
        const getAvailableModelsForAuthType = vi.fn((t) => {
            if (t === AuthType.USE_OPENAI) {
                return [{ id: 'gpt-4', label: 'GPT-4', authType: t }];
            }
            if (t === AuthType.HOPCODE_OAUTH) {
                return getFilteredHopCodeModels().map((m) => ({
                    id: m.id,
                    label: m.label,
                    authType: AuthType.HOPCODE_OAUTH,
                }));
            }
            return [];
        });
        const { props, mockSettings } = renderComponent({}, {
            getModel: vi.fn(() => 'gpt-4'),
            getAuthType,
            switchModel,
            getAvailableModelsForAuthType,
            getAllConfiguredModels: vi.fn(() => [
                ...getFilteredHopCodeModels().map((m) => ({
                    id: m.id,
                    label: m.label,
                    description: m.description || '',
                    authType: AuthType.HOPCODE_OAUTH,
                })),
                {
                    id: 'gpt-4',
                    label: 'GPT-4',
                    description: 'GPT-4 model',
                    authType: AuthType.USE_OPENAI,
                },
            ]),
            getContentGeneratorConfig: vi.fn(() => ({
                authType: AuthType.USE_OPENAI,
                model: 'gpt-4',
            })),
        });
        const childOnSelect = mockedSelect.mock.calls[0][0].onSelect;
        expect(childOnSelect).toBeDefined();
        // Select a non-OAuth model (USE_OPENAI)
        await childOnSelect(`${AuthType.USE_OPENAI}::gpt-4`);
        expect(switchModel).toHaveBeenCalledWith(AuthType.USE_OPENAI, 'gpt-4', {
            baseUrl: undefined,
        });
        expect(mockSettings.setValue).toHaveBeenCalledWith(SettingScope.User, 'model.name', 'gpt-4');
        expect(mockSettings.setValue).toHaveBeenCalledWith(SettingScope.User, 'security.auth.selectedType', AuthType.USE_OPENAI);
        expect(props.onClose).toHaveBeenCalledTimes(1);
    });
    it('stores authType-qualified selectors in fast model mode', async () => {
        const setFastModel = vi.fn();
        const { props, mockSettings } = renderComponent({ isFastModelMode: true }, {
            getAuthType: vi.fn(() => AuthType.USE_ANTHROPIC),
            getModel: vi.fn(() => 'claude-opus-4-7'),
            getAllConfiguredModels: vi.fn(() => [
                {
                    id: 'deepseek-v4-flash',
                    label: 'deepseek-v4-flash',
                    authType: AuthType.USE_OPENAI,
                },
                {
                    id: 'claude-opus-4-7',
                    label: 'claude-opus-4-7',
                    authType: AuthType.USE_ANTHROPIC,
                },
            ]),
            getContentGeneratorConfig: vi.fn(() => ({
                authType: AuthType.USE_ANTHROPIC,
                model: 'claude-opus-4-7',
            })),
            setFastModel,
        });
        const childOnSelect = mockedSelect.mock.calls[0][0].onSelect;
        await childOnSelect(`${AuthType.USE_OPENAI}::deepseek-v4-flash`);
        expect(mockSettings.setValue).toHaveBeenCalledWith(SettingScope.User, 'fastModel', 'openai:deepseek-v4-flash');
        expect(setFastModel).toHaveBeenCalledWith('openai:deepseek-v4-flash');
        expect(props.onClose).toHaveBeenCalledTimes(1);
    });
    it('highlights the cross-auth row for a bare fast-model setting', () => {
        // `/model --fast deepseek-v4-flash` validates across all providers and
        // persists the bare model id. When the dialog re-opens, it must locate
        // the right row even though the setting carries no authType prefix —
        // otherwise the highlight falls back to the current auth's first row
        // and Enter would silently overwrite the setting.
        const mockSettings = {
            isTrusted: true,
            user: { settings: {} },
            workspace: { settings: {} },
            merged: { fastModel: 'deepseek-v4-flash' },
            setValue: vi.fn(),
        };
        const allModels = [
            {
                id: 'claude-opus-4-7',
                label: 'claude-opus-4-7',
                description: '',
                authType: AuthType.USE_ANTHROPIC,
            },
            {
                id: 'deepseek-v4-flash',
                label: 'deepseek-v4-flash',
                description: '',
                authType: AuthType.USE_OPENAI,
            },
        ];
        render(_jsx(SettingsContext.Provider, { value: mockSettings, children: _jsx(ConfigContext.Provider, { value: {
                    getModel: vi.fn(() => 'claude-opus-4-7'),
                    getAuthType: vi.fn(() => AuthType.USE_ANTHROPIC),
                    getAllConfiguredModels: vi.fn(() => allModels),
                    getContentGeneratorConfig: vi.fn(() => ({
                        authType: AuthType.USE_ANTHROPIC,
                        model: 'claude-opus-4-7',
                    })),
                    getModelsConfig: vi.fn(() => ({
                        getGenerationConfig: vi.fn(() => ({ baseUrl: undefined })),
                    })),
                    getActiveRuntimeModelSnapshot: vi.fn(() => undefined),
                    getUsageStatisticsEnabled: vi.fn(() => false),
                    getSessionId: vi.fn(() => 'session'),
                    getDebugMode: vi.fn(() => false),
                    getUseModelRouter: vi.fn(() => false),
                    getProxy: vi.fn(() => undefined),
                }, children: _jsx(ModelDialog, { onClose: vi.fn(), isFastModelMode: true }) }) }));
        const items = mockedSelect.mock.calls[0][0].items;
        const deepseekIndex = items.findIndex((item) => String(item.value).includes('deepseek-v4-flash'));
        expect(deepseekIndex).toBeGreaterThanOrEqual(0);
        expect(mockedSelect.mock.calls[0][0].initialIndex).toBe(deepseekIndex);
    });
    it('blocks switching to hopcode-oauth from another authType (discontinued)', async () => {
        const switchModel = vi.fn().mockResolvedValue(undefined);
        const getAuthType = vi.fn(() => AuthType.USE_OPENAI);
        const getAvailableModelsForAuthType = vi.fn((t) => {
            if (t === AuthType.USE_OPENAI) {
                return [{ id: 'gpt-4', label: 'GPT-4', authType: t }];
            }
            if (t === AuthType.HOPCODE_OAUTH) {
                return getFilteredHopCodeModels().map((m) => ({
                    id: m.id,
                    label: m.label,
                    authType: AuthType.HOPCODE_OAUTH,
                }));
            }
            return [];
        });
        const mockConfigWithSwitchAuthType = {
            getAuthType,
            getModel: vi.fn(() => 'gpt-4'),
            getContentGeneratorConfig: vi.fn(() => ({
                authType: AuthType.USE_OPENAI,
                model: 'gpt-4',
            })),
            switchModel,
            getAvailableModelsForAuthType,
        };
        const { props } = renderComponent({}, mockConfigWithSwitchAuthType);
        const childOnSelect = mockedSelect.mock.calls[0][0].onSelect;
        await childOnSelect(`${AuthType.HOPCODE_OAUTH}::${DEFAULT_HOPCODE_MODEL}`);
        // hopcode-oauth is discontinued — switchModel should NOT be called
        expect(switchModel).not.toHaveBeenCalled();
        // Dialog should NOT close
        expect(props.onClose).not.toHaveBeenCalled();
    });
    it('passes onHighlight to DescriptiveRadioButtonSelect', () => {
        renderComponent();
        const childOnHighlight = mockedSelect.mock.calls[0][0].onHighlight;
        expect(childOnHighlight).toBeDefined();
        expect(typeof childOnHighlight).toBe('function');
    });
    it('calls onClose prop when "escape" key is pressed', () => {
        const { props } = renderComponent();
        expect(mockedUseKeypress).toHaveBeenCalled();
        const keyPressHandler = mockedUseKeypress.mock.calls[0][0];
        const options = mockedUseKeypress.mock.calls[0][1];
        expect(options).toEqual({ isActive: true });
        keyPressHandler({
            name: 'escape',
            ctrl: false,
            meta: false,
            shift: false,
            paste: false,
            sequence: '',
        });
        expect(props.onClose).toHaveBeenCalledTimes(1);
        keyPressHandler({
            name: 'a',
            ctrl: false,
            meta: false,
            shift: false,
            paste: false,
            sequence: '',
        });
        expect(props.onClose).toHaveBeenCalledTimes(1);
    });
    it('updates initialIndex when config context changes', () => {
        const mockGetModel = vi.fn(() => DEFAULT_HOPCODE_MODEL);
        const mockGetAuthType = vi.fn(() => 'hopcode-oauth');
        const mockGetModelsConfig = vi.fn(() => ({
            getGenerationConfig: vi.fn(() => ({ baseUrl: undefined })),
        }));
        const mockGetActiveRuntimeModelSnapshot = vi.fn(() => undefined);
        const mockSettings = {
            isTrusted: true,
            user: { settings: {} },
            workspace: { settings: {} },
            setValue: vi.fn(),
        };
        const { rerender } = render(_jsx(SettingsContext.Provider, { value: mockSettings, children: _jsx(ConfigContext.Provider, { value: {
                    getModel: mockGetModel,
                    getAuthType: mockGetAuthType,
                    getAvailableModelsForAuthType: createMockGetAvailableModelsForAuthType(),
                    getAllConfiguredModels: vi.fn(() => getFilteredHopCodeModels().map((m) => ({
                        id: m.id,
                        label: m.label,
                        description: m.description || '',
                        authType: AuthType.HOPCODE_OAUTH,
                    }))),
                    getModelsConfig: mockGetModelsConfig,
                    getActiveRuntimeModelSnapshot: mockGetActiveRuntimeModelSnapshot,
                }, children: _jsx(ModelDialog, { onClose: vi.fn() }) }) }));
        // DEFAULT_HOPCODE_MODEL (coder-model) should be at index 0
        // Use first call to verify initial state
        const firstSelectCall = mockedSelect.mock.calls[0]?.[0];
        expect(firstSelectCall?.initialIndex).toBe(0);
        mockGetModel.mockReturnValue(DEFAULT_HOPCODE_MODEL);
        const newMockConfig = {
            getModel: mockGetModel,
            getAuthType: mockGetAuthType,
            getAvailableModelsForAuthType: createMockGetAvailableModelsForAuthType(),
            getAllConfiguredModels: vi.fn(() => getFilteredHopCodeModels().map((m) => ({
                id: m.id,
                label: m.label,
                description: m.description || '',
                authType: AuthType.HOPCODE_OAUTH,
            }))),
            getModelsConfig: mockGetModelsConfig,
            getActiveRuntimeModelSnapshot: mockGetActiveRuntimeModelSnapshot,
        };
        rerender(_jsx(SettingsContext.Provider, { value: mockSettings, children: _jsx(ConfigContext.Provider, { value: newMockConfig, children: _jsx(ModelDialog, { onClose: vi.fn() }) }) }));
        // Should be called at least twice: initial render + re-render after context change
        // Use the last call to verify the final state
        const callCount = mockedSelect.mock.calls.length;
        expect(callCount).toBeGreaterThanOrEqual(2);
        const afterChangeCall = mockedSelect.mock.calls[callCount - 1][0];
        // Calculate expected index for DEFAULT_HOPCODE_MODEL dynamically
        const hopcodeModels = getFilteredHopCodeModels();
        const expectedCoderIndex = hopcodeModels.findIndex((m) => m.id === DEFAULT_HOPCODE_MODEL);
        expect(afterChangeCall.initialIndex).toBe(expectedCoderIndex);
    });
});
//# sourceMappingURL=ModelDialog.test.js.map