/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
import { ExtensionManager } from './extension-manager.js';
import { debugLogger, coreEvents } from '@google/gemini-cli-core';
import { createTestMergedSettings } from './settings.js';
import { createExtension } from '../test-utils/createExtension.js';
import { EXTENSIONS_DIRECTORY_NAME } from './extensions/variables.js';
const mockHomedir = vi.hoisted(() => vi.fn(() => '/tmp/mock-home'));
vi.mock('node:os', async (importOriginal) => {
    const actual = await importOriginal();
    return {
        ...actual,
        homedir: mockHomedir,
    };
});
// Mock @google/gemini-cli-core
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
    const actual = await importOriginal();
    return {
        ...actual,
        homedir: mockHomedir,
        // Use actual implementations for loading skills and agents to test hydration
        loadAgentsFromDirectory: actual.loadAgentsFromDirectory,
        loadSkillsFromDir: actual.loadSkillsFromDir,
    };
});
describe('ExtensionManager hydration', () => {
    let extensionManager;
    let tempDir;
    let extensionsDir;
    beforeEach(() => {
        vi.clearAllMocks();
        vi.spyOn(coreEvents, 'emitFeedback');
        vi.spyOn(debugLogger, 'debug').mockImplementation(() => { });
        tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gemini-test-'));
        mockHomedir.mockReturnValue(tempDir);
        // Create the extensions directory that ExtensionManager expects
        extensionsDir = path.join(tempDir, '.gemini', EXTENSIONS_DIRECTORY_NAME);
        fs.mkdirSync(extensionsDir, { recursive: true });
        extensionManager = new ExtensionManager({
            settings: createTestMergedSettings({
                telemetry: { enabled: false },
                experimental: { extensionConfig: true },
            }),
            requestConsent: vi.fn().mockResolvedValue(true),
            requestSetting: vi.fn(),
            workspaceDir: tempDir,
        });
    });
    afterEach(() => {
        try {
            fs.rmSync(tempDir, { recursive: true, force: true });
        }
        catch {
            // ignore
        }
    });
    it('should hydrate skill body with extension settings', async () => {
        const sourceDir = path.join(tempDir, 'source-ext-skill');
        const extensionName = 'skill-hydration-ext';
        createExtension({
            extensionsDir: sourceDir,
            name: extensionName,
            version: '1.0.0',
            settings: [
                {
                    name: 'API Key',
                    description: 'API Key',
                    envVar: 'MY_API_KEY',
                },
            ],
            installMetadata: {
                type: 'local',
                source: path.join(sourceDir, extensionName),
            },
        });
        const extensionPath = path.join(sourceDir, extensionName);
        // Create skill with variable
        const skillsDir = path.join(extensionPath, 'skills');
        const skillSubdir = path.join(skillsDir, 'my-skill');
        fs.mkdirSync(skillSubdir, { recursive: true });
        fs.writeFileSync(path.join(skillSubdir, 'SKILL.md'), `---
name: my-skill
description: test
---
Use key: \${MY_API_KEY}
`);
        await extensionManager.loadExtensions();
        extensionManager.setRequestSetting(async (setting) => {
            if (setting.envVar === 'MY_API_KEY')
                return 'secret-123';
            return '';
        });
        const extension = await extensionManager.installOrUpdateExtension({
            type: 'local',
            source: extensionPath,
        });
        expect(extension.skills).toHaveLength(1);
        expect(extension.skills[0].body).toContain('Use key: secret-123');
    });
    it('should hydrate agent system prompt with extension settings', async () => {
        const sourceDir = path.join(tempDir, 'source-ext-agent');
        const extensionName = 'agent-hydration-ext';
        createExtension({
            extensionsDir: sourceDir,
            name: extensionName,
            version: '1.0.0',
            settings: [
                {
                    name: 'Model Name',
                    description: 'Model',
                    envVar: 'MODEL_NAME',
                },
            ],
            installMetadata: {
                type: 'local',
                source: path.join(sourceDir, extensionName),
            },
        });
        const extensionPath = path.join(sourceDir, extensionName);
        // Create agent with variable
        const agentsDir = path.join(extensionPath, 'agents');
        fs.mkdirSync(agentsDir, { recursive: true });
        fs.writeFileSync(path.join(agentsDir, 'my-agent.md'), `---
name: my-agent
description: test
---
System using model: \${MODEL_NAME}
`);
        await extensionManager.loadExtensions();
        extensionManager.setRequestSetting(async (setting) => {
            if (setting.envVar === 'MODEL_NAME')
                return 'gemini-pro';
            return '';
        });
        const extension = await extensionManager.installOrUpdateExtension({
            type: 'local',
            source: extensionPath,
        });
        expect(extension.agents).toHaveLength(1);
        const agent = extension.agents[0];
        if (agent.kind === 'local') {
            expect(agent.promptConfig.systemPrompt).toContain('System using model: gemini-pro');
        }
        else {
            throw new Error('Expected local agent');
        }
    });
    it('should hydrate hooks with extension settings', async () => {
        const sourceDir = path.join(tempDir, 'source-ext-hooks');
        const extensionName = 'hooks-hydration-ext';
        createExtension({
            extensionsDir: sourceDir,
            name: extensionName,
            version: '1.0.0',
            settings: [
                {
                    name: 'Hook Command',
                    description: 'Cmd',
                    envVar: 'HOOK_CMD',
                },
            ],
            installMetadata: {
                type: 'local',
                source: path.join(sourceDir, extensionName),
            },
        });
        const extensionPath = path.join(sourceDir, extensionName);
        const hooksDir = path.join(extensionPath, 'hooks');
        fs.mkdirSync(hooksDir, { recursive: true });
        fs.writeFileSync(path.join(hooksDir, 'hooks.json'), JSON.stringify({
            hooks: {
                BeforeTool: [
                    {
                        hooks: [
                            {
                                type: 'command',
                                command: 'echo $HOOK_CMD',
                            },
                        ],
                    },
                ],
            },
        }));
        // Enable hooks in settings
        extensionManager = new ExtensionManager({
            settings: createTestMergedSettings({
                telemetry: { enabled: false },
                experimental: { extensionConfig: true },
                hooksConfig: { enabled: true },
            }),
            requestConsent: vi.fn().mockResolvedValue(true),
            requestSetting: vi.fn(),
            workspaceDir: tempDir,
        });
        await extensionManager.loadExtensions();
        extensionManager.setRequestSetting(async (setting) => {
            if (setting.envVar === 'HOOK_CMD')
                return 'hello-world';
            return '';
        });
        const extension = await extensionManager.installOrUpdateExtension({
            type: 'local',
            source: extensionPath,
        });
        expect(extension.hooks).toBeDefined();
        expect(extension.hooks?.BeforeTool).toHaveLength(1);
        expect(extension.hooks?.BeforeTool[0].hooks[0].env?.['HOOK_CMD']).toBe('hello-world');
    });
    it('should pick up new settings after restartExtension', async () => {
        const sourceDir = path.join(tempDir, 'source-ext-restart');
        const extensionName = 'restart-hydration-ext';
        createExtension({
            extensionsDir: sourceDir,
            name: extensionName,
            version: '1.0.0',
            settings: [
                {
                    name: 'Value',
                    description: 'Val',
                    envVar: 'MY_VALUE',
                },
            ],
            installMetadata: {
                type: 'local',
                source: path.join(sourceDir, extensionName),
            },
        });
        const extensionPath = path.join(sourceDir, extensionName);
        const skillsDir = path.join(extensionPath, 'skills');
        const skillSubdir = path.join(skillsDir, 'my-skill');
        fs.mkdirSync(skillSubdir, { recursive: true });
        fs.writeFileSync(path.join(skillSubdir, 'SKILL.md'), '---\nname: my-skill\ndescription: test\n---\nValue is: ${MY_VALUE}');
        await extensionManager.loadExtensions();
        // Initial setting
        extensionManager.setRequestSetting(async () => 'first');
        const extension = await extensionManager.installOrUpdateExtension({
            type: 'local',
            source: extensionPath,
        });
        expect(extension.skills[0].body).toContain('Value is: first');
        const { updateSetting, ExtensionSettingScope } = await import('./extensions/extensionSettings.js');
        const extensionConfig = await extensionManager.loadExtensionConfig(extensionPath);
        const mockRequestSetting = vi.fn().mockResolvedValue('second');
        await updateSetting(extensionConfig, extension.id, 'MY_VALUE', mockRequestSetting, ExtensionSettingScope.USER, process.cwd());
        await extensionManager.restartExtension(extension);
        const reloadedExtension = extensionManager
            .getExtensions()
            .find((e) => e.name === extensionName);
        expect(reloadedExtension.skills[0].body).toContain('Value is: second');
    });
});
//# sourceMappingURL=extension-manager-hydration.test.js.map