import { existsSync, readFileSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
import { substituteEnvVars } from '../config/env-substitution.js';
import { getConfigPath } from '../config/paths.js';
import { logError } from '../utils/message-queue.js';
/**
 * Show deprecation warning for array format MCP configuration
 */
function showArrayFormatDeprecationWarning() {
    logError('Warning: Array format for MCP servers is deprecated.');
    logError('Please use object format: { "mcpServers": { "serverName": { ... } } }');
}
/**
 * Show deprecation warning for MCP servers in agents.config.json
 */
function showAgentsConfigDeprecationWarning() {
    const _configPath = join(getConfigPath(), '.mcp.json');
    logError('Warning: MCP servers in agents.config.json are deprecated.');
    logError(`Please migrate to ${_configPath}. See documentation for details.`);
}
/**
 * Parse MCP servers from config object, supporting both array and object formats
 * Converts array format to normalized server list with deprecation warning
 */
function parseMCPServers(config) {
    if (typeof config !== 'object' || config === null) {
        return null;
    }
    const configObj = config;
    let mcpServers = null;
    let usedArrayFormat = false;
    // Direct array format at root
    if (Array.isArray(config)) {
        mcpServers = config;
        usedArrayFormat = true;
    }
    else if ('nanocoder' in configObj &&
        configObj.nanocoder &&
        typeof configObj.nanocoder === 'object' &&
        'mcpServers' in configObj.nanocoder &&
        Array.isArray(configObj.nanocoder.mcpServers)) {
        mcpServers = configObj.nanocoder
            .mcpServers;
        usedArrayFormat = true;
    }
    else if ('mcpServers' in configObj && Array.isArray(configObj.mcpServers)) {
        mcpServers = configObj.mcpServers;
        usedArrayFormat = true;
    }
    else if ('mcpServers' in configObj &&
        configObj.mcpServers &&
        typeof configObj.mcpServers === 'object' &&
        !Array.isArray(configObj.mcpServers)) {
        // Claude Code format: { "serverName": { ...serverConfig } }
        mcpServers = Object.entries(configObj.mcpServers).map(([name, serverConfig]) => ({
            name,
            ...serverConfig,
        }));
    }
    // Show deprecation warning if array format was used
    if (usedArrayFormat && mcpServers && mcpServers.length > 0) {
        showArrayFormatDeprecationWarning();
    }
    return mcpServers;
}
/**
 * Load project-level MCP configuration from .mcp.json
 */
export function loadProjectMCPConfig() {
    const configPath = join(process.cwd(), '.mcp.json');
    if (!existsSync(configPath)) {
        return [];
    }
    try {
        const rawData = readFileSync(configPath, 'utf-8');
        const config = JSON.parse(rawData);
        const mcpServers = parseMCPServers(config);
        if (Array.isArray(mcpServers) && mcpServers.length > 0) {
            // Apply environment variable substitution
            const processedServers = substituteEnvVars(mcpServers);
            return processedServers.map((server) => {
                const typedServer = server;
                return {
                    server: {
                        name: typedServer.name,
                        transport: typedServer.transport,
                        command: typedServer.command,
                        args: typedServer.args,
                        env: typedServer.env,
                        url: typedServer.url,
                        headers: typedServer.headers,
                        auth: typedServer.auth,
                        timeout: typedServer.timeout,
                        reconnect: typedServer.reconnect,
                        description: typedServer.description,
                        tags: typedServer.tags,
                        enabled: typedServer.enabled,
                    },
                    source: 'project',
                };
            });
        }
    }
    catch (error) {
        logError(`Failed to load MCP config from ${configPath}: ${String(error)}`);
    }
    return [];
}
/**
 * Load global MCP configuration from ~/.config/nanocoder/.mcp.json
 * Falls back to agents.config.json with deprecation warning
 */
export function loadGlobalMCPConfig() {
    const configDir = getConfigPath();
    const newConfigPath = join(configDir, '.mcp.json');
    // First, check the new .mcp.json location
    if (existsSync(newConfigPath)) {
        try {
            const rawData = readFileSync(newConfigPath, 'utf-8');
            const config = JSON.parse(rawData);
            const mcpServers = parseMCPServers(config);
            if (Array.isArray(mcpServers) && mcpServers.length > 0) {
                const processedServers = substituteEnvVars(mcpServers);
                return processedServers.map((server) => {
                    const typedServer = server;
                    return {
                        server: {
                            name: typedServer.name,
                            transport: typedServer.transport,
                            command: typedServer.command,
                            args: typedServer.args,
                            env: typedServer.env,
                            url: typedServer.url,
                            headers: typedServer.headers,
                            auth: typedServer.auth,
                            timeout: typedServer.timeout,
                            reconnect: typedServer.reconnect,
                            description: typedServer.description,
                            tags: typedServer.tags,
                            enabled: typedServer.enabled,
                        },
                        source: 'global',
                    };
                });
            }
        }
        catch (error) {
            logError(`Failed to load MCP config from ${newConfigPath}: ${String(error)}`);
        }
        return [];
    }
    // Fallback to legacy agents.config.json with deprecation warning
    const homePath = join(homedir(), '.agents.config.json');
    if (existsSync(homePath)) {
        return loadMCPConfigFromAgentsConfig(homePath);
    }
    const legacyConfigPath = join(configDir, 'agents.config.json');
    if (existsSync(legacyConfigPath)) {
        return loadMCPConfigFromAgentsConfig(legacyConfigPath);
    }
    return [];
}
/**
 * Load MCP config from legacy agents.config.json with deprecation warning
 */
function loadMCPConfigFromAgentsConfig(filePath) {
    try {
        const rawData = readFileSync(filePath, 'utf-8');
        const config = JSON.parse(rawData);
        let mcpServers = null;
        let hasMcpServers = false;
        if (config.nanocoder && Array.isArray(config.nanocoder.mcpServers)) {
            mcpServers = config.nanocoder.mcpServers;
            hasMcpServers = mcpServers !== null && mcpServers.length > 0;
        }
        else if (config.nanocoder &&
            config.nanocoder.mcpServers &&
            typeof config.nanocoder.mcpServers === 'object' &&
            !Array.isArray(config.nanocoder.mcpServers)) {
            // Claude Code format in agents.config.json
            mcpServers = Object.entries(config.nanocoder.mcpServers).map(([name, serverConfig]) => ({
                name,
                ...serverConfig,
            }));
            hasMcpServers = mcpServers && mcpServers.length > 0;
        }
        // Show deprecation warning if MCP servers found in agents.config.json
        if (hasMcpServers) {
            showAgentsConfigDeprecationWarning();
        }
        if (Array.isArray(mcpServers) && mcpServers.length > 0) {
            const processedServers = substituteEnvVars(mcpServers);
            return processedServers.map((server) => {
                const typedServer = server;
                return {
                    server: {
                        name: typedServer.name,
                        transport: typedServer.transport,
                        command: typedServer.command,
                        args: typedServer.args,
                        env: typedServer.env,
                        url: typedServer.url,
                        headers: typedServer.headers,
                        auth: typedServer.auth,
                        timeout: typedServer.timeout,
                        reconnect: typedServer.reconnect,
                        description: typedServer.description,
                        tags: typedServer.tags,
                        enabled: typedServer.enabled,
                    },
                    source: 'global',
                };
            });
        }
    }
    catch (error) {
        logError(`Failed to load MCP config from ${filePath}: ${String(error)}`);
    }
    return [];
}
/**
 * Merge project-level and global MCP configurations
 * ALL servers from both locations are loaded (project servers shown first)
 * No overriding - each unique server is preserved
 */
export function mergeMCPConfigs(projectServers, globalServers) {
    const serverMap = new Map();
    // Add project servers first (displayed first in UI)
    for (const projectServer of projectServers) {
        serverMap.set(projectServer.server.name, projectServer);
    }
    // Add global servers (only if not already added from project)
    for (const globalServer of globalServers) {
        if (!serverMap.has(globalServer.server.name)) {
            serverMap.set(globalServer.server.name, globalServer);
        }
    }
    return Array.from(serverMap.values());
}
/**
 * Load all MCP configurations with proper hierarchy and merging
 */
export function loadAllMCPConfigs() {
    const projectServers = loadProjectMCPConfig();
    // Skip loading global servers in test environment to allow test isolation
    const globalServers = process.env.NODE_ENV === 'test' ? [] : loadGlobalMCPConfig();
    return mergeMCPConfigs(projectServers, globalServers);
}
/**
 * Load provider configurations from all available levels (project and global)
 * This mirrors the approach used for MCP servers to support hierarchical loading
 */
export function loadAllProviderConfigs() {
    const projectProviders = loadProjectProviderConfigs();
    // Skip loading global providers in test environment to allow test isolation
    const globalProviders = process.env.NODE_ENV === 'test' ? [] : loadGlobalProviderConfigs();
    // Merge providers with project providers taking precedence over global ones
    // If a provider with the same name exists in both, project version wins
    const providerMap = new Map();
    // Add global providers first (lower priority)
    for (const provider of globalProviders) {
        providerMap.set(provider.name, provider);
    }
    // Add project providers (higher priority) - they will override global ones
    for (const provider of projectProviders) {
        providerMap.set(provider.name, provider);
    }
    return Array.from(providerMap.values());
}
/**
 * Load provider configurations from project-level files
 */
function loadProjectProviderConfigs() {
    // Try to find provider configs in project-level config files
    const configPath = join(process.cwd(), 'agents.config.json');
    if (existsSync(configPath)) {
        try {
            const rawData = readFileSync(configPath, 'utf-8');
            const config = JSON.parse(rawData);
            if (config.nanocoder && Array.isArray(config.nanocoder.providers)) {
                // Apply environment variable substitution
                const processedProviders = substituteEnvVars(config.nanocoder.providers);
                return processedProviders;
            }
            else if (Array.isArray(config.providers)) {
                // Apply environment variable substitution
                const processedProviders = substituteEnvVars(config.providers);
                return processedProviders;
            }
        }
        catch (error) {
            logError(`Failed to load project provider config from ${configPath}: ${String(error)}`);
        }
    }
    return [];
}
/**
 * Load provider configurations from global config files using the same path resolution as the original system
 */
function loadGlobalProviderConfigs() {
    // Use the same path resolution logic as getClosestConfigFile
    const configDir = getConfigPath();
    // Check the $HOME for a hidden file. This should only be for legacy support
    const homePath = join(homedir(), '.agents.config.json');
    if (existsSync(homePath)) {
        const providers = loadProviderConfigFromFile(homePath);
        if (providers.length > 0) {
            return providers;
        }
    }
    // Next, lets look for a user level config.
    const configPath = join(configDir, 'agents.config.json');
    if (existsSync(configPath)) {
        return loadProviderConfigFromFile(configPath);
    }
    // Note: We don't check CWD here as that's handled by project-level loading
    return [];
}
// Helper function to load provider config from a specific file
function loadProviderConfigFromFile(filePath) {
    try {
        const rawData = readFileSync(filePath, 'utf-8');
        const config = JSON.parse(rawData);
        if (config.nanocoder && Array.isArray(config.nanocoder.providers)) {
            // Apply environment variable substitution
            const processedProviders = substituteEnvVars(config.nanocoder.providers);
            return processedProviders;
        }
        else if (Array.isArray(config.providers)) {
            // Apply environment variable substitution
            const processedProviders = substituteEnvVars(config.providers);
            return processedProviders;
        }
    }
    catch (error) {
        logError(`Failed to load provider config from ${filePath}: ${String(error)}`);
    }
    return [];
}
//# sourceMappingURL=mcp-config-loader.js.map