/**
 * Phase 2: Load Balancing Profile Detection Tests (type: loadbalancer format)
 * @plan PLAN-20251211issue486c
 * Phase 2: Profile Detection for `type: "loadbalancer"` format
 *
 * These tests verify that the profile loading system correctly detects
 * the {type: "loadbalancer", profiles: [...]} format and loads referenced
 * sub-profiles using ProfileManager.
 */
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
// Mock runtime settings module
const switchActiveProviderMock = vi.fn();
const setActiveModelMock = vi.fn();
const updateActiveProviderBaseUrlMock = vi.fn();
const updateActiveProviderApiKeyMock = vi.fn();
const setActiveModelParamMock = vi.fn();
const clearActiveModelParamMock = vi.fn();
const getActiveModelParamsMock = vi.fn();
const setEphemeralSettingMock = vi.fn();
const getCliRuntimeServicesMock = vi.fn();
const getActiveProviderOrThrowMock = vi.fn();
const isCliStatelessProviderModeEnabledMock = vi
    .fn()
    .mockReturnValue(false);
const isCliRuntimeStatelessReadyMock = vi
    .fn()
    .mockReturnValue(true);
// Stub objects
const configStub = {
    model: undefined,
    ephemerals: new Map(),
    getModel() {
        return this.model;
    },
    setModel(value) {
        this.model = value;
    },
    getEphemeralSetting(key) {
        return this.ephemerals.get(key);
    },
    setEphemeralSetting(key, value) {
        if (value === undefined) {
            this.ephemerals.delete(key);
            return;
        }
        this.ephemerals.set(key, value);
    },
    getEphemeralSettings() {
        return Object.fromEntries(this.ephemerals.entries());
    },
    getContentGeneratorConfig() {
        return { authType: 'key' };
    },
};
const settingsServiceStub = {
    currentProfile: null,
    providerSettings: new Map(),
    setCurrentProfileName(name) {
        this.currentProfile = name;
    },
    getCurrentProfileName() {
        return this.currentProfile;
    },
    set(key, value) {
        if (key === 'currentProfile') {
            this.currentProfile = value ?? null;
        }
    },
    get(key) {
        if (key === 'currentProfile') {
            return this.currentProfile;
        }
        return undefined;
    },
    getProviderSettings(providerName) {
        return (this.providerSettings.get(providerName) ??
            this.providerSettings.set(providerName, {}).get(providerName));
    },
    setProviderSetting(providerName, key, value) {
        const settings = this.getProviderSettings(providerName);
        settings[key] = value;
    },
};
const providerManagerStub = {
    providers: new Map(),
    activeProviderName: null,
    registerProvider(provider) {
        const providerWithName = provider;
        this.providers.set(providerWithName.name, provider);
    },
    getProviderByName(name) {
        return this.providers.get(name) ?? null;
    },
    switchProvider(name) {
        this.activeProviderName = name;
    },
    listProviders() {
        return Array.from(this.providers.keys());
    },
    getActiveProvider() {
        return this.activeProviderName
            ? (this.providers.get(this.activeProviderName) ?? null)
            : null;
    },
    getActiveProviderName() {
        return this.activeProviderName;
    },
};
const profileManagerStub = {
    loadProfile: vi.fn(),
};
// Capture original registerProvider to prevent nested wrappers
const originalRegisterProvider = providerManagerStub.registerProvider;
// Helper function to wrap registerProvider and capture LoadBalancingProvider
function wrapRegisterProviderToCaptureLB() {
    let capturedLBProvider = null;
    const original = providerManagerStub.registerProvider.bind(providerManagerStub);
    providerManagerStub.registerProvider = vi.fn((provider) => {
        const providerWithName = provider;
        if (providerWithName.name === 'load-balancer') {
            capturedLBProvider = provider;
        }
        return original(provider);
    });
    return { getLBProvider: () => capturedLBProvider };
}
beforeEach(() => {
    configStub.model = undefined;
    configStub.ephemerals.clear();
    settingsServiceStub.currentProfile = null;
    settingsServiceStub.providerSettings.clear();
    providerManagerStub.providers.clear();
    providerManagerStub.activeProviderName = null;
    // Restore original registerProvider to prevent nested wrappers
    providerManagerStub.registerProvider = originalRegisterProvider;
    // Register standard providers
    providerManagerStub.registerProvider({
        name: 'gemini',
        getDefaultModel: () => 'gemini-2.0-flash-exp',
    });
    providerManagerStub.registerProvider({
        name: 'openai',
        getDefaultModel: () => 'gpt-4o-mini',
    });
    switchActiveProviderMock.mockImplementation(async (providerName) => {
        providerManagerStub.switchProvider(providerName);
        return {
            infoMessages: [],
            changed: true,
            authType: 'key',
        };
    });
    setActiveModelMock.mockResolvedValue({ nextModel: 'test-model' });
    updateActiveProviderBaseUrlMock.mockResolvedValue({
        message: 'Base URL set',
    });
    updateActiveProviderApiKeyMock.mockResolvedValue({ message: 'API key set' });
    setActiveModelParamMock.mockClear();
    clearActiveModelParamMock.mockClear();
    getActiveModelParamsMock.mockReturnValue({});
    setEphemeralSettingMock.mockImplementation((key, value) => {
        configStub.setEphemeralSetting(key, value);
    });
    getCliRuntimeServicesMock.mockReturnValue({
        config: configStub,
        settingsService: settingsServiceStub,
        providerManager: providerManagerStub,
        profileManager: profileManagerStub,
    });
    getActiveProviderOrThrowMock.mockReturnValue({ name: 'gemini' });
    isCliStatelessProviderModeEnabledMock.mockReturnValue(false);
    isCliRuntimeStatelessReadyMock.mockReturnValue(true);
});
afterEach(() => {
    vi.clearAllMocks();
});
vi.mock('../runtimeSettings.js', () => ({
    switchActiveProvider: switchActiveProviderMock,
    setActiveModel: setActiveModelMock,
    updateActiveProviderBaseUrl: updateActiveProviderBaseUrlMock,
    updateActiveProviderApiKey: updateActiveProviderApiKeyMock,
    setActiveModelParam: setActiveModelParamMock,
    clearActiveModelParam: clearActiveModelParamMock,
    getActiveModelParams: getActiveModelParamsMock,
    setEphemeralSetting: setEphemeralSettingMock,
    getCliRuntimeServices: getCliRuntimeServicesMock,
    getActiveProviderOrThrow: getActiveProviderOrThrowMock,
    isCliStatelessProviderModeEnabled: isCliStatelessProviderModeEnabledMock,
    isCliRuntimeStatelessReady: isCliRuntimeStatelessReadyMock,
}));
const { applyProfileWithGuards } = await import('../profileApplication.js');
describe('Phase 2: Load Balancing Profile Detection (type: loadbalancer format)', () => {
    describe('Profile format detection', () => {
        it('should detect {type: "loadbalancer", profiles: [...]} format @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['syntheticglm46', 'syntheticm2maxstreaming'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {
                    'context-limit': 190000,
                },
            };
            // Mock ProfileManager to return standard profiles for the referenced profiles
            const mockLoadProfile = vi.fn(async (profileName) => {
                if (profileName === 'syntheticglm46') {
                    return {
                        version: 1,
                        provider: 'gemini',
                        model: 'glm-4-flash',
                        modelParams: {},
                        ephemeralSettings: {
                            'auth-key': 'test-key-glm46',
                            'base-url': 'https://api.glm46.example.com',
                        },
                    };
                }
                if (profileName === 'syntheticm2maxstreaming') {
                    return {
                        version: 1,
                        provider: 'openai',
                        model: 'm2-max',
                        modelParams: {},
                        ephemeralSettings: {
                            'auth-key': 'test-key-m2max',
                            'base-url': 'https://api.m2max.example.com',
                        },
                    };
                }
                throw new Error(`Profile '${profileName}' not found`);
            });
            profileManagerStub.loadProfile = mockLoadProfile;
            // Track if LoadBalancingProvider was created
            const { getLBProvider } = wrapRegisterProviderToCaptureLB();
            await applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            });
            // Verify that LoadBalancingProvider was detected and created
            expect(getLBProvider()).not.toBeNull();
            expect(mockLoadProfile).toHaveBeenCalledWith('syntheticglm46');
            expect(mockLoadProfile).toHaveBeenCalledWith('syntheticm2maxstreaming');
        });
        it('should NOT detect standard profile as loadbalancer @plan:PLAN-20251211issue486c @phase:2', async () => {
            const standardProfile = {
                version: 1,
                provider: 'gemini',
                model: 'gemini-2.0-flash-exp',
                modelParams: {},
                ephemeralSettings: {},
            };
            // Track if LoadBalancingProvider was created
            const { getLBProvider } = wrapRegisterProviderToCaptureLB();
            await applyProfileWithGuards(standardProfile, {
                profileName: 'standard-profile',
            });
            // Verify that LoadBalancingProvider was NOT created
            expect(getLBProvider()).toBeNull();
        });
    });
    describe('Sub-profile loading via ProfileManager', () => {
        it('should load each sub-profile using ProfileManager @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['profile1', 'profile2', 'profile3'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            };
            const mockLoadProfile = vi.fn(async (profileName) => ({
                version: 1,
                provider: 'gemini',
                model: `model-${profileName}`,
                modelParams: {},
                ephemeralSettings: {
                    'auth-key': `key-${profileName}`,
                },
            }));
            profileManagerStub.loadProfile = mockLoadProfile;
            await applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            });
            // Verify each sub-profile was loaded
            expect(mockLoadProfile).toHaveBeenCalledWith('profile1');
            expect(mockLoadProfile).toHaveBeenCalledWith('profile2');
            expect(mockLoadProfile).toHaveBeenCalledWith('profile3');
            expect(mockLoadProfile).toHaveBeenCalledTimes(3);
        });
        it('should extract full config from loaded sub-profiles @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['subProfile1'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            };
            const mockLoadProfile = vi.fn(async () => ({
                version: 1,
                provider: 'gemini',
                model: 'gemini-2.0-flash-exp',
                modelParams: {
                    temperature: 0.7,
                    max_tokens: 4096,
                },
                ephemeralSettings: {
                    'auth-key': 'test-api-key',
                    'base-url': 'https://custom.api.example.com',
                    'context-limit': 100000,
                },
            }));
            profileManagerStub.loadProfile = mockLoadProfile;
            const { getLBProvider } = wrapRegisterProviderToCaptureLB();
            await applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            });
            // Verify provider was created (config extraction happens internally)
            expect(getLBProvider()).not.toBeNull();
            expect(mockLoadProfile).toHaveBeenCalledWith('subProfile1');
        });
    });
    describe('Error handling - missing sub-profiles', () => {
        it('should fail-fast when referenced profile does not exist @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['existingProfile', 'nonexistent'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            };
            const mockLoadProfile = vi.fn(async (profileName) => {
                if (profileName === 'existingProfile') {
                    return {
                        version: 1,
                        provider: 'gemini',
                        model: 'test-model',
                        modelParams: {},
                        ephemeralSettings: {},
                    };
                }
                throw new Error(`Profile '${profileName}' not found`);
            });
            profileManagerStub.loadProfile = mockLoadProfile;
            await expect(applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            })).rejects.toThrow(/nonexistent.*does not exist/i);
        });
        it('should provide clear error message with LB profile name and missing sub-profile name @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['missingSubProfile'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            };
            const mockLoadProfile = vi.fn(async (profileName) => {
                throw new Error(`Profile '${profileName}' not found`);
            });
            profileManagerStub.loadProfile = mockLoadProfile;
            let caughtError = null;
            try {
                await applyProfileWithGuards(lbProfile, {
                    profileName: 'myLB',
                });
            }
            catch (error) {
                caughtError = error;
            }
            expect(caughtError).not.toBeNull();
            // Error message should mention both LB profile name and missing sub-profile
            expect(caughtError?.message).toMatch(/myLB/);
            expect(caughtError?.message).toMatch(/missingSubProfile/);
        });
    });
    describe('Error handling - circular references', () => {
        it('should detect circular reference when sub-profile is also a loadbalancer @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['circularLB'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            };
            const mockLoadProfile = vi.fn(async () => 
            // Return another loadbalancer profile (circular)
            ({
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['anotherProfile'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            }));
            profileManagerStub.loadProfile = mockLoadProfile;
            await expect(applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            })).rejects.toThrow(/circular|loadbalancer.*loadbalancer/i);
        });
    });
    describe('LoadBalancingProvider creation from loaded sub-profiles', () => {
        it('should create LoadBalancingProvider with resolved sub-profile configs @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['sub1', 'sub2'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            };
            const mockLoadProfile = vi.fn(async (profileName) => {
                if (profileName === 'sub1') {
                    return {
                        version: 1,
                        provider: 'gemini',
                        model: 'gemini-flash',
                        modelParams: {},
                        ephemeralSettings: {
                            'auth-key': 'key-sub1',
                            'base-url': 'https://sub1.example.com',
                        },
                    };
                }
                return {
                    version: 1,
                    provider: 'openai',
                    model: 'gpt-4o-mini',
                    modelParams: {},
                    ephemeralSettings: {
                        'auth-key': 'key-sub2',
                        'base-url': 'https://sub2.example.com',
                    },
                };
            });
            profileManagerStub.loadProfile = mockLoadProfile;
            const { getLBProvider } = wrapRegisterProviderToCaptureLB();
            await applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            });
            expect(getLBProvider()).not.toBeNull();
            expect(getLBProvider()).toHaveProperty('name', 'load-balancer');
        });
        it('should register LoadBalancingProvider as "load-balancer" @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['sub1'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            };
            const mockLoadProfile = vi.fn(async () => ({
                version: 1,
                provider: 'gemini',
                model: 'gemini-flash',
                modelParams: {},
                ephemeralSettings: {},
            }));
            profileManagerStub.loadProfile = mockLoadProfile;
            await applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            });
            const registeredProvider = providerManagerStub.getProviderByName('load-balancer');
            expect(registeredProvider).not.toBeNull();
            expect(registeredProvider).toHaveProperty('name', 'load-balancer');
        });
        it('should set load-balancer as active provider @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['sub1'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            };
            const mockLoadProfile = vi.fn(async () => ({
                version: 1,
                provider: 'gemini',
                model: 'gemini-flash',
                modelParams: {},
                ephemeralSettings: {},
            }));
            profileManagerStub.loadProfile = mockLoadProfile;
            await applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            });
            const activeProviderName = providerManagerStub.getActiveProviderName();
            expect(activeProviderName).toBe('load-balancer');
        });
        it('should return load-balancer as providerName in result @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['sub1'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            };
            const mockLoadProfile = vi.fn(async () => ({
                version: 1,
                provider: 'gemini',
                model: 'gemini-flash',
                modelParams: {},
                ephemeralSettings: {},
            }));
            profileManagerStub.loadProfile = mockLoadProfile;
            const result = (await applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            }));
            expect(result.providerName).toBe('load-balancer');
        });
    });
    describe('Integration with ephemeralSettings', () => {
        it('should apply top-level ephemeralSettings from LB profile @plan:PLAN-20251211issue486c @phase:2', async () => {
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['sub1'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {
                    'context-limit': 190000,
                    streaming: 'enabled',
                },
            };
            const mockLoadProfile = vi.fn(async () => ({
                version: 1,
                provider: 'gemini',
                model: 'gemini-flash',
                modelParams: {},
                ephemeralSettings: {},
            }));
            profileManagerStub.loadProfile = mockLoadProfile;
            await applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            });
            // Verify ephemeralSettings were applied
            expect(setEphemeralSettingMock).toHaveBeenCalledWith('context-limit', 190000);
            expect(setEphemeralSettingMock).toHaveBeenCalledWith('streaming', 'enabled');
        });
    });
    describe('Detection priority', () => {
        it('should check for type: loadbalancer BEFORE loadBalancer.subProfiles format @plan:PLAN-20251211issue486c @phase:2', async () => {
            // This test ensures the new format is checked first (as per plan requirement)
            // We verify by ensuring that when we pass a type: loadbalancer profile,
            // it uses ProfileManager to load sub-profiles (new behavior)
            // rather than using inline subProfiles (old behavior from 486b)
            const lbProfile = {
                version: 1,
                type: 'loadbalancer',
                policy: 'roundrobin',
                profiles: ['sub1'],
                provider: '',
                model: '',
                modelParams: {},
                ephemeralSettings: {},
            };
            const mockLoadProfile = vi.fn(async () => ({
                version: 1,
                provider: 'gemini',
                model: 'gemini-flash',
                modelParams: {},
                ephemeralSettings: {},
            }));
            profileManagerStub.loadProfile = mockLoadProfile;
            await applyProfileWithGuards(lbProfile, {
                profileName: 'myLB',
            });
            // Verify that ProfileManager.loadProfile was called (new format behavior)
            expect(mockLoadProfile).toHaveBeenCalledWith('sub1');
            // Verify LoadBalancingProvider was created
            const registeredProvider = providerManagerStub.getProviderByName('load-balancer');
            expect(registeredProvider).not.toBeNull();
        });
    });
    describe('Phase 5: Old 486b inline format is no longer supported', () => {
        it('should ignore old inline loadBalancer.subProfiles format and NOT create LB provider @plan:PLAN-20251211issue486c @phase:5', async () => {
            // Phase 5: The old 486b format {loadBalancer: {subProfiles: [...]}} is no longer supported
            // It should be ignored (treated as a standard profile) and NOT create a LoadBalancingProvider
            // Create a profile with the old 486b loadBalancer format
            // Cast as Profile to bypass type checking since this format is no longer supported
            const oldInlineFormatProfile = {
                version: 1,
                provider: 'gemini',
                model: 'gemini-2.0-flash-exp',
                modelParams: {},
                ephemeralSettings: {},
                loadBalancer: {
                    strategy: 'round-robin',
                    subProfiles: [
                        {
                            name: 'sub1',
                            provider: 'gemini',
                            model: 'gemini-flash',
                            apiKey: 'test-key',
                        },
                        {
                            name: 'sub2',
                            provider: 'openai',
                            model: 'gpt-4o-mini',
                            apiKey: 'test-key-2',
                        },
                    ],
                },
            };
            // Track if LoadBalancingProvider was created
            const { getLBProvider } = wrapRegisterProviderToCaptureLB();
            // This should treat the profile as a standard profile (use provider: gemini)
            const result = (await applyProfileWithGuards(oldInlineFormatProfile, {
                profileName: 'oldFormat',
            }));
            // Verify that LoadBalancingProvider was NOT created
            expect(getLBProvider()).toBeNull();
            // Verify the provider was set to 'gemini' (from profile.provider), not 'load-balancer'
            expect(result.providerName).toBe('gemini');
            // Verify the model was set correctly
            expect(result.modelName).toBe('test-model');
        });
    });
});
//# sourceMappingURL=profileApplication.lb.test.js.map