"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findVersionCatalogFiles = findVersionCatalogFiles;
exports.updatePluginVersionInCatalogAst = updatePluginVersionInCatalogAst;
exports.getPluginAliasFromCatalogAst = getPluginAliasFromCatalogAst;
exports.extractPluginVersionFromCatalogAst = extractPluginVersionFromCatalogAst;
exports.updateNxPluginVersionInCatalogsAst = updateNxPluginVersionInCatalogsAst;
const devkit_1 = require("@nx/devkit");
const toml_eslint_parser_1 = require("toml-eslint-parser");
const versions_1 = require("./versions");
async function findVersionCatalogFiles(tree) {
    const versionCatalogPaths = [];
    const globFiles = await (0, devkit_1.globAsync)(tree, ['**/gradle/*.versions.toml']);
    for (const filePath of globFiles) {
        if (!versionCatalogPaths.includes(filePath)) {
            versionCatalogPaths.push(filePath);
        }
    }
    return versionCatalogPaths;
}
/**
 * Reconstructs TOML content from source text with selective replacements
 * based on AST node ranges. This preserves all original formatting.
 */
function reconstructTomlWithUpdates(sourceText, updates) {
    // Sort updates by start position in reverse order to apply from end to beginning
    const sortedUpdates = updates.sort((a, b) => b.start - a.start);
    let result = sourceText;
    for (const update of sortedUpdates) {
        result =
            result.slice(0, update.start) +
                update.replacement +
                result.slice(update.end);
    }
    return result;
}
/**
 * Finds a key-value pair in a TOML table by key name
 */
function findKeyValueInTable(table, keyName) {
    if (!table.body)
        return null;
    for (const item of table.body) {
        if (item.type === 'TOMLKeyValue') {
            if (getKeyName(item.key) === keyName) {
                return item;
            }
        }
    }
    return null;
}
/**
 * Gets the string representation of a TOML key
 */
function getKeyName(key) {
    if (key.type === 'TOMLKey' && key.keys && key.keys.length > 0) {
        // Handle TOMLKey type which contains an array of keys (for dotted keys like version.ref)
        return key.keys
            .map((k) => {
            if (k.type === 'TOMLBare') {
                return k.name;
            }
            else if (k.type === 'TOMLQuoted') {
                return k.value;
            }
            return '';
        })
            .join('.');
    }
    else if (key.type === 'TOMLBare') {
        return key.name;
    }
    else if (key.type === 'TOMLQuoted') {
        return key.value;
    }
    return '';
}
/**
 * Gets the string value from a TOML value node
 */
function getStringValue(value) {
    if (value.type === 'TOMLValue' && value.kind === 'string') {
        return value.value;
    }
    return null;
}
/**
 * Finds plugin configuration in the plugins table
 */
function findPluginConfig(pluginsTable, pluginName) {
    if (!pluginsTable.body)
        return null;
    for (const item of pluginsTable.body) {
        if (item.type === 'TOMLKeyValue') {
            const value = item.value;
            const alias = getKeyName(item.key);
            if (value.type === 'TOMLValue' && value.kind === 'string') {
                // Simple format: plugin = "id:version"
                const stringValue = value.value;
                if (stringValue.startsWith(`${pluginName}:`)) {
                    return { keyValue: item, format: 'simple', alias };
                }
            }
            else if (value.type === 'TOMLInlineTable') {
                // Object format: { id = "plugin.name", version = "1.0.0" }
                const idKeyValue = findKeyValueInTable(value, 'id');
                if (idKeyValue) {
                    const idValue = getStringValue(idKeyValue.value);
                    if (idValue === pluginName) {
                        return { keyValue: item, format: 'object', alias };
                    }
                }
            }
        }
    }
    return null;
}
/**
 * Finds the plugins table in the AST
 */
