import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { McpSchemas } from './schemas.js';
const CONFIG_FILE_NAME = 'mcp.json';
const DEFAULT_CONFIG_CONTENT = JSON.stringify({ mcpServers: {} }, null, 2);
export class FileMcpConfigRepository {
    workspaceRoot;
    stateRoot;
    constructor(options) {
        this.workspaceRoot = resolve(options.workspaceRoot);
        this.stateRoot = resolve(options.stateRoot);
    }
    static resolvePath(stateRoot) {
        return join(stateRoot, CONFIG_FILE_NAME);
    }
    read() {
        const configPath = FileMcpConfigRepository.resolvePath(this.stateRoot);
        if (!existsSync(configPath)) {
            return {
                configPath,
                servers: [],
                issues: [],
            };
        }
        try {
            const parsed = McpSchemas.parseRawConfig(JSON.parse(readFileSync(configPath, 'utf8')));
            return {
                configPath,
                ...normalizeRawConfig(parsed, {
                    configPath,
                    workspaceRoot: this.workspaceRoot,
                }),
            };
        }
        catch (error) {
            return {
                configPath,
                servers: [],
                issues: [{
                        code: 'config_invalid',
                        path: configPath,
                        message: error instanceof Error ? error.message : String(error),
                    }],
            };
        }
    }
    ensureDocument() {
        const configPath = FileMcpConfigRepository.resolvePath(this.stateRoot);
        if (!existsSync(configPath)) {
            mkdirSync(dirname(configPath), { recursive: true });
            writeFileSync(configPath, `${DEFAULT_CONFIG_CONTENT}\n`, 'utf8');
        }
        return this.readDocument();
    }
    readDocument() {
        const configPath = FileMcpConfigRepository.resolvePath(this.stateRoot);
        if (!existsSync(configPath)) {
            return {
                configPath,
                content: `${DEFAULT_CONFIG_CONTENT}\n`,
                exists: false,
                issues: [],
            };
        }
        const content = readFileSync(configPath, 'utf8');
        return {
            configPath,
            content,
            exists: true,
            issues: this.read().issues,
        };
    }
    writeDocument(content) {
        const configPath = FileMcpConfigRepository.resolvePath(this.stateRoot);
        const formatted = formatConfigContent(content);
        mkdirSync(dirname(configPath), { recursive: true });
        writeFileSync(configPath, formatted, 'utf8');
        return this.readDocument();
    }
}
export class FileMcpActivationRepository {
    filePath;
    constructor(options) {
        this.filePath = FileMcpActivationRepository.resolvePath(options.stateRoot);
    }
    static resolvePath(stateRoot) {
        return join(stateRoot, 'mcp', 'activation.json');
    }
    read() {
        if (!existsSync(this.filePath)) {
            return McpSchemas.emptyActivationStore();
        }
        try {
            return McpSchemas.parseActivationStore(JSON.parse(readFileSync(this.filePath, 'utf8')));
        }
        catch {
            return McpSchemas.emptyActivationStore();
        }
    }
    write(store) {
        mkdirSync(dirname(this.filePath), { recursive: true });
        writeFileSync(this.filePath, `${JSON.stringify(McpSchemas.parseActivationStore(store), null, 2)}\n`, 'utf8');
    }
}
export class FileMcpCatalogRepository {
    filePath;
    constructor(options) {
        this.filePath = FileMcpCatalogRepository.resolvePath(options.stateRoot);
    }
    static resolvePath(stateRoot) {
        return join(stateRoot, 'mcp', 'catalog.json');
    }
    read() {
        if (!existsSync(this.filePath)) {
            return McpSchemas.emptyCatalogStore();
        }
        try {
            return McpSchemas.parseCatalogStore(JSON.parse(readFileSync(this.filePath, 'utf8')));
        }
        catch {
            return McpSchemas.emptyCatalogStore();
        }
    }
    write(store) {
        mkdirSync(dirname(this.filePath), { recursive: true });
        writeFileSync(this.filePath, `${JSON.stringify(McpSchemas.parseCatalogStore(store), null, 2)}\n`, 'utf8');
    }
}
function normalizeRawConfig(config, options) {
    const standardServers = config.mcpServers ?? {};
    const vscodeServers = config.servers ?? {};
    const standard = Object.entries(standardServers).map(([id, server]) => normalizeServer(id, server, {
        source: 'standard',
        ...options,
    }));
    const vscode = Object.entries(vscodeServers)
        .filter(([id]) => standardServers[id] === undefined)
        .map(([id, server]) => normalizeServer(id, server, {
        source: 'vscode',
        ...options,
    }));
    const normalized = [...standard, ...vscode];
    return {
        servers: normalized.flatMap((result) => result.server ? [result.server] : []),
        issues: normalized.flatMap((result) => result.issues),
    };
}
function formatConfigContent(content) {
    const raw = content.trim().length > 0 ? JSON.parse(content) : { mcpServers: {} };
    return `${JSON.stringify(McpSchemas.parseRawConfig(raw), null, 2)}\n`;
}
function normalizeServer(id, raw, options) {
    const transport = normalizeTransport(raw.type ?? raw.transport, raw);
    const path = `${options.configPath}#${id}`;
    if (!isSafeIdentifier(id)) {
        return {
            issues: [{
                    code: 'server_invalid',
                    path,
                    message: `MCP server id must use letters, numbers, underscores, or hyphens: ${id}`,
                }],
        };
    }
    if (!transport) {
        return {
            issues: [{
                    code: 'unsupported_transport',
                    path,
                    message: 'MCP server must define a supported transport: stdio, http, streamable-http, or sse.',
                }],
        };
    }
    if (transport === 'stdio') {
        if (!raw.command) {
            return {
                issues: [{
                        code: 'server_invalid',
                        path,
                        message: 'Stdio MCP server requires command.',
                    }],
            };
        }
        const server = {
            id,
            transport,
            source: options.source,
            command: raw.command,
            args: raw.args ?? [],
            cwd: raw.cwd ? resolveTemplatePath(raw.cwd, options.workspaceRoot) : undefined,
            env: raw.env ?? {},
            envFile: raw.envFile,
            tools: raw.tools,
        };
        return { server, issues: [] };
    }
    if (!raw.url) {
        return {
            issues: [{
                    code: 'server_invalid',
                    path,
                    message: 'HTTP MCP server requires url.',
                }],
        };
    }
    try {
        const url = new URL(raw.url);
        const server = {
            id,
            transport,
            source: options.source,
            url: url.toString(),
            headers: raw.headers ?? {},
            tools: raw.tools,
        };
        return { server, issues: [] };
    }
    catch {
        return {
            issues: [{
                    code: 'server_invalid',
                    path,
                    message: `Invalid MCP server URL: ${raw.url}`,
                }],
        };
    }
}
function normalizeTransport(value, raw) {
    if (!value && raw.command) {
        return 'stdio';
    }
    if (value === 'streamable-http') {
        return 'http';
    }
    if (value === 'stdio' || value === 'http' || value === 'sse') {
        return value;
    }
    return undefined;
}
function resolveTemplatePath(value, workspaceRoot) {
    return value.replaceAll('${workspaceFolder}', workspaceRoot);
}
function isSafeIdentifier(value) {
    return /^[A-Za-z0-9_-]+$/.test(value);
}
//# sourceMappingURL=repositories.js.map