/**
 * This file contains utility functions for handling MCPB configuration,
 * including variable replacement and MCP server configuration generation.
 */
/**
 * Recursively replaces variables in any value. Handles strings, arrays, and objects.
 *
 * @param value The value to process
 * @param variables Object containing variable replacements
 * @returns The processed value with all variables replaced
 */
export function replaceVariables(value, variables) {
    if (typeof value === "string") {
        let result = value;
        // Replace all variables in the string
        for (const [key, replacement] of Object.entries(variables)) {
            const pattern = new RegExp(`\\$\\{${key}\\}`, "g");
            // Check if this pattern actually exists in the string
            if (result.match(pattern)) {
                if (Array.isArray(replacement)) {
                    console.warn(`Cannot replace ${key} with array value in string context: "${value}"`, { key, replacement });
                }
                else {
                    result = result.replace(pattern, replacement);
                }
            }
        }
        return result;
    }
    else if (Array.isArray(value)) {
        // For arrays, we need to handle special case of array expansion
        const result = [];
        for (const item of value) {
            if (typeof item === "string" &&
                item.match(/^\$\{user_config\.[^}]+\}$/)) {
                // This is a user config variable that might expand to multiple values
                const varName = item.match(/^\$\{([^}]+)\}$/)?.[1];
                if (varName && variables[varName]) {
                    const replacement = variables[varName];
                    if (Array.isArray(replacement)) {
                        // Expand array inline
                        result.push(...replacement);
                    }
                    else {
                        result.push(replacement);
                    }
                }
                else {
                    // Variable not found, keep original
                    result.push(item);
                }
            }
            else {
                // Recursively process non-variable items
                result.push(replaceVariables(item, variables));
            }
        }
        return result;
    }
    else if (value && typeof value === "object") {
        const result = {};
        for (const [key, val] of Object.entries(value)) {
            result[key] = replaceVariables(val, variables);
        }
        return result;
    }
    return value;
}
export async function getMcpConfigForManifest(options) {
    const { manifest, extensionPath, systemDirs, userConfig, pathSeparator, logger, } = options;
    const baseConfig = manifest.server?.mcp_config;
    if (!baseConfig) {
        return undefined;
    }
    let result = {
        ...baseConfig,
    };
    if (baseConfig.platform_overrides) {
        if (process.platform in baseConfig.platform_overrides) {
            const platformConfig = baseConfig.platform_overrides[process.platform];
            result.command = platformConfig.command || result.command;
            result.args = platformConfig.args || result.args;
            result.env = platformConfig.env || result.env;
        }
    }
    // Check if required configuration is missing
    if (hasRequiredConfigMissing({ manifest, userConfig })) {
        logger?.warn(`Extension ${manifest.name} has missing required configuration, skipping MCP config`);
        return undefined;
    }
    const variables = {
        __dirname: extensionPath,
        pathSeparator,
        "/": pathSeparator,
        ...systemDirs,
    };
    // Build merged configuration from defaults and user settings
    const mergedConfig = {};
    // First, add defaults from manifest
    if (manifest.user_config) {
        for (const [key, configOption] of Object.entries(manifest.user_config)) {
            if (configOption.default !== undefined) {
                mergedConfig[key] = configOption.default;
            }
        }
    }
    // Then, override with user settings
    if (userConfig) {
        Object.assign(mergedConfig, userConfig);
    }
    // Add merged configuration variables for substitution
    for (const [key, value] of Object.entries(mergedConfig)) {
        // Convert user config to the format expected by variable substitution
        const userConfigKey = `user_config.${key}`;
        if (Array.isArray(value)) {
            // Keep arrays as arrays for proper expansion
            variables[userConfigKey] = value.map(String);
        }
        else if (typeof value === "boolean") {
            // Convert booleans to "true"/"false" strings as per spec
            variables[userConfigKey] = value ? "true" : "false";
        }
        else {
            // Convert other types to strings
            variables[userConfigKey] = String(value);
        }
    }
    // Replace all variables in the config
    result = replaceVariables(result, variables);
    return result;
}
function isInvalidSingleValue(value) {
    return value === undefined || value === null || value === "";
}
/**
 * Check if an extension has missing required configuration
 * @param manifest The extension manifest
 * @param userConfig The user configuration
 * @returns true if required configuration is missing
 */
export function hasRequiredConfigMissing({ manifest, userConfig, }) {
    if (!manifest.user_config) {
        return false;
    }
    const config = userConfig || {};
    for (const [key, configOption] of Object.entries(manifest.user_config)) {
        if (configOption.required) {
            const value = config[key];
            if (isInvalidSingleValue(value) ||
                (Array.isArray(value) &&
                    (value.length === 0 || value.some(isInvalidSingleValue)))) {
                return true;
            }
        }
    }
    return false;
}