function findPluginsTable(ast) {
    // Look through all top-level elements
    for (const topLevel of ast.body) {
        if (topLevel.type === 'TOMLTopLevelTable') {
            // Look through the body of the top-level table for [plugins] table
            for (const item of topLevel.body) {
                if (item.type === 'TOMLTable' && item.key && item.key.keys) {
                    const tableName = item.key.keys
                        .map((key) => getKeyName(key))
                        .join('.');
                    if (tableName === 'plugins') {
                        return item;
                    }
                }
            }
            // If no [plugins] table found, check if there's a plugins key in the root table
            const pluginsKeyValue = findKeyValueInTable(topLevel, 'plugins');
            if (pluginsKeyValue?.value.type === 'TOMLInlineTable') {
                return pluginsKeyValue.value;
            }
        }
    }
    return null;
}
/**
 * Finds the versions table in the AST
 */
function findVersionsTable(ast) {
    // Look through all top-level elements
    for (const topLevel of ast.body) {
        if (topLevel.type === 'TOMLTopLevelTable') {
            // Look through the body of the top-level table for [versions] table
            for (const item of topLevel.body) {
                if (item.type === 'TOMLTable' && item.key && item.key.keys) {
                    const tableName = item.key.keys
                        .map((key) => getKeyName(key))
                        .join('.');
                    if (tableName === 'versions') {
                        return item;
                    }
                }
            }
        }
    }
    return null;
}
/**
 * Updates a plugin version in a TOML catalog while preserving formatting
 */
function updatePluginVersionInCatalogAst(sourceText, pluginName, newVersion) {
    try {
        const ast = (0, toml_eslint_parser_1.parseTOML)(sourceText);
        const updates = [];
        const pluginsTable = findPluginsTable(ast);
        if (!pluginsTable) {
            return null; // No plugins table found
        }
        const pluginConfig = findPluginConfig(pluginsTable, pluginName);
        if (!pluginConfig) {
            return null; // Plugin not found
        }
        const { keyValue, format } = pluginConfig;
        if (format === 'simple') {
            // Update simple format: "plugin:version" -> "plugin:newVersion"
            const value = keyValue.value;
            const newValue = `${pluginName}:${newVersion}`;
            // Preserve the quote style
            const quote = value.style === 'basic' ? '"' : "'";
            const replacement = `${quote}${newValue}${quote}`;
            updates.push({
                start: value.range[0],
                end: value.range[1],
                replacement,
            });
        }
        else if (format === 'object') {
            // Handle object format
            const inlineTable = keyValue.value;
            // First, try to find direct version
            const versionKeyValue = findKeyValueInTable(inlineTable, 'version');
            if (versionKeyValue) {
                const versionValue = versionKeyValue.value;
                if (versionValue.type === 'TOMLValue' &&
                    versionValue.kind === 'string') {
                    // Direct version update
                    const quote = versionValue.style === 'basic' ? '"' : "'";
                    const replacement = `${quote}${newVersion}${quote}`;
                    updates.push({
                        start: versionValue.range[0],
                        end: versionValue.range[1],
                        replacement,
                    });
                }
            }
            else {
                // Check for version.ref pattern - look for either quoted or unquoted key
                let versionRefKeyValue = findKeyValueInTable(inlineTable, 'version.ref');
                if (!versionRefKeyValue) {
                    // Try with quotes since TOML inline tables may require quoted keys for dotted keys
                    versionRefKeyValue = findKeyValueInTable(inlineTable, '"version.ref"');
                }
                if (versionRefKeyValue) {
                    // This means we need to update the referenced version in the [versions] table
                    const refValue = getStringValue(versionRefKeyValue.value);
                    if (refValue) {
                        const versionsTable = findVersionsTable(ast);
                        if (versionsTable) {
                            const referencedVersion = findKeyValueInTable(versionsTable, refValue);
                            if (referencedVersion) {
                                const refVersionValue = referencedVersion.value;
                                if (refVersionValue.type === 'TOMLValue' &&
                                    refVersionValue.kind === 'string') {
                                    const quote = refVersionValue.style === 'basic' ? '"' : "'";
                                    const replacement = `${quote}${newVersion}${quote}`;
                                    updates.push({
                                        start: refVersionValue.range[0],
                                        end: refVersionValue.range[1],
                                        replacement,
                                    });
                                }
                            }
                        }
                    }
                }
            }
        }
        if (updates.length === 0) {
            return null;
        }
        return reconstructTomlWithUpdates(sourceText, updates);
    }
    catch (error) {
        console.error('Error parsing TOML with AST:', error);
        return null;
    }
}
/**
 * Gets the plugin alias from a version catalog for a given plugin ID
 * Returns the alias name (e.g., "nxProjectGraph") if found, null otherwise
 */
