"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNodes = exports.makeCreateNodesForGradleConfigFile = exports.createNodesV2 = void 0;
exports.writeTargetsToCache = writeTargetsToCache;
const devkit_1 = require("@nx/devkit");
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const cache_directory_1 = require("nx/src/utils/cache-directory");
const devkit_internals_1 = require("nx/src/devkit-internals");
const get_gradle_report_1 = require("./utils/get-gradle-report");
const file_hasher_1 = require("nx/src/hasher/file-hasher");
const split_config_files_1 = require("../utils/split-config-files");
const exec_gradle_1 = require("../utils/exec-gradle");
const cacheableTaskType = new Set(['Build', 'Verification']);
const dependsOnMap = {
    build: ['^build', 'classes', 'test'],
    testClasses: ['classes'],
    test: ['testClasses'],
    classes: ['^classes'],
};
function normalizeOptions(options) {
    options ??= {};
    options.testTargetName ??= 'test';
    options.classesTargetName ??= 'classes';
    options.buildTargetName ??= 'build';
    return options;
}
function readTargetsCache(cachePath) {
    return (0, node_fs_1.existsSync)(cachePath) ? (0, devkit_1.readJsonFile)(cachePath) : {};
}
function writeTargetsToCache(cachePath, results) {
    (0, devkit_1.writeJsonFile)(cachePath, results);
}
exports.createNodesV2 = [
    split_config_files_1.gradleConfigAndTestGlob,
    async (files, options, context) => {
        const { buildFiles, projectRoots, gradlewFiles, testFiles } = (0, split_config_files_1.splitConfigFiles)(files);
        const optionsHash = (0, file_hasher_1.hashObject)(options);
        const cachePath = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, `gradle-${optionsHash}.hash`);
        const targetsCache = readTargetsCache(cachePath);
        await (0, get_gradle_report_1.populateGradleReport)(context.workspaceRoot, gradlewFiles.map((f) => (0, node_path_1.join)(context.workspaceRoot, f)));
        const gradleReport = (0, get_gradle_report_1.getCurrentGradleReport)();
        const gradleProjectRootToTestFilesMap = getGradleProjectRootToTestFilesMap(testFiles, projectRoots);
        try {
            return (0, devkit_1.createNodesFromFiles)((0, exports.makeCreateNodesForGradleConfigFile)(gradleReport, targetsCache, gradleProjectRootToTestFilesMap), buildFiles, options, context);
        }
        finally {
            writeTargetsToCache(cachePath, targetsCache);
        }
    },
];
const makeCreateNodesForGradleConfigFile = (gradleReport, targetsCache = {}, gradleProjectRootToTestFilesMap = {}) => async (gradleFilePath, options, context) => {
    const projectRoot = (0, node_path_1.dirname)(gradleFilePath);
    options = normalizeOptions(options);
    const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, options ?? {}, context);
    targetsCache[hash] ??= await createGradleProject(gradleReport, gradleFilePath, options, context, gradleProjectRootToTestFilesMap[projectRoot]);
    const project = targetsCache[hash];
    if (!project) {
        return {};
    }
    return {
        projects: {
            [projectRoot]: project,
        },
    };
};
exports.makeCreateNodesForGradleConfigFile = makeCreateNodesForGradleConfigFile;
/**
 @deprecated This is replaced with {@link createNodesV2}. Update your plugin to export its own `createNodesV2` function that wraps this one instead.
  This function will change to the v2 function in Nx 20.
 */
