"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addBuildGradleFileNextToSettingsGradle = addBuildGradleFileNextToSettingsGradle;
exports.extractNxPluginVersion = extractNxPluginVersion;
exports.updateNxPluginVersion = updateNxPluginVersion;
exports.addNxProjectGraphPlugin = addNxProjectGraphPlugin;
const devkit_1 = require("@nx/devkit");
const versions_1 = require("../../utils/versions");
const path_1 = require("path");
const exec_gradle_1 = require("../../utils/exec-gradle");
const version_catalog_ast_utils_1 = require("../../utils/version-catalog-ast-utils");
/**
 * Adds a `build.gradle(.kts)` file next to each `settings.gradle(.kts)` file found in the workspace.
 * If the build.gradle file already exists, it reads its contents.
 */
async function addBuildGradleFileNextToSettingsGradle(tree) {
    const settingsGradleFiles = await (0, devkit_1.globAsync)(tree, [
        '**/settings.gradle',
        '**/settings.gradle.kts',
    ]);
    return settingsGradleFiles.map((settingsGradlePath) => {
        return ensureBuildGradleFile(tree, settingsGradlePath);
    });
}
/**
 * Determines the appropriate build.gradle file path based on the settings file
 * and ensures it exists in the tree. Returns the path and contents.
 */
