/**
 * OMK Routing — MCP configuration loading & redaction
 * Extracted from routing.ts to break God Module coupling
 */
import { existsSync, readFileSync } from "fs";
import { homedir } from "os";
import { isAbsolute, join, relative, resolve, sep } from "path";
import { normalizeUserHomePath } from "../../util/fs.js";
const SECRET_PATTERNS = ["apikey", "token", "password", "secret", "authorization"];
function isSecretKey(key) {
    const normalized = key.toLowerCase().replace(/[_-]/g, "");
    return SECRET_PATTERNS.some((pattern) => normalized === pattern || normalized.endsWith(pattern));
}
function isPlainObject(value) {
    return value !== null && typeof value === "object" && !Array.isArray(value);
}
function deepMerge(target, source) {
    const result = { ...target };
    for (const key of Object.keys(source)) {
        const sVal = source[key];
        const tVal = result[key];
        if (isPlainObject(sVal) && isPlainObject(tVal)) {
            result[key] = deepMerge(tVal, sVal);
        }
        else {
            result[key] = sVal;
        }
    }
    return result;
}
export function redactMcpConfig(cfg) {
    if (isPlainObject(cfg)) {
        const result = {};
        for (const [key, value] of Object.entries(cfg)) {
            if (isSecretKey(key)) {
                result[key] = "***";
            }
            else if (isPlainObject(value)) {
                result[key] = redactMcpConfig(value);
            }
            else if (Array.isArray(value)) {
                result[key] = value.map((item) => redactMcpConfig(item));
            }
            else {
                result[key] = value;
            }
        }
        return result;
    }
    if (Array.isArray(cfg)) {
        return cfg.map((item) => redactMcpConfig(item));
    }
    return cfg;
}
function getRoutingUserHome() {
    return (normalizeUserHomePath(process.env.OMK_ORIGINAL_HOME)
        ?? normalizeUserHomePath(process.env.HOME)
        ?? normalizeUserHomePath(homedir())
        ?? homedir());
}
function formatRoutingPath(root, path) {
    const resolvedPath = resolve(path);
    const resolvedRoot = resolve(root);
    const rootRelative = relative(resolvedRoot, resolvedPath);
    if (rootRelative === "")
        return ".";
    if (isRelativeChildPath(rootRelative))
        return rootRelative;
    const home = resolve(getRoutingUserHome());
    const homeRelative = relative(home, resolvedPath);
    if (homeRelative === "")
        return "~";
    if (isRelativeChildPath(homeRelative))
        return `~/${homeRelative.split(sep).join("/")}`;
    return resolvedPath;
}
function isRelativeChildPath(path) {
    return path !== "" && path !== ".." && !path.startsWith(`..${sep}`) && !path.startsWith("../") && !isAbsolute(path);
}
function mcpConfigErrorMessage(err) {
    if (err instanceof SyntaxError)
        return "invalid JSON";
    if (err && typeof err === "object" && "code" in err && typeof err.code === "string") {
        return `unreadable MCP config (${err.code})`;
    }
    return "invalid MCP config";
}
function createMcpConfigDiagnostic(root, source, path, err) {
    return {
        kind: "mcp-config",
        source,
        path: formatRoutingPath(root, path),
        message: mcpConfigErrorMessage(err),
    };
}
export function loadMergedMcpConfigSync(projectRoot, scope) {
    const root = resolve(projectRoot);
    const servers = {};
    const sources = new Map();
    const diagnostics = [];
    if (scope === "none") {
        return { servers, sources, diagnostics };
    }
    const globalFiles = scope === "all" ? [join(getRoutingUserHome(), ".kimi", "mcp.json"), join(getRoutingUserHome(), ".omk", "mcp.json")] : [];
    for (const path of globalFiles) {
        if (!existsSync(path))
            continue;
        try {
            const parsed = JSON.parse(readFileSync(path, "utf-8"));
            for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
                if (!sources.has(name)) {
                    servers[name] = cfg;
                    sources.set(name, "global");
                }
            }
        }
        catch (err) {
            diagnostics.push(createMcpConfigDiagnostic(root, "global", path, err));
        }
    }
    const projectFiles = [
        join(root, ".omk", "mcp.json"),
        join(root, ".kimi", "mcp.json"),
    ];
    for (const path of projectFiles) {
        if (!existsSync(path))
            continue;
        try {
            const parsed = JSON.parse(readFileSync(path, "utf-8"));
            for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
                if (!sources.has(name) || sources.get(name) === "global") {
                    if (sources.has(name) && isPlainObject(servers[name]) && isPlainObject(cfg)) {
                        servers[name] = deepMerge(servers[name], cfg);
                    }
                    else {
                        servers[name] = cfg;
                    }
                    sources.set(name, "project");
                }
            }
        }
        catch (err) {
            diagnostics.push(createMcpConfigDiagnostic(root, "project", path, err));
        }
    }
    if (!sources.has("omk-project")) {
        servers["omk-project"] = { type: "builtin", command: "omk", args: ["mcp", "serve", "omk-project"] };
        sources.set("omk-project", "builtin");
    }
    return { servers, sources, diagnostics };
}
export function loadMergedMcpConfig(projectRoot, scope) {
    return Promise.resolve(loadMergedMcpConfigSync(projectRoot, scope));
}