function getPluginAliasFromCatalogAst(sourceText, pluginName) {
    try {
        const ast = (0, toml_eslint_parser_1.parseTOML)(sourceText);
        const pluginsTable = findPluginsTable(ast);
        if (!pluginsTable) {
            return null;
        }
        const pluginConfig = findPluginConfig(pluginsTable, pluginName);
        if (!pluginConfig) {
            return null;
        }
        return pluginConfig.alias;
    }
    catch (error) {
        console.error('Error parsing TOML with AST:', error);
        return null;
    }
}
/**
 * Extracts plugin version from catalog using AST parsing
 */
function extractPluginVersionFromCatalogAst(sourceText, pluginName) {
    try {
        const ast = (0, toml_eslint_parser_1.parseTOML)(sourceText);
        const pluginsTable = findPluginsTable(ast);
        if (!pluginsTable) {
            return null;
        }
        const pluginConfig = findPluginConfig(pluginsTable, pluginName);
        if (!pluginConfig) {
            return null;
        }
        const { keyValue, format } = pluginConfig;
        if (format === 'simple') {
            // Extract from simple format: "plugin:version"
            const value = keyValue.value;
            const stringValue = value.value;
            return stringValue.split(':')[1] || null;
        }
        else if (format === 'object') {
            // Extract from object format
            const inlineTable = keyValue.value;
            // First, try to find direct version
            const versionKeyValue = findKeyValueInTable(inlineTable, 'version');
            if (versionKeyValue) {
                return getStringValue(versionKeyValue.value);
            }
            // Check for version.ref pattern - look for either quoted or unquoted key
            let versionRefKeyValue = findKeyValueInTable(inlineTable, 'version.ref');
            if (!versionRefKeyValue) {
                // Try with quotes since TOML inline tables may require quoted keys for dotted keys
                versionRefKeyValue = findKeyValueInTable(inlineTable, '"version.ref"');
            }
            if (versionRefKeyValue) {
                const refValue = getStringValue(versionRefKeyValue.value);
                if (refValue) {
                    const versionsTable = findVersionsTable(ast);
                    if (versionsTable) {
                        const referencedVersion = findKeyValueInTable(versionsTable, refValue);
                        if (referencedVersion) {
                            return getStringValue(referencedVersion.value);
                        }
                    }
                }
            }
        }
        return null;
    }
    catch (error) {
        console.error('Error parsing TOML with AST:', error);
        return null;
    }
}
/**
 * Updates Nx plugin version in all catalog files using AST-based approach
 */
async function updateNxPluginVersionInCatalogsAst(tree, expectedVersion) {
    const catalogFiles = await findVersionCatalogFiles(tree);
    let updated = false;
    for (const catalogPath of catalogFiles) {
        if (!tree.exists(catalogPath)) {
            continue;
        }
        const content = tree.read(catalogPath, 'utf-8');
        if (!content) {
            continue;
        }
        const currentVersion = extractPluginVersionFromCatalogAst(content, versions_1.gradleProjectGraphPluginName);
        if (currentVersion && currentVersion !== expectedVersion) {
            const updatedContent = updatePluginVersionInCatalogAst(content, versions_1.gradleProjectGraphPluginName, expectedVersion);
            if (updatedContent) {
                tree.write(catalogPath, updatedContent);
                updated = true;
            }
        }
    }
    return updated;
}