function ensureBuildGradleFile(tree, settingsGradlePath) {
    const isKotlinDsl = settingsGradlePath.endsWith('.kts');
    const buildGradleFile = (0, path_1.join)((0, path_1.dirname)(settingsGradlePath), isKotlinDsl ? 'build.gradle.kts' : 'build.gradle');
    let content = '';
    if (tree.exists(buildGradleFile)) {
        content = tree.read(buildGradleFile, 'utf-8');
    }
    else {
        tree.write(buildGradleFile, content);
    }
    return { filePath: buildGradleFile, content };
}
// a regex to get the version in build file in format `id "dev.nx.gradle.project-graph" version "x"`
const regex = /(id\s*\(?["']dev\.nx\.gradle\.project-graph["']\)?\s*version\s*\(?["'])([^"']+)(["']\)?)/;
/**
 * Extract gradle plugin version from build.gradle file
 */
async function extractNxPluginVersion(gradleFilePath, gradleContent) {
    const match = gradleContent.match(regex);
    let version = match ? match[2] : null;
    if (!version) {
        try {
            const gradlewFile = (0, exec_gradle_1.findGradlewFile)(gradleFilePath, devkit_1.workspaceRoot, undefined);
            const buildEnvironment = (await (0, exec_gradle_1.execGradleAsync)((0, path_1.join)(devkit_1.workspaceRoot, gradlewFile), [
                'buildEnvironment',
                '--quiet',
            ])).toString();
            version = getPluginVersion(buildEnvironment);
        }
        catch (e) { } // Silently ignore error, fallback remains null
    }
    return version;
}
function getPluginVersion(dependencyTree) {
    const lines = dependencyTree.split('\n');
    for (const line of lines) {
        // line is dev.nx.gradle.project-graph:dev.nx.gradle.project-graph.gradle.plugin:version
        const match = line.match(/dev\.nx\.gradle\.project-graph:dev\.nx\.gradle\.project-graph\.gradle\.plugin:([^\s\\]+)/);
        if (match) {
            return match[1]; // returns the version part
        }
    }
    return null; // not found
}
/**
 * Updates the plugin version in the given Gradle file content.
 */
function updateNxPluginVersion(content, newVersion) {
    if (regex.test(content)) {
        return content.replace(regex, `$1${newVersion}$3`);
    }
    else {
        devkit_1.logger.warn(`Please update plugin dev.nx.gradle.project-graph to ${newVersion}`);
    }
    return content;
}
/**
 * Ensures all build.gradle(.kts) files use the expected version of dev.nx.gradle.project-graph.
 */
async function addNxProjectGraphPlugin(tree, expectedVersion = versions_1.gradleProjectGraphVersion) {
    const files = await addBuildGradleFileNextToSettingsGradle(tree);
    for (const { filePath, content } of files) {
        await addNxProjectGraphPluginToBuildGradle(filePath, content, expectedVersion, tree);
    }
}
/**
 * Converts a version catalog alias to a Gradle accessor path.
 * In Gradle, dashes in aliases become dots in the accessor.
 * e.g., "nx-project-graph" -> "nx.project.graph"
 */
function aliasToAccessorPath(alias) {
    return alias.replace(/-/g, '.');
}
/**
 * Finds a version catalog file near the given build.gradle file and returns
 * the plugin alias if the Nx project graph plugin is defined in it.
 * Searches in the following order:
 * 1. Standard Gradle location: gradle/libs.versions.toml relative to the build.gradle file
 * 2. Workspace root: gradle/libs.versions.toml at the workspace root
 * 3. Any libs.versions.toml in subdirectories of the build.gradle file
 */
async function findVersionCatalogPluginAlias(gradleFilePath, tree) {
    const gradleDir = (0, path_1.dirname)(gradleFilePath);
    // Check standard Gradle version catalog locations
    const possibleCatalogPaths = [
        // Standard location relative to the build.gradle file
        (0, path_1.join)(gradleDir, 'gradle', 'libs.versions.toml'),
        // Workspace root location (common in monorepos)
        'gradle/libs.versions.toml',
    ];
    for (const catalogPath of possibleCatalogPaths) {
        if (tree.exists(catalogPath)) {
            const catalogContent = tree.read(catalogPath, 'utf-8');
            if (catalogContent) {
                const alias = (0, version_catalog_ast_utils_1.getPluginAliasFromCatalogAst)(catalogContent, versions_1.gradleProjectGraphPluginName);
                if (alias) {
                    return alias;
                }
            }
        }
    }
    // Fallback: search for any version catalog in subdirectories
    const versionCatalogFiles = await (0, devkit_1.globAsync)(tree, [
        (0, path_1.join)(gradleDir, '**/libs.versions.toml'),
    ]);
    for (const versionCatalogPath of versionCatalogFiles) {
        const catalogContent = tree.read(versionCatalogPath, 'utf-8');
        if (catalogContent) {
            const alias = (0, version_catalog_ast_utils_1.getPluginAliasFromCatalogAst)(catalogContent, versions_1.gradleProjectGraphPluginName);
            if (alias) {
                return alias;
            }
        }
    }
    return null;
}
/**
 * Adds or updates the Nx Project Graph plugin in the build.gradle(.kts) file.
 * Ensures the correct version and applies the plugin to all projects.
 * Returns the updated build.gradle content.
 */
async function addNxProjectGraphPluginToBuildGradle(gradleFilePath, buildGradleContent, expectedVersion = versions_1.gradleProjectGraphVersion, tree) {
    const isKotlinDsl = gradleFilePath.endsWith('.kts');
    // Check if a version catalog exists with the plugin alias
    const pluginAlias = await findVersionCatalogPluginAlias(gradleFilePath, tree);
    const versionCatalogPluginAccessor = pluginAlias
        ? `libs.plugins.${aliasToAccessorPath(pluginAlias)}`
        : null;
    // Determine the plugin declaration syntax based on whether we have a version catalog
    const nxProjectGraphReportPlugin = versionCatalogPluginAccessor
        ? `alias(${versionCatalogPluginAccessor})`
        : isKotlinDsl
            ? `id("${versions_1.gradleProjectGraphPluginName}") version("${expectedVersion}")`
            : `id "${versions_1.gradleProjectGraphPluginName}" version "${expectedVersion}"`;
    // Check if the plugin is already included (directly or via version catalog alias)
    const hasPluginDirectly = buildGradleContent.includes(versions_1.gradleProjectGraphPluginName);
    const hasPluginViaAlias = versionCatalogPluginAccessor &&
        buildGradleContent.includes(versionCatalogPluginAccessor);
    const hasPlugin = hasPluginDirectly || hasPluginViaAlias;
    // Helper to add plugin to plugins block
    function addPluginToPluginsBlock(content) {
        return content.replace(/plugins\s*\{/, `plugins {\n    ${nxProjectGraphReportPlugin}`);
    }
    // Helper to add plugins block if missing
    function addPluginsBlock(content) {
        return `plugins {\n    ${nxProjectGraphReportPlugin}\n}\n${content}`;
    }
    // Helper to add plugin application to allprojects
    function addPluginToAllProjects(content) {
        const applyPlugin = versionCatalogPluginAccessor
            ? `plugin(${versionCatalogPluginAccessor})`
            : isKotlinDsl
                ? `plugin("${versions_1.gradleProjectGraphPluginName}")`
                : `plugin("${versions_1.gradleProjectGraphPluginName}")`;
        return `${content}\nallprojects {\n    apply {\n        ${applyPlugin}\n    }\n}`;
    }
    // Add to plugins block if plugin not already present
    if (buildGradleContent.includes('plugins {')) {
        if (hasPlugin) {
            // Plugin already exists - update version if needed (only for direct declarations)
            if (hasPluginDirectly) {
                const currentVersion = await extractNxPluginVersion(gradleFilePath, buildGradleContent);
                if (currentVersion && currentVersion !== expectedVersion) {
                    buildGradleContent = updateNxPluginVersion(buildGradleContent, expectedVersion);
                }
            }
        }
        else {
            buildGradleContent = addPluginToPluginsBlock(buildGradleContent);
        }
    }
    else {
        buildGradleContent = addPluginsBlock(buildGradleContent);
    }
    // Skip allprojects handling if plugin was already correctly declared via version catalog alias
    // In that case, the plugin is already applied and doesn't need to be propagated via allprojects
    if (!hasPluginViaAlias) {
        const applyPluginPattern = versionCatalogPluginAccessor
            ? new RegExp(`\\s*plugin\\(${versionCatalogPluginAccessor.replace(/\./g, '\\.')}\\)`)
            : new RegExp(`\\s*plugin\\(["']${versions_1.gradleProjectGraphPluginName}["']\\)`);
        if (buildGradleContent.includes('allprojects {')) {
            if (!applyPluginPattern.test(buildGradleContent)) {
                const applyPlugin = versionCatalogPluginAccessor
                    ? `plugin(${versionCatalogPluginAccessor})`
                    : isKotlinDsl
                        ? `plugin("${versions_1.gradleProjectGraphPluginName}")`
                        : `plugin "${versions_1.gradleProjectGraphPluginName}"`;
                buildGradleContent = buildGradleContent.replace(/allprojects\s*\{/, `allprojects {\n    apply ${applyPlugin}`);
            }
        }
        else {
            buildGradleContent = addPluginToAllProjects(buildGradleContent);
        }
    }
    tree.write(gradleFilePath, buildGradleContent);
    return buildGradleContent;
}