exports.createNodes = exports.createNodesV2;
async function createGradleProject(gradleReport, gradleFilePath, options, context, testFiles = []) {
    try {
        const { gradleProjectToTasksTypeMap, gradleProjectToTasksMap, gradleFileToOutputDirsMap, gradleFileToGradleProjectMap, gradleProjectToProjectName, } = gradleReport;
        const gradleProject = gradleFileToGradleProjectMap.get(gradleFilePath);
        const projectName = gradleProjectToProjectName.get(gradleProject);
        if (!projectName) {
            return;
        }
        const tasksTypeMap = gradleProjectToTasksTypeMap.get(gradleProject);
        const tasksSet = gradleProjectToTasksMap.get(gradleProject);
        let tasks = [];
        tasksSet.forEach((taskName) => {
            tasks.push({
                type: tasksTypeMap?.get(taskName),
                name: taskName,
            });
        });
        if (options.includeSubprojectsTasks) {
            tasksTypeMap.forEach((taskType, taskName) => {
                if (!tasksSet.has(taskName)) {
                    tasks.push({
                        type: taskType,
                        name: taskName,
                    });
                }
            });
        }
        const outputDirs = gradleFileToOutputDirsMap.get(gradleFilePath);
        const { targets, targetGroups } = await createGradleTargets(tasks, options, context, outputDirs, gradleProject, gradleFilePath, testFiles);
        const project = {
            name: projectName,
            projectType: 'application',
            targets,
            metadata: {
                targetGroups,
                technologies: ['gradle'],
            },
        };
        return project;
    }
    catch (e) {
        console.error(e);
        return undefined;
    }
}
async function createGradleTargets(tasks, options, context, outputDirs, gradleProject, gradleBuildFilePath, testFiles = []) {
    const inputsMap = createInputsMap(context);
    const gradlewFileDirectory = (0, node_path_1.dirname)((0, exec_gradle_1.findGradlewFile)(gradleBuildFilePath, context.workspaceRoot));
    const targets = {};
    const targetGroups = {};
    for (const task of tasks) {
        const targetName = options?.[`${task.name}TargetName`] ?? task.name;
        let outputs = [outputDirs.get(task.name)].filter(Boolean);
        if (task.name === 'test') {
            outputs = [
                outputDirs.get('testReport'),
                outputDirs.get('testResults'),
            ].filter(Boolean);
            getTestCiTargets(testFiles, gradleProject, targetName, options.ciTargetName, inputsMap['test'], outputs, task.type, targets, targetGroups, gradlewFileDirectory);
        }
        const taskCommandToRun = `${gradleProject ? gradleProject + ':' : ''}${task.name}`;
        targets[targetName] = {
            command: `${(0, exec_gradle_1.getGradleExecFile)()} ${taskCommandToRun}`,
            options: {
                cwd: gradlewFileDirectory,
            },
            cache: cacheableTaskType.has(task.type),
            inputs: inputsMap[task.name],
            dependsOn: dependsOnMap[task.name],
            metadata: {
                technologies: ['gradle'],
                help: {
                    command: `${(0, exec_gradle_1.getGradleExecFile)()} help --task ${taskCommandToRun}`,
                    example: {
                        options: {
                            args: ['--rerun'],
                        },
                    },
                },
            },
            ...(outputs && outputs.length ? { outputs } : {}),
        };
        if (task.type) {
            if (!targetGroups[task.type]) {
                targetGroups[task.type] = [];
            }
            targetGroups[task.type].push(targetName);
        }
    }
    return { targetGroups, targets };
}
function createInputsMap(context) {
    const namedInputs = context.nxJsonConfiguration.namedInputs;
    return {
        build: namedInputs?.production
            ? ['production', '^production']
            : ['default', '^default'],
        test: ['default', namedInputs?.production ? '^production' : '^default'],
        classes: namedInputs?.production
            ? ['production', '^production']
            : ['default', '^default'],
    };
}
function getTestCiTargets(testFiles, gradleProject, testTargetName, ciTargetName, inputs, outputs, targetGroupName, targets, targetGroups, gradlewFileDirectory) {
    if (!testFiles || testFiles.length === 0 || !ciTargetName) {
        return;
    }
    const taskCommandToRun = `${gradleProject ? gradleProject + ':' : ''}test`;
    if (!targetGroups[targetGroupName]) {
        targetGroups[targetGroupName] = [];
    }
    const dependsOn = [];
    testFiles.forEach((testFile) => {
        const testName = (0, node_path_1.basename)(testFile).split('.')[0];
        const targetName = ciTargetName + '--' + testName;
        targets[targetName] = {
            command: `${(0, exec_gradle_1.getGradleExecFile)()} ${taskCommandToRun} --tests ${testName}`,
            options: {
                cwd: gradlewFileDirectory,
            },
            cache: true,
            inputs,
            dependsOn: dependsOnMap['test'],
            metadata: {
                technologies: ['gradle'],
                description: `Runs Gradle test ${testFile} in CI`,
                help: {
                    command: `${(0, exec_gradle_1.getGradleExecFile)()} help --task ${taskCommandToRun}`,
                    example: {
                        options: {
                            args: ['--rerun'],
                        },
                    },
                },
            },
            ...(outputs && outputs.length > 0 ? { outputs } : {}),
        };
        targetGroups[targetGroupName].push(targetName);
        dependsOn.push({
            target: targetName,
            projects: 'self',
            params: 'forward',
        });
    });
    targets[ciTargetName] = {
        executor: 'nx:noop',
        cache: true,
        inputs,
        dependsOn: dependsOn,
        ...(outputs && outputs.length > 0 ? { outputs } : {}),
        metadata: {
            technologies: ['gradle'],
            description: 'Runs Gradle Tests in CI',
            nonAtomizedTarget: testTargetName,
            help: {
                command: `${(0, exec_gradle_1.getGradleExecFile)()} help --task ${taskCommandToRun}`,
                example: {
                    options: {
                        args: ['--rerun'],
                    },
                },
            },
        },
    };
    targetGroups[targetGroupName].push(ciTargetName);
}
function getGradleProjectRootToTestFilesMap(testFiles, projectRoots) {
    if (testFiles.length === 0 || projectRoots.length === 0) {
        return;
    }
    const roots = new Map(projectRoots.map((root) => [root, root]));
    const testFilesToGradleProjectMap = {};
    testFiles.forEach((testFile) => {
        const projectRoot = (0, devkit_internals_1.findProjectForPath)(testFile, roots);
        if (projectRoot) {
            if (!testFilesToGradleProjectMap[projectRoot]) {
                testFilesToGradleProjectMap[projectRoot] = [];
            }
            testFilesToGradleProjectMap[projectRoot].push(testFile);
        }
    });
    return testFilesToGradleProjectMap;
}
