import fs from 'node:fs/promises';
import path from 'node:path';
import { loadServerDefinitions } from '../../config.js';
import { createRuntime } from '../../runtime.js';
import { extractHttpServerTarget, normalizeHttpUrl } from '../http-utils.js';
export async function fileExists(filePath) {
    try {
        await fs.access(filePath);
        return true;
    }
    catch {
        return false;
    }
}
export function ensureInvocationDefaults(invocation, definition) {
    const serverRef = invocation.serverRef ?? definition.name;
    const configPath = invocation.configPath ??
        (definition.source && definition.source.kind === 'local' ? definition.source.path : undefined);
    return {
        ...invocation,
        serverRef,
        configPath,
    };
}
export async function resolveServerDefinition(serverRef, configPath, rootDir) {
    const trimmed = serverRef.trim();
    if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
        // Allow callers to inline a JSON server definition (used by tests + CLI).
        const parsed = JSON.parse(trimmed);
        if (!parsed.name) {
            throw new Error("Inline server definition must include a 'name' field.");
        }
        return { definition: normalizeDefinition(parsed), name: parsed.name };
    }
    const possiblePath = path.resolve(trimmed);
    try {
        const buffer = await fs.readFile(possiblePath, 'utf8');
        const parsed = JSON.parse(buffer);
        if (!parsed.mcpServers || typeof parsed.mcpServers !== 'object') {
            throw new Error(`Config file ${possiblePath} does not contain mcpServers.`);
        }
        const entries = Object.entries(parsed.mcpServers);
        if (entries.length === 0) {
            throw new Error(`Config file ${possiblePath} does not define any servers.`);
        }
        const first = entries[0];
        if (!first) {
            throw new Error(`Config file ${possiblePath} does not define any servers.`);
        }
        const [name, value] = first;
        return {
            definition: normalizeDefinition({
                name,
                ...value,
            }),
            name,
        };
    }
    catch (error) {
        if (error.code !== 'ENOENT') {
            throw error;
        }
    }
    const definitions = await loadServerDefinitions({
        configPath,
        rootDir,
    });
    const matchByName = definitions.find((def) => def.name === trimmed);
    if (matchByName) {
        return { definition: matchByName, name: matchByName.name };
    }
    const httpTarget = extractHttpServerTarget(trimmed);
    if (httpTarget) {
        const normalizedTarget = normalizeHttpUrl(httpTarget);
        if (normalizedTarget) {
            const matchByUrl = definitions.find((def) => {
                if (def.command.kind !== 'http') {
                    return false;
                }
                const normalizedDefinitionUrl = normalizeHttpUrl(def.command.url);
                return normalizedDefinitionUrl === normalizedTarget;
            });
            if (matchByUrl) {
                return { definition: matchByUrl, name: matchByUrl.name };
            }
        }
    }
    throw new Error(`Unknown MCP server '${trimmed}'. Provide a name from config, a JSON file, inline JSON, or an HTTP URL that matches a configured server.`);
}
export async function fetchTools(definition, serverName, configPath, rootDir) {
    // Reuse the runtime helper so bundle builds and CLI generation share the same discovery path.
    const runtime = await createRuntime({
        configPath,
        rootDir,
        servers: configPath ? undefined : [definition],
    });
    try {
        const tools = await runtime.listTools(serverName, { includeSchema: true });
        const derivedDescription = definition.description
            ? undefined
            : await deriveDefinitionDescription(runtime, serverName);
        return { tools, derivedDescription };
    }
    finally {
        await runtime.close(serverName).catch(() => { });
    }
}
async function deriveDefinitionDescription(runtime, serverName) {
    try {
        const context = await runtime.connect(serverName);
        const instructions = typeof context.client.getInstructions === 'function' ? context.client.getInstructions() : undefined;
        const serverInfo = typeof context.client.getServerVersion === 'function' ? context.client.getServerVersion() : undefined;
        const derived = pickDescription(instructions, serverInfo);
        return derived;
    }
    catch {
        // Ignore metadata lookup failures; fallback description will be used instead.
        return undefined;
    }
}
function pickDescription(instructions, serverInfo) {
    const ordered = [instructions, serverInfo?.title, serverInfo?.name];
    for (const candidate of ordered) {
        if (typeof candidate === 'string') {
            const trimmed = candidate.trim();
            if (trimmed.length > 0) {
                return trimmed;
            }
        }
    }
    return undefined;
}
export function normalizeDefinition(def) {
    if (isServerDefinition(def)) {
        return def;
    }
    const name = def.name;
    if (typeof name !== 'string' || name.trim().length === 0) {
        throw new Error('Server definition must include a name.');
    }
    const description = typeof def.description === 'string' ? def.description : undefined;
    const env = toStringRecord(def.env);
    const auth = typeof def.auth === 'string' ? def.auth : undefined;
    const tokenCacheDir = typeof def.tokenCacheDir === 'string' ? def.tokenCacheDir : undefined;
    const clientName = typeof def.clientName === 'string' ? def.clientName : undefined;
    const headers = toStringRecord(def.headers);
    const commandValue = def.command;
    if (isCommandSpec(commandValue)) {
        return {
            name,
            description,
            command: normalizeCommand(commandValue, headers),
            env,
            auth,
            tokenCacheDir,
            clientName,
        };
    }
    if (typeof commandValue === 'string' && commandValue.trim().length > 0) {
        return {
            name,
            description,
            command: toCommandSpec(commandValue, getStringArray(def.args), headers ? { headers } : undefined),
            env,
            auth,
            tokenCacheDir,
            clientName,
        };
    }
    if (Array.isArray(commandValue) && commandValue.length > 0) {
        const [first, ...rest] = commandValue;
        if (typeof first !== 'string' || !rest.every((entry) => typeof entry === 'string')) {
            throw new Error('Command array must contain only strings.');
        }
        return {
            name,
            description,
            command: toCommandSpec(first, rest, headers ? { headers } : undefined),
            env,
            auth,
            tokenCacheDir,
            clientName,
        };
    }
    throw new Error('Server definition must include command information.');
}
function isServerDefinition(value) {
    if (typeof value !== 'object' || value === null) {
        return false;
    }
    const record = value;
    if (typeof record.name !== 'string') {
        return false;
    }
    return isCommandSpec(record.command);
}
function isCommandSpec(value) {
    if (typeof value !== 'object' || value === null) {
        return false;
    }
    const candidate = value;
    if (candidate.kind === 'http') {
        return 'url' in candidate;
    }
    if (candidate.kind === 'stdio') {
        return 'command' in candidate;
    }
    return false;
}
function normalizeCommand(command, headers) {
    if (command.kind === 'http') {
        const urlValue = command.url;
        const url = urlValue instanceof URL ? urlValue : new URL(String(urlValue));
        const mergedHeaders = command.headers ? (headers ? { ...command.headers, ...headers } : command.headers) : headers;
        const normalized = {
            kind: 'http',
            url,
            ...(mergedHeaders ? { headers: mergedHeaders } : {}),
        };
        return normalized;
    }
    return {
        kind: 'stdio',
        command: command.command,
        args: [...command.args],
        cwd: command.cwd ?? process.cwd(),
    };
}
function toCommandSpec(command, args, extra) {
    if (command.startsWith('http://') || command.startsWith('https://')) {
        const httpCommand = {
            kind: 'http',
            url: new URL(command),
            ...(extra?.headers ? { headers: extra.headers } : {}),
        };
        return httpCommand;
    }
    const stdio = {
        kind: 'stdio',
        command,
        args: args ?? [],
        cwd: process.cwd(),
    };
    return stdio;
}
function getStringArray(value) {
    if (!Array.isArray(value)) {
        return undefined;
    }
    const entries = value.filter((item) => typeof item === 'string');
    return entries.length > 0 ? entries : undefined;
}
function toStringRecord(value) {
    if (typeof value !== 'object' || value === null) {
        return undefined;
    }
    const result = {};
    for (const [key, entry] of Object.entries(value)) {
        if (typeof entry === 'string') {
            result[key] = entry;
        }
    }
    return Object.keys(result).length > 0 ? result : undefined;
}
//# sourceMappingURL=definition.js.map