#!/usr/bin/env node
/* eslint-disable */
// Generic stdio MCP smoke runner.
//
// Usage:
//   node scripts/smoke-mcp.js <server-name> [--call toolName] [--args '{"foo":"bar"}']
//
// Server names map to dist/mcp/<name>-server.js. Dummy env vars are injected so
// the server boots without needing real credentials — we're exercising the
// protocol surface, not the upstream API.

const { spawn } = require('child_process');
const path = require('path');

const SERVER_DUMMY_ENV = {
    coolify: { COOLIFY_API_URL: 'https://smoke.example', COOLIFY_API_TOKEN: 'smoke-token' },
    gitea: { GITEA_API_URL: 'https://smoke.example', GITEA_API_TOKEN: 'smoke-token', GITEA_USERNAME: 'smoke' },
    workspace: {
        WORKSPACE_ROOT: path.resolve(__dirname, '..'),
        GITEA_API_URL: 'https://smoke.example',
        GITEA_API_TOKEN: 'smoke-token',
        GITEA_USERNAME: 'smoke',
    },
    'vibn-platform': {
        SESSION_KEY: 'smoke-session',
        GITEA_API_URL: 'https://smoke.example',
        GITEA_API_TOKEN: 'smoke-token',
    },
    agent: { AGENT_RUNNER_URL: 'http://127.0.0.1:65535' },
};

const DEFAULT_CALL = {
    coolify: { name: 'coolify_list_projects', args: {} },
    gitea: { name: 'list_repos', args: {} },
    workspace: { name: 'list_directory', args: { path: '.' } },
    'vibn-platform': {
        name: 'save_memory',
        args: { key: 'smoke_probe', type: 'note', value: 'hello from smoke test' },
    },
    agent: { name: 'get_job_status', args: { job_id: 'smoke-nonexistent' } },
};

const args = process.argv.slice(2);
const serverName = args[0];
if (!serverName) {
    console.error('usage: node scripts/smoke-mcp.js <coolify|gitea|...> [--call name] [--args json]');
    process.exit(2);
}

let callName = DEFAULT_CALL[serverName]?.name;
let callArgs = DEFAULT_CALL[serverName]?.args || {};
for (let i = 1; i < args.length; i++) {
    if (args[i] === '--call') callName = args[++i];
    else if (args[i] === '--args') callArgs = JSON.parse(args[++i]);
}

const serverPath = path.resolve(__dirname, '..', 'dist', 'mcp', `${serverName}-server.js`);
const env = { ...process.env, ...(SERVER_DUMMY_ENV[serverName] || {}) };
// Only inject dummies for keys that aren't already set by the real environment.
for (const [k, v] of Object.entries(SERVER_DUMMY_ENV[serverName] || {})) {
    if (!process.env[k]) env[k] = v;
}

const child = spawn(process.execPath, [serverPath], { stdio: ['pipe', 'pipe', 'inherit'], env });

let buf = '';
const pending = new Map();
let nextId = 1;

function send(method, params) {
    const id = nextId++;
    return new Promise((resolve, reject) => {
        pending.set(id, { resolve, reject });
        child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
    });
}

function notify(method, params) {
    child.stdin.write(JSON.stringify({ jsonrpc: '2.0', method, params }) + '\n');
}

child.stdout.on('data', (chunk) => {
    buf += chunk.toString('utf8');
    let idx;
    while ((idx = buf.indexOf('\n')) !== -1) {
        const line = buf.slice(0, idx).trim();
        buf = buf.slice(idx + 1);
        if (!line) continue;
        try {
            const msg = JSON.parse(line);
            if (msg.id && pending.has(msg.id)) {
                const { resolve, reject } = pending.get(msg.id);
                pending.delete(msg.id);
                if (msg.error) reject(new Error(msg.error.message));
                else resolve(msg.result);
            }
        } catch {
            console.error('[smoke] non-JSON line:', line);
        }
    }
});

child.on('error', (err) => { console.error('[smoke] spawn error:', err); process.exit(2); });

(async () => {
    try {
        const init = await send('initialize', {
            protocolVersion: '2024-11-05',
            capabilities: {},
            clientInfo: { name: 'vibn-smoke', version: '0.0.0' },
        });
        console.log('initialize.serverInfo:', JSON.stringify(init.serverInfo));

        notify('notifications/initialized', {});

        const tools = await send('tools/list', {});
        console.log(`\ntools/list returned ${tools.tools.length} tools:`);
        for (const t of tools.tools) {
            const required = t.inputSchema?.required ?? [];
            console.log(`  - ${t.name}${required.length ? ` (required: ${required.join(', ')})` : ''}`);
        }

        if (callName) {
            const callResult = await send('tools/call', { name: callName, arguments: callArgs });
            console.log(`\ntools/call ${callName}(${JSON.stringify(callArgs)}):`);
            console.log(JSON.stringify(callResult, null, 2).slice(0, 500));
        }

        console.log('\n[smoke] ✓ MCP stdio round-trip successful');
        child.kill();
        process.exit(0);
    } catch (err) {
        console.error('[smoke] FAILED:', err.message);
        child.kill();
        process.exit(1);
    }
})();
