import fs from 'node:fs/promises';
import path from 'node:path';
import { __configInternals } from '../config.js';
import { expandHome } from '../env.js';
import { canonicalKeepAliveName, resolveLifecycle } from '../lifecycle.js';
const TEMP_SOURCE = { kind: 'local', path: '<adhoc>' };
export function resolveEphemeralServer(spec) {
    if (!spec.httpUrl && !spec.stdioCommand) {
        throw new Error('Ad-hoc servers require either --http-url or --stdio.');
    }
    if (spec.httpUrl && spec.stdioCommand) {
        throw new Error('Cannot combine --http-url and --stdio in the same ad-hoc server.');
    }
    if (spec.httpUrl) {
        const url = new URL(spec.httpUrl);
        if (url.protocol !== 'https:' && url.protocol !== 'http:') {
            throw new Error(`Unsupported protocol '${url.protocol}' for --http-url.`);
        }
        if (url.protocol === 'http:' && !spec.allowInsecureHttp) {
            throw new Error('HTTP endpoints require --allow-http to confirm insecure usage.');
        }
        const command = {
            kind: 'http',
            url,
            headers: __configInternals.ensureHttpAcceptHeader(undefined),
        };
        const canonical = spec.name ? undefined : canonicalKeepAliveName(command);
        const name = slugify(spec.name ?? canonical ?? inferNameFromUrl(url));
        const lifecycle = resolveLifecycle(name, undefined, command);
        const definition = {
            name,
            description: spec.description,
            command,
            env: spec.env && Object.keys(spec.env).length > 0 ? spec.env : undefined,
            source: TEMP_SOURCE,
            lifecycle,
        };
        const persistedEntry = {
            baseUrl: url.href,
            ...(spec.description ? { description: spec.description } : {}),
            ...(spec.env && Object.keys(spec.env).length > 0 ? { env: spec.env } : {}),
            ...(lifecycle ? { lifecycle: serializeLifecycle(lifecycle) } : {}),
        };
        return { definition, name, persistedEntry };
    }
    const stdioCommand = spec.stdioCommand;
    const parts = splitCommandLine(stdioCommand);
    if (parts.length === 0) {
        throw new Error('--stdio requires a non-empty command.');
    }
    const [commandBinary, ...commandRest] = parts;
    const commandArgs = commandRest.concat(spec.stdioArgs ?? []);
    const cwd = spec.cwd ? path.resolve(spec.cwd) : process.cwd();
    const command = {
        kind: 'stdio',
        command: commandBinary,
        args: commandArgs,
        cwd,
    };
    const canonical = spec.name ? undefined : canonicalKeepAliveName(command);
    const name = slugify(spec.name ?? canonical ?? inferNameFromCommand(parts));
    const lifecycle = resolveLifecycle(name, undefined, command);
    const definition = {
        name,
        description: spec.description,
        command,
        env: spec.env && Object.keys(spec.env).length > 0 ? spec.env : undefined,
        source: TEMP_SOURCE,
        lifecycle,
    };
    const persistedEntry = {
        command: commandBinary,
        ...(commandArgs.length > 0 ? { args: commandArgs } : {}),
        ...(spec.description ? { description: spec.description } : {}),
        ...(spec.env && Object.keys(spec.env).length > 0 ? { env: spec.env } : {}),
        ...(lifecycle ? { lifecycle: serializeLifecycle(lifecycle) } : {}),
    };
    if (spec.cwd) {
        persistedEntry.cwd = spec.cwd;
    }
    return { definition, name, persistedEntry };
}
export async function persistEphemeralServer(resolution, rawPath) {
    const resolvedPath = path.resolve(expandHome(rawPath));
    let existing;
    try {
        const buffer = await fs.readFile(resolvedPath, 'utf8');
        existing = JSON.parse(buffer);
    }
    catch (error) {
        if (error.code !== 'ENOENT') {
            throw error;
        }
        existing = { mcpServers: {} };
    }
    if (typeof existing.mcpServers !== 'object' || existing.mcpServers === null) {
        existing.mcpServers = {};
    }
    const servers = existing.mcpServers;
    servers[resolution.name] = resolution.persistedEntry;
    await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
    const serialized = `${JSON.stringify(existing, null, 2)}\n`;
    await fs.writeFile(resolvedPath, serialized, 'utf8');
}
function inferNameFromUrl(url) {
    const host = url.hostname.replace(/^www\./, '');
    const pathSegments = url.pathname.split('/').filter(Boolean);
    if (pathSegments.length === 0) {
        return host;
    }
    return `${host}-${pathSegments[pathSegments.length - 1]}`;
}
function inferNameFromCommand(parts) {
    const wrapperPackage = inferPackageFromWrapper(parts);
    if (wrapperPackage) {
        return wrapperPackage;
    }
    const executable = path.basename(parts[0] ?? 'command');
    if (parts.length === 1) {
        return executable;
    }
    const script = parts.find((segment) => segment.endsWith('.ts') || segment.endsWith('.js'));
    if (script) {
        return `${executable}-${path.basename(script, path.extname(script))}`;
    }
    const fallback = parts[1] ?? 'tool';
    return `${executable}-${fallback}`;
}
function inferPackageFromWrapper(parts) {
    if (parts.length < 2) {
        return undefined;
    }
    const executable = path.basename(parts[0] ?? '');
    if (executable !== 'npx') {
        return undefined;
    }
    for (let index = 1; index < parts.length; index += 1) {
        const token = parts[index];
        if (!token) {
            continue;
        }
        if (token === '--') {
            break;
        }
        if (token.startsWith('-')) {
            continue;
        }
        return stripPackageVersion(token);
    }
    return undefined;
}
function stripPackageVersion(token) {
    if (token.startsWith('@')) {
        const secondAt = token.indexOf('@', 1);
        if (secondAt !== -1) {
            return token.slice(0, secondAt);
        }
        return token;
    }
    const versionAt = token.indexOf('@');
    if (versionAt > 0) {
        return token.slice(0, versionAt);
    }
    return token;
}
function slugify(value) {
    return value
        .trim()
        .toLowerCase()
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/^-+|-+$/g, '')
        .replace(/-{2,}/g, '-');
}
export function splitCommandLine(input) {
    const result = [];
    let current = '';
    let inSingle = false;
    let inDouble = false;
    for (let i = 0; i < input.length; i += 1) {
        const char = input.charAt(i);
        if (char === "'" && !inDouble) {
            inSingle = !inSingle;
            continue;
        }
        if (char === '"' && !inSingle) {
            inDouble = !inDouble;
            continue;
        }
        if (!inSingle && !inDouble && /\s/.test(char)) {
            if (current !== '') {
                result.push(current);
                current = '';
            }
            continue;
        }
        if (char === '\\' && !inSingle && i + 1 < input.length) {
            const next = input.charAt(i + 1);
            if (inDouble && next !== '"' && next !== '\\' && next !== '$') {
                current += char;
                continue;
            }
            current += next;
            i += 1;
            continue;
        }
        current += char;
    }
    if (inSingle || inDouble) {
        throw new Error('Unterminated quote in --stdio command.');
    }
    if (current !== '') {
        result.push(current);
    }
    return result;
}
function serializeLifecycle(lifecycle) {
    if (!lifecycle) {
        return undefined;
    }
    if (lifecycle.mode === 'keep-alive' && lifecycle.idleTimeoutMs === undefined) {
        return 'keep-alive';
    }
    if (lifecycle.mode === 'keep-alive') {
        return { mode: 'keep-alive', idleTimeoutMs: lifecycle.idleTimeoutMs };
    }
    return 'ephemeral';
}
//# sourceMappingURL=adhoc-server.js.map