/**
 * Codex MCP Adapter
 *
 * Codex does not receive per-session MCP servers from the current SDK surface.
 * This adapter persists MCP servers into Codex's config.toml while preserving
 * unrelated user-owned config text.
 */
const MCP_TABLE_PATTERN = /^\s*\[mcp_servers\.("(?:\\.|[^"])+"|[A-Za-z0-9_-]+)\]\s*$/;
const TABLE_PATTERN = /^\s*\[[^\]]+\]\s*$/;
const KEY_VALUE_PATTERN = /^\s*([A-Za-z0-9_-]+)\s*=\s*(.*)\s*$/;
async function readTextFile(filePath) {
    try {
        const fs = await import('node:fs/promises');
        return await fs.readFile(filePath, 'utf-8');
    }
    catch {
        return '';
    }
}
async function writeTextFile(filePath, content) {
    const fs = await import('node:fs/promises');
    const path = await import('node:path');
    await fs.mkdir(path.dirname(filePath), { recursive: true });
    await fs.writeFile(filePath, content, 'utf-8');
}
function createId(prefix) {
    const timestamp = Date.now().toString(36);
    const random = Math.random().toString(36).slice(2, 8);
    return `${prefix}_${timestamp}_${random}`;
}
function escapeTomlString(value) {
    return String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
}
function unescapeTomlString(value) {
    return value.replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
function quoteTomlString(value) {
    return `"${escapeTomlString(value)}"`;
}
function parseTomlString(raw) {
    const trimmed = raw.trim();
    if (!trimmed.startsWith('"') || !trimmed.endsWith('"')) {
        return undefined;
    }
    return unescapeTomlString(trimmed.slice(1, -1));
}
function parseTomlStringArray(raw) {
    const trimmed = raw.trim();
    if (!trimmed.startsWith('[') || !trimmed.endsWith(']')) {
        return undefined;
    }
    const values = [];
    let current = '';
    let inString = false;
    let escaping = false;
    for (const char of trimmed.slice(1, -1)) {
        if (escaping) {
            current += char;
            escaping = false;
            continue;
        }
        if (char === '\\' && inString) {
            escaping = true;
            continue;
        }
        if (char === '"') {
            if (inString) {
                values.push(unescapeTomlString(current));
                current = '';
            }
            inString = !inString;
            continue;
        }
        if (inString) {
            current += char;
        }
    }
    return values;
}
function formatTomlArray(values) {
    return `[${(values ?? []).map(quoteTomlString).join(', ')}]`;
}
function parseTableName(rawName) {
    const trimmed = rawName.trim();
    if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
        return unescapeTomlString(trimmed.slice(1, -1));
    }
    return trimmed;
}
function isNestedMcpTableForServer(line, serverName) {
    const trimmed = line.trim();
    const quotedPrefix = `[mcp_servers.${quoteTomlString(serverName)}.`;
    const barePrefix = `[mcp_servers.${serverName}.`;
    return trimmed.startsWith(quotedPrefix) || trimmed.startsWith(barePrefix);
}
function findMcpBlocks(raw) {
    const lines = raw.split(/\r?\n/);
    const blocks = [];
    let currentName = null;
    let currentStart = null;
    const flushCurrent = (end) => {
        if (currentName === null || currentStart === null) {
            return;
        }
        blocks.push({
            name: currentName,
            start: currentStart,
            end,
            lines: lines.slice(currentStart, end),
        });
        currentName = null;
        currentStart = null;
    };
    lines.forEach((line, index) => {
        const mcpMatch = line.match(MCP_TABLE_PATTERN);
        if (mcpMatch) {
            flushCurrent(index);
            currentName = parseTableName(mcpMatch[1]);
            currentStart = index;
            return;
        }
        if (line.match(TABLE_PATTERN) && currentName && !isNestedMcpTableForServer(line, currentName)) {
            flushCurrent(index);
        }
    });
    flushCurrent(lines.length);
    return blocks;
}
function buildCodexMcpBlock(server) {
    const spec = server.server;
    const lines = [`[mcp_servers.${quoteTomlString(server.name)}]`];
    if (spec.command) {
        lines.push(`command = ${quoteTomlString(spec.command)}`);
    }
    if (spec.args) {
        lines.push(`args = ${formatTomlArray(spec.args)}`);
    }
    if (spec.cwd) {
        lines.push(`cwd = ${quoteTomlString(spec.cwd)}`);
    }
    if (spec.url) {
        lines.push(`url = ${quoteTomlString(spec.url)}`);
    }
    const envEntries = Object.entries(spec.env ?? {}).sort(([left], [right]) => left.localeCompare(right));
    if (envEntries.length > 0) {
        lines.push('');
        lines.push(`[mcp_servers.${quoteTomlString(server.name)}.env]`);
        for (const [key, value] of envEntries) {
            lines.push(`${key} = ${quoteTomlString(value)}`);
        }
    }
    return lines.join('\n');
}
function removeCodexMcpBlock(raw, serverName) {
    const lines = raw.split(/\r?\n/);
    const block = findMcpBlocks(raw).find((entry) => entry.name === serverName);
    if (!block) {
        return raw;
    }
    const nextLines = [...lines.slice(0, block.start), ...lines.slice(block.end)];
    return nextLines.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd();
}
function upsertCodexMcpBlock(raw, server) {
    const withoutExisting = removeCodexMcpBlock(raw, server.name).trimEnd();
    const block = buildCodexMcpBlock(server);
    if (!withoutExisting) {
        return `${block}\n`;
    }
    return `${withoutExisting}\n\n${block}\n`;
}
function parseCodexBlock(block) {
    const spec = { type: 'stdio' };
    const env = {};
    let inEnv = false;
    for (const line of block.lines.slice(1)) {
        const trimmed = line.trim();
        if (!trimmed || trimmed.startsWith('#')) {
            continue;
        }
        if (trimmed === `[mcp_servers.${quoteTomlString(block.name)}.env]`) {
            inEnv = true;
            continue;
        }
        const match = line.match(KEY_VALUE_PATTERN);
        if (!match) {
            continue;
        }
        const [, key, rawValue] = match;
        if (inEnv) {
            const value = parseTomlString(rawValue);
            if (value !== undefined) {
                env[key] = value;
            }
            continue;
        }
        if (key === 'args') {
            spec.args = parseTomlStringArray(rawValue) ?? [];
            continue;
        }
        if (key === 'command' || key === 'cwd' || key === 'url') {
            const value = parseTomlString(rawValue);
            if (value !== undefined) {
                spec[key] = value;
            }
        }
    }
    if (Object.keys(env).length > 0) {
        spec.env = env;
    }
    if (spec.url) {
        spec.type = 'sse';
    }
    return spec;
}
export class CodexMcpAdapter {
    constructor(configPath) {
        this.configPath = configPath;
        this.appId = 'codex';
    }
    async importServers() {
        const raw = await readTextFile(this.configPath);
        return findMcpBlocks(raw).map((block) => ({
            id: createId('mcp'),
            name: block.name,
            server: parseCodexBlock(block),
            apps: {
                acode: false,
                claude: false,
                claudecode: false,
                codex: true,
                gemini: false,
                opencode: false,
                openclaw: false,
            },
        }));
    }
    async syncServer(server) {
        const raw = await readTextFile(this.configPath);
        await writeTextFile(this.configPath, upsertCodexMcpBlock(raw, server));
    }
    async removeServer(server) {
        const raw = await readTextFile(this.configPath);
        const next = removeCodexMcpBlock(raw, server.name);
        if (next === raw) {
            return;
        }
        await writeTextFile(this.configPath, next ? `${next}\n` : '');
    }
}
