import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { registerProviderPlugin } from '../providers';
import { OpenAIProvider } from '../../providers/openai';
vi.mock('react-mcp-hook', () => ({
    CLIENT_CAPABILITIES: {},
    CLIENT_INFO: {},
    CLIENT_PROTOCOL_VERSION: '1.0',
    INITIAL_STATE: {},
    useMcp: vi.fn()
}));
const mocks = vi.hoisted(() => {
    const hostedMcpTool = vi.fn();
    const tool = vi.fn().mockImplementation((config) => ({ ...config }));
    const setDefaultOpenAIClient = vi.fn();
    const setDefaultOpenAIKey = vi.fn();
    const setDefaultModelProvider = vi.fn();
    const setOpenAIAPI = vi.fn();
    const addTraceProcessor = vi.fn();
    const setTracingExportApiKey = vi.fn();
    const traceProvider = {
        setDisabled: vi.fn(),
        startExportLoop: vi.fn()
    };
    const getGlobalTraceProvider = vi.fn(() => traceProvider);
    const openAiConstructor = vi.fn().mockImplementation(() => ({}));
    let lastProviderOptions;
    const OpenAIProviderMock = vi.fn().mockImplementation(function MockOpenAIProvider(options) {
        this.options = options;
        lastProviderOptions = options;
    });
    class AgentMock {
        constructor() { }
    }
    return {
        hostedMcpTool,
        tool,
        setDefaultOpenAIClient,
        setDefaultOpenAIKey,
        setDefaultModelProvider,
        setOpenAIAPI,
        addTraceProcessor,
        setTracingExportApiKey,
        getGlobalTraceProvider,
        traceProvider,
        openAiConstructor,
        AgentMock,
        OpenAIProviderMock,
        getLastProviderOptions: () => lastProviderOptions,
        resetLastProviderOptions: () => {
            lastProviderOptions = undefined;
        },
        resetTraceProvider: () => {
            traceProvider.setDisabled.mockReset();
            traceProvider.startExportLoop.mockReset();
            getGlobalTraceProvider.mockReset();
            getGlobalTraceProvider.mockReturnValue(traceProvider);
        }
    };
});
vi.mock('@openai/agents', () => ({
    Agent: mocks.AgentMock,
    hostedMcpTool: mocks.hostedMcpTool,
    tool: mocks.tool,
    setDefaultOpenAIClient: mocks.setDefaultOpenAIClient,
    setDefaultOpenAIKey: mocks.setDefaultOpenAIKey,
    setDefaultModelProvider: mocks.setDefaultModelProvider,
    OpenAIProvider: mocks.OpenAIProviderMock,
    setOpenAIAPI: mocks.setOpenAIAPI,
    addTraceProcessor: mocks.addTraceProcessor,
    setTracingExportApiKey: mocks.setTracingExportApiKey,
    getGlobalTraceProvider: mocks.getGlobalTraceProvider
}));
vi.mock('openai', () => ({
    __esModule: true,
    default: mocks.openAiConstructor
}));
import { createChatAgent } from '../chatAgent';
beforeAll(async () => {
    await registerProviderPlugin(new OpenAIProvider());
});
describe('ChatAgent MCP server label normalization', () => {
    beforeEach(() => {
        mocks.hostedMcpTool.mockReset();
        mocks.tool.mockReset();
        mocks.setDefaultOpenAIClient.mockReset();
        mocks.setDefaultOpenAIKey.mockReset();
        mocks.setDefaultModelProvider.mockReset();
        mocks.setOpenAIAPI.mockReset();
        mocks.addTraceProcessor.mockReset();
        mocks.setTracingExportApiKey.mockReset();
        mocks.resetTraceProvider();
        mocks.openAiConstructor.mockClear();
        mocks.OpenAIProviderMock.mockClear();
        mocks.resetLastProviderOptions();
    });
    it('normalizes server ids to the mcp_ prefix without duplicating it', () => {
        mocks.hostedMcpTool.mockImplementation((config) => ({ ...config }));
        const options = {
            apiKey: 'test-key',
            providerId: 'openai',
            modelName: 'gpt-test',
            mcpServers: [
                {
                    id: 'abc-123',
                    options: {
                        url: 'https://example.com/abc',
                        autoFetch: false,
                        headers: {},
                        serverName: 'ABC Server',
                        serverEnabled: true
                    }
                },
                {
                    id: 'mcp-xyz-456',
                    options: {
                        url: 'https://example.com/xyz',
                        autoFetch: false,
                        headers: {},
                        serverName: 'XYZ Server',
                        serverEnabled: true
                    }
                },
                {
                    id: 'mcp_qrs_789',
                    options: {
                        url: 'https://example.com/qrs',
                        autoFetch: false,
                        headers: {},
                        serverName: 'QRS Server',
                        serverEnabled: true
                    }
                },
                {
                    id: 'disabled-server',
                    options: {
                        url: 'https://example.com/disabled',
                        autoFetch: false,
                        headers: {},
                        serverName: 'Disabled Server',
                        serverEnabled: false
                    }
                }
            ]
        };
        createChatAgent(options);
        const usedLabels = mocks.hostedMcpTool.mock.calls.map(([config]) => config.serverLabel);
        expect(usedLabels).toEqual([
            'mcp_abc-123',
            'mcp_xyz-456',
            'mcp_qrs_789'
        ]);
    });
    it('does not request approvals when no tools require ask-first permission', () => {
        mocks.hostedMcpTool.mockImplementation((config) => ({ ...config }));
        const options = {
            apiKey: 'test-key',
            providerId: 'openai',
            modelName: 'gpt-test',
            mcpServers: [
                {
                    id: 'search-server',
                    options: {
                        url: 'https://example.com/search',
                        autoFetch: false,
                        headers: {},
                        serverName: 'Search Server',
                        serverEnabled: true,
                        toolPreferences: {
                            search: 'enabled',
                            summarize: 'disabled'
                        }
                    }
                }
            ]
        };
        createChatAgent(options);
        const hostedConfig = mocks.hostedMcpTool.mock.calls[0]?.[0];
        expect(hostedConfig.allowedTools).toEqual(['search']);
        expect(hostedConfig).not.toHaveProperty('requireApproval');
    });
});
describe('ChatAgent OpenAI configuration', () => {
    beforeEach(() => {
        mocks.hostedMcpTool.mockReset();
        mocks.tool.mockReset();
        mocks.setDefaultOpenAIClient.mockReset();
        mocks.setDefaultOpenAIKey.mockReset();
        mocks.setDefaultModelProvider.mockReset();
        mocks.setOpenAIAPI.mockReset();
        mocks.addTraceProcessor.mockReset();
        mocks.setTracingExportApiKey.mockReset();
        mocks.resetTraceProvider();
        mocks.openAiConstructor.mockClear();
        mocks.OpenAIProviderMock.mockClear();
        mocks.resetLastProviderOptions();
    });
    it('passes client headers, log level, and api mode to OpenAI client', () => {
        const clientHeaders = {
            'X-Custom-Header': 'value'
        };
        const options = {
            apiKey: 'another-key',
            providerId: 'openai',
            modelName: 'gpt-4.1-mini',
            instructions: 'be helpful',
            clientHeaders,
            logLevel: 'debug',
            apiMode: 'chat_completions',
            mcpServers: []
        };
        createChatAgent(options);
        expect(mocks.openAiConstructor).toHaveBeenCalledWith(expect.objectContaining({
            apiKey: 'another-key',
            dangerouslyAllowBrowser: true,
            defaultHeaders: expect.objectContaining(clientHeaders),
            logLevel: 'debug'
        }));
        expect(mocks.setOpenAIAPI).toHaveBeenCalledWith('chat_completions');
        expect(mocks.setDefaultOpenAIKey).toHaveBeenCalledWith('another-key');
        expect(mocks.setDefaultOpenAIClient).toHaveBeenCalled();
        expect(mocks.OpenAIProviderMock).toHaveBeenCalledWith(expect.objectContaining({
            openAIClient: mocks.openAiConstructor.mock.results[0]?.value
        }));
        expect(mocks.setDefaultModelProvider).toHaveBeenCalled();
        const providerOptions = mocks.getLastProviderOptions();
        expect(providerOptions).toMatchObject({
            openAIClient: mocks.openAiConstructor.mock.results[0]?.value,
            useResponses: false
        });
    });
    it('normalizes GPT-5 specific model settings before constructing the agent', () => {
        const agent = createChatAgent({
            apiKey: 'key-123',
            providerId: 'openai',
            modelName: 'GPT-5-Preview',
            mcpServers: [],
            modelSettings: {
                maxTokens: '2048',
                reasoningEffort: 'high',
                textVerbosity: 'low',
                temperature: 0.5,
                topP: 0.9,
                reasoning: { effort: 'medium', mode: 'fast' },
                text: { verbosity: 'medium', style: 'formal' }
            }
        });
        const normalizedSettings = agent.options.modelSettings;
        expect(normalizedSettings).toMatchObject({
            maxTokens: 2048,
            reasoning: { effort: 'high', mode: 'fast' },
            text: { verbosity: 'low', style: 'formal' }
        });
        expect(normalizedSettings).not.toHaveProperty('reasoningEffort');
        expect(normalizedSettings).not.toHaveProperty('textVerbosity');
        expect(normalizedSettings).not.toHaveProperty('temperature');
        expect(normalizedSettings).not.toHaveProperty('topP');
    });
});
describe('Attachment tools', () => {
    beforeEach(() => {
        mocks.hostedMcpTool.mockReset();
        mocks.tool.mockReset();
        mocks.setDefaultOpenAIClient.mockReset();
        mocks.setDefaultOpenAIKey.mockReset();
        mocks.setDefaultModelProvider.mockReset();
        mocks.setOpenAIAPI.mockReset();
        mocks.openAiConstructor.mockClear();
        mocks.OpenAIProviderMock.mockClear();
        mocks.resetLastProviderOptions();
    });
    it('reads attachments from the provided cache resolver', async () => {
        mocks.tool.mockImplementation((config) => ({ ...config }));
        const attachments = {
            'att-1': {
                id: 'att-1',
                name: 'report.txt',
                mimeType: 'text/plain',
                size: 128,
                dataUrl: 'data:text/plain;base64,Zm9v',
                dateAdded: 1760036097154
            }
        };
        const options = {
            apiKey: 'key',
            providerId: 'openai',
            modelName: 'gpt-test',
            mcpServers: [],
            getAttachmentCache: () => attachments
        };
        createChatAgent(options);
        const listToolConfig = mocks.tool.mock.calls.find(([config]) => config.name === 'list_cached_files')?.[0];
        expect(listToolConfig).toBeDefined();
        const listResult = await listToolConfig.execute({}, undefined);
        const listPayload = JSON.parse(listResult);
        expect(listPayload.attachments).toHaveLength(1);
        expect(listPayload.attachments[0]).toMatchObject({
            id: 'att-1',
            hasDataUrl: true
        });
        const getToolConfig = mocks.tool.mock.calls.find(([config]) => config.name === 'get_cached_file')?.[0];
        expect(getToolConfig).toBeDefined();
        const getResult = await getToolConfig.execute({ attachmentId: 'att-1' }, undefined);
        const getPayload = JSON.parse(getResult);
        expect(getPayload).toMatchObject({
            id: 'att-1',
            dataPreview: 'data:text/plain;base64,Zm9v'
        });
    });
    it('truncates large data URLs when returning attachment previews', async () => {
        mocks.tool.mockImplementation((config) => ({ ...config }));
        const longDataUrl = 'data:text/plain;base64,' + 'A'.repeat(600);
        const attachments = {
            'att-2': {
                id: 'att-2',
                name: 'long.txt',
                mimeType: 'text/plain',
                size: 2048,
                dataUrl: longDataUrl,
                dateAdded: 1760036097154
            }
        };
        const options = {
            apiKey: 'key',
            providerId: 'openai',
            modelName: 'gpt-test',
            mcpServers: [],
            getAttachmentCache: () => attachments
        };
        createChatAgent(options);
        const getToolConfig = mocks.tool.mock.calls.find(([config]) => config.name === 'get_cached_file')?.[0];
        expect(getToolConfig).toBeDefined();
        const getResult = await getToolConfig.execute({ attachmentId: 'att-2' }, undefined);
        const getPayload = JSON.parse(getResult);
        expect(getPayload.id).toBe('att-2');
        expect(getPayload.dataPreview).toBeDefined();
        const preview = getPayload.dataPreview;
        expect(preview.length).toBeLessThan(longDataUrl.length);
        expect(preview.endsWith('…')).toBe(true);
        expect(longDataUrl.startsWith(preview.slice(0, -1))).toBe(true);
    });
});
