import { spawnSync } from 'node:child_process';
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, realpathSync, rmSync, writeFileSync, } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
const originalEnv = { ...process.env };
afterEach(() => {
    process.env = { ...originalEnv };
    vi.resetModules();
    vi.restoreAllMocks();
});
function createBundledMcpPackage(version = '1.0.0') {
    const packageRoot = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-package-'));
    const bundledDist = path.join(packageRoot, 'package', 'aws-client-agent-mcp', 'dist');
    mkdirSync(bundledDist, { recursive: true });
    writeFileSync(path.join(packageRoot, 'package.json'), JSON.stringify({ name: 'aws-runtime-bridge', version: '9.9.9' }), 'utf-8');
    writeFileSync(path.join(packageRoot, 'package', 'aws-client-agent-mcp', 'package.json'), JSON.stringify({
        name: 'aws-client-agent-mcp',
        version,
        type: 'module',
        main: 'dist/index.js',
        dependencies: {
            '@modelcontextprotocol/sdk': '^1.27.1',
            ws: '^8.14.2',
            yaml: '^2.3.4',
        },
    }), 'utf-8');
    writeFileSync(path.join(packageRoot, 'package', 'aws-client-agent-mcp', 'README.md'), '# aws-client-agent-mcp\n', 'utf-8');
    mkdirSync(path.join(packageRoot, 'node_modules', '@modelcontextprotocol', 'sdk'), {
        recursive: true,
    });
    writeFileSync(path.join(packageRoot, 'node_modules', '@modelcontextprotocol', 'sdk', 'package.json'), JSON.stringify({
        name: '@modelcontextprotocol/sdk',
        version: '1.27.1',
        type: 'module',
        main: 'index.js',
        exports: './index.js',
    }), 'utf-8');
    writeFileSync(path.join(packageRoot, 'node_modules', '@modelcontextprotocol', 'sdk', 'index.js'), 'export const sdkMarker = "resolved-sdk";\n', 'utf-8');
    mkdirSync(path.join(packageRoot, 'node_modules', 'ws'), { recursive: true });
    mkdirSync(path.join(packageRoot, 'node_modules', 'yaml'), { recursive: true });
    writeFileSync(path.join(bundledDist, 'index.js'), 'console.log("mcp v1");\n', 'utf-8');
    return packageRoot;
}
describe('aws-client-agent-mcp service', () => {
    it('prefers explicit command override from env', async () => {
        process.env.AWS_CLIENT_AGENT_MCP_COMMAND = 'custom-aws-client-agent-mcp';
        process.env.AWS_CLIENT_AGENT_MCP_ARGS = JSON.stringify(['--stdio', '--trace']);
        const mod = await import('./aws-client-agent-mcp.js');
        expect(mod.resolveAwsClientAgentMcpCommand()).toEqual({
            source: 'override',
            command: 'custom-aws-client-agent-mcp',
            args: ['--stdio', '--trace'],
        });
    });
    it('uses bundled dist entry after release check', async () => {
        process.env.AWS_CLIENT_AGENT_MCP_COMMAND = '';
        const mod = await import('./aws-client-agent-mcp.js');
        const releasedEntry = path.join(os.homedir(), '.aws-bridge', 'mcp', 'dist', 'index.js');
        const exists = vi.fn((filePath) => filePath === releasedEntry);
        const release = vi.fn(() => releasedEntry);
        const resolved = mod.resolveAwsClientAgentMcpCommand({ exists, release });
        expect(resolved).toEqual({
            source: 'bundled',
            command: process.execPath,
            args: [releasedEntry],
        });
        expect(exists).toHaveBeenCalled();
        expect(release).toHaveBeenCalledOnce();
    });
    it('writes release manifest and skips copy when bundled mcp is unchanged', async () => {
        process.env.AWS_CLIENT_AGENT_MCP_COMMAND = '';
        const packageRoot = createBundledMcpPackage('1.0.0');
        const releasedRoot = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-release-'));
        try {
            const mod = await import('./aws-client-agent-mcp.js');
            const firstRelease = mod.releaseBundledAwsClientAgentMcp({
                packageRoot,
                releasedRoot,
                now: () => '2026-05-04T00:00:00.000Z',
            });
            const releasedIndex = path.join(releasedRoot, 'dist', 'index.js');
            const manifestPath = path.join(releasedRoot, '.release.json');
            const releasedPackageJson = path.join(releasedRoot, 'package.json');
            const releasedNodeModules = path.join(releasedRoot, 'node_modules');
            expect(firstRelease).toBe(releasedIndex);
            expect(existsSync(releasedIndex)).toBe(true);
            expect(JSON.parse(readFileSync(releasedPackageJson, 'utf-8'))).toMatchObject({
                name: 'aws-client-agent-mcp',
                version: '1.0.0',
                dependencies: {
                    '@modelcontextprotocol/sdk': '^1.27.1',
                },
            });
            expect(readFileSync(path.join(releasedRoot, 'README.md'), 'utf-8')).toBe('# aws-client-agent-mcp\n');
            expect(lstatSync(releasedNodeModules).isSymbolicLink()).toBe(true);
            expect(realpathSync(releasedNodeModules)).toBe(realpathSync(path.join(packageRoot, 'node_modules')));
            expect(path.resolve(readlinkSync(releasedNodeModules))).toBe(path.join(packageRoot, 'node_modules'));
            const firstManifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
            expect(firstManifest).toMatchObject({
                bridgeVersion: '9.9.9',
                mcpVersion: '1.0.0',
                releasedAt: '2026-05-04T00:00:00.000Z',
            });
            expect(firstManifest.distHash).toMatch(/^sha256:/);
            writeFileSync(releasedIndex, 'console.log("local marker");\n', 'utf-8');
            rmSync(releasedNodeModules, { recursive: true, force: true });
            mkdirSync(releasedNodeModules, { recursive: true });
            mod.releaseBundledAwsClientAgentMcp({
                packageRoot,
                releasedRoot,
                now: () => '2026-05-05T00:00:00.000Z',
            });
            expect(readFileSync(releasedIndex, 'utf-8')).toBe('console.log("local marker");\n');
            expect(realpathSync(releasedNodeModules)).toBe(realpathSync(path.join(packageRoot, 'node_modules')));
            expect(JSON.parse(readFileSync(manifestPath, 'utf-8')).releasedAt).toBe('2026-05-04T00:00:00.000Z');
        }
        finally {
            rmSync(packageRoot, { recursive: true, force: true });
            rmSync(releasedRoot, { recursive: true, force: true });
        }
    });
    it('refreshes released mcp when bundled dist changes', async () => {
        process.env.AWS_CLIENT_AGENT_MCP_COMMAND = '';
        const packageRoot = createBundledMcpPackage('1.0.0');
        const releasedRoot = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-release-'));
        try {
            const mod = await import('./aws-client-agent-mcp.js');
            const bundledIndex = path.join(packageRoot, 'package', 'aws-client-agent-mcp', 'dist', 'index.js');
            const releasedIndex = path.join(releasedRoot, 'dist', 'index.js');
            mod.releaseBundledAwsClientAgentMcp({ packageRoot, releasedRoot });
            writeFileSync(bundledIndex, 'console.log("mcp v2");\n', 'utf-8');
            mod.releaseBundledAwsClientAgentMcp({
                packageRoot,
                releasedRoot,
                now: () => '2026-05-05T00:00:00.000Z',
            });
            expect(readFileSync(releasedIndex, 'utf-8')).toBe('console.log("mcp v2");\n');
            expect(JSON.parse(readFileSync(path.join(releasedRoot, '.release.json'), 'utf-8')).releasedAt).toBe('2026-05-05T00:00:00.000Z');
        }
        finally {
            rmSync(packageRoot, { recursive: true, force: true });
            rmSync(releasedRoot, { recursive: true, force: true });
        }
    });
    it('releases a runnable package root that resolves bundled mcp runtime dependencies', async () => {
        process.env.AWS_CLIENT_AGENT_MCP_COMMAND = '';
        const packageRoot = createBundledMcpPackage('1.0.0');
        const releasedRoot = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-release-'));
        try {
            const bundledIndex = path.join(packageRoot, 'package', 'aws-client-agent-mcp', 'dist', 'index.js');
            writeFileSync(bundledIndex, 'import { sdkMarker } from "@modelcontextprotocol/sdk";\nconsole.log(sdkMarker);\n', 'utf-8');
            const mod = await import('./aws-client-agent-mcp.js');
            const releasedEntry = mod.releaseBundledAwsClientAgentMcp({ packageRoot, releasedRoot });
            expect(releasedEntry).toBe(path.join(releasedRoot, 'dist', 'index.js'));
            if (!releasedEntry) {
                throw new Error('Expected bundled MCP release entry to exist');
            }
            const result = spawnSync(process.execPath, [releasedEntry], {
                cwd: releasedRoot,
                encoding: 'utf-8',
            });
            expect(result.stderr).toBe('');
            expect(result.status).toBe(0);
            expect(result.stdout.trim()).toBe('resolved-sdk');
        }
        finally {
            rmSync(packageRoot, { recursive: true, force: true });
            rmSync(releasedRoot, { recursive: true, force: true });
        }
    });
    it('releases bundled mcp into home directory when runtime dist is missing', async () => {
        process.env.AWS_CLIENT_AGENT_MCP_COMMAND = '';
        const mod = await import('./aws-client-agent-mcp.js');
        const releasedEntry = path.join(os.homedir(), '.aws-bridge', 'mcp', 'dist', 'index.js');
        let released = false;
        const exists = vi.fn((filePath) => released && filePath === releasedEntry);
        const release = vi.fn(() => {
            released = true;
            return releasedEntry;
        });
        expect(mod.resolveAwsClientAgentMcpCommand({ exists, release })).toEqual({
            source: 'bundled',
            command: process.execPath,
            args: [releasedEntry],
        });
        expect(release).toHaveBeenCalledOnce();
    });
    it('falls back to global aws-client-agent-mcp command when bundle is missing', async () => {
        process.env.AWS_CLIENT_AGENT_MCP_COMMAND = '';
        const mod = await import('./aws-client-agent-mcp.js');
        expect(mod.resolveAwsClientAgentMcpCommand({ exists: () => false, release: () => null })).toEqual({
            source: 'global',
            command: 'aws-client-agent-mcp',
            args: [],
        });
    });
    it('builds aws-mcp server config with inferred scheduler urls', async () => {
        process.env.AWS_CLIENT_AGENT_MCP_COMMAND = 'aws-client-agent-mcp';
        process.env.CUSTOM_SECRET = 'should-not-leak';
        process.env.AWS_RUNTIME_HOME_DIR = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-config-'));
        process.env.AWS_TEST_HOME = process.env.AWS_RUNTIME_HOME_DIR;
        process.env.AWS_RUNTIME_SCHEDULER_BASE_URL = '';
        process.env.AWS_SERVER_URL = '';
        process.env.AWS_MCP_HTTP_URL = '';
        const mod = await import('./aws-client-agent-mcp.js');
        const config = mod.buildAwsMcpServerConfig({
            agentId: 'agent-1',
            workspacePath: '/workspace/demo',
        });
        expect(config.command).toBe('aws-client-agent-mcp');
        expect(config.env.AWS_AGENT_ID).toBe('agent-1');
        expect(config.env.AWS_WORKSPACE_PATH).toBe('/workspace/demo');
        expect(config.env.AWS_SERVER_URL).toBe('ws://localhost:8080/ws/agent');
        expect(config.env.AWS_MCP_HTTP_URL).toBe('http://localhost:8080/mcp/call');
        expect(config.env.CUSTOM_SECRET).toBeUndefined();
    });
    it('repairs duplicated scheduler ports when building MCP URLs', async () => {
        process.env.AWS_CLIENT_AGENT_MCP_COMMAND = 'aws-client-agent-mcp';
        process.env.AWS_RUNTIME_HOME_DIR = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-config-'));
        process.env.AWS_RUNTIME_SCHEDULER_BASE_URL = 'http://127.0.0.1:8080:8080';
        process.env.AWS_SERVER_URL = '';
        process.env.AWS_MCP_HTTP_URL = '';
        const mod = await import('./aws-client-agent-mcp.js');
        const config = mod.buildAwsMcpServerConfig({
            agentId: 'agent-1',
            workspacePath: '/workspace/demo',
        });
        expect(config.env.AWS_SERVER_URL).toBe('ws://127.0.0.1:8080/ws/agent');
        expect(config.env.AWS_MCP_HTTP_URL).toBe('http://127.0.0.1:8080/mcp/call');
    });
    it('ignores malformed AWS_CLIENT_AGENT_MCP_ARGS JSON', async () => {
        process.env.AWS_CLIENT_AGENT_MCP_COMMAND = 'custom-aws-client-agent-mcp';
        process.env.AWS_CLIENT_AGENT_MCP_ARGS = '{bad json';
        const mod = await import('./aws-client-agent-mcp.js');
        expect(mod.resolveAwsClientAgentMcpCommand()).toEqual({
            source: 'override',
            command: 'custom-aws-client-agent-mcp',
            args: [],
        });
    });
});
