"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toPackageDependencies = exports.processModulePkgDeps = exports.RuleToBazelModulePackageDep = exports.bazelModulePackageDepToPackageDependency = void 0;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const github_url_from_git_1 = tslib_1.__importDefault(require("github-url-from-git"));
const zod_1 = require("zod");
const logger_1 = require("../../../logger");
const regex_1 = require("../../../util/regex");
const bazel_1 = require("../../datasource/bazel");
const github_tags_1 = require("../../datasource/github-tags");
const fragments_1 = require("./fragments");
function isOverride(value) {
    return 'bazelDepSkipReason' in value;
}
function isMerge(value) {
    return 'bazelDepMergeFields' in value;
}
// This function exists to remove properties that are specific to
// BazelModulePackageDep. In theory, there is no harm in leaving the properties
// as it does not invalidate the PackageDependency interface. However, it might
// be surprising to someone outside the bazel-module code to see the extra
// properties.
function bazelModulePackageDepToPackageDependency(bmpd) {
    const copy = structuredClone(bmpd);
    if (isOverride(copy)) {
        const partial = copy;
        delete partial.bazelDepSkipReason;
    }
    if (isMerge(copy)) {
        const partial = copy;
        delete partial.bazelDepMergeFields;
    }
    return copy;
}
exports.bazelModulePackageDepToPackageDependency = bazelModulePackageDepToPackageDependency;
const BazelDepToPackageDep = fragments_1.RecordFragmentSchema.extend({
    children: zod_1.z.object({
        rule: fragments_1.StringFragmentSchema.extend({
            value: zod_1.z.literal('bazel_dep'),
        }),
        name: fragments_1.StringFragmentSchema,
        version: fragments_1.StringFragmentSchema,
    }),
}).transform(({ children: { rule, name, version } }) => ({
    datasource: bazel_1.BazelDatasource.id,
    depType: rule.value,
    depName: name.value,
    currentValue: version.value,
}));
const GitOverrideToPackageDep = fragments_1.RecordFragmentSchema.extend({
    children: zod_1.z.object({
        rule: fragments_1.StringFragmentSchema.extend({
            value: zod_1.z.literal('git_override'),
        }),
        module_name: fragments_1.StringFragmentSchema,
        remote: fragments_1.StringFragmentSchema,
        commit: fragments_1.StringFragmentSchema,
    }),
}).transform(({ children: { rule, module_name: moduleName, remote, commit }, }) => {
    const override = {
        depType: rule.value,
        depName: moduleName.value,
        bazelDepSkipReason: 'git-dependency',
        currentDigest: commit.value,
    };
    const ghPackageName = githubPackageName(remote.value);
    if (is_1.default.nonEmptyString(ghPackageName)) {
        override.datasource = github_tags_1.GithubTagsDatasource.id;
        override.packageName = ghPackageName;
    }
    else {
        override.skipReason = 'unsupported-datasource';
    }
    return override;
});
const SingleVersionOverrideToPackageDep = fragments_1.RecordFragmentSchema.extend({
    children: zod_1.z.object({
        rule: fragments_1.StringFragmentSchema.extend({
            value: zod_1.z.literal('single_version_override'),
        }),
        module_name: fragments_1.StringFragmentSchema,
        version: fragments_1.StringFragmentSchema.optional(),
        registry: fragments_1.StringFragmentSchema.optional(),
    }),
}).transform(({ children: { rule, module_name: moduleName, version, registry }, }) => {
    const base = {
        depType: rule.value,
        depName: moduleName.value,
        skipReason: 'ignored',
    };
    // If a version is specified, then add a skipReason to bazel_dep
    if (version) {
        const override = base;
        override.bazelDepSkipReason = 'is-pinned';
        override.currentValue = version.value;
    }
    // If a registry is specified, then merge it into the bazel_dep
    if (registry) {
        const merge = base;
        merge.bazelDepMergeFields = ['registryUrls'];
        merge.registryUrls = [registry.value];
    }
    return base;
});
const UnsupportedOverrideToPackageDep = fragments_1.RecordFragmentSchema.extend({
    children: zod_1.z.object({
        rule: fragments_1.StringFragmentSchema.extend({
            value: zod_1.z.enum(['archive_override', 'local_path_override']),
        }),
        module_name: fragments_1.StringFragmentSchema,
    }),
}).transform(({ children: { rule, module_name: moduleName } }) => {
    let bazelDepSkipReason = 'unsupported';
    switch (rule.value) {
        case 'archive_override':
            bazelDepSkipReason = 'file-dependency';
            break;
        case 'local_path_override':
            bazelDepSkipReason = 'local-dependency';
            break;
    }
    return {
        depType: rule.value,
        depName: moduleName.value,
        skipReason: 'unsupported-datasource',
        bazelDepSkipReason,
    };
});
exports.RuleToBazelModulePackageDep = zod_1.z.union([
    BazelDepToPackageDep,
    GitOverrideToPackageDep,
    SingleVersionOverrideToPackageDep,
    UnsupportedOverrideToPackageDep,
]);
const githubRemoteRegex = (0, regex_1.regEx)(/^https:\/\/github\.com\/(?<packageName>[^/]+\/.+)$/);
function githubPackageName(remote) {
    return (0, github_url_from_git_1.default)(remote)?.match(githubRemoteRegex)?.groups?.packageName;
}
function collectByModule(packageDeps) {
    const rulesByModule = new Map();
    for (const pkgDep of packageDeps) {
        const bmi = rulesByModule.get(pkgDep.depName) ?? [];
        bmi.push(pkgDep);
        rulesByModule.set(pkgDep.depName, bmi);
    }
    return Array.from(rulesByModule.values());
}
function processModulePkgDeps(packageDeps) {
    if (!packageDeps.length) {
        return [];
    }
    const moduleName = packageDeps[0].depName;
    const bazelDep = packageDeps.find((pd) => pd.depType === 'bazel_dep');
    if (!bazelDep) {
        logger_1.logger.debug(`A 'bazel_dep' was not found for '${moduleName}'.`);
        return [];
    }
    // Create a new bazelDep that will be modified. We do not want to change the
    // input.
    const bazelDepOut = { ...bazelDep };
    const deps = [bazelDepOut];
    const merges = packageDeps.filter(isMerge);
    for (const merge of merges) {
        merge.bazelDepMergeFields.forEach((k) => (bazelDepOut[k] = merge[k]));
    }
    const overrides = packageDeps.filter(isOverride);
    if (overrides.length === 0) {
        return deps;
    }
    // It is an error for more than one override to exist for a module. We will
    // ignore the overrides if there is more than one.
    if (overrides.length > 1) {
        const depTypes = overrides.map((o) => o.depType);
        logger_1.logger.debug({ depName: moduleName, depTypes }, 'More than one override for a module was found');
        return deps;
    }
    const override = overrides[0];
    deps.push(bazelModulePackageDepToPackageDependency(override));
    bazelDepOut.skipReason = override.bazelDepSkipReason;
    return deps;
}
exports.processModulePkgDeps = processModulePkgDeps;
function toPackageDependencies(packageDeps) {
    return collectByModule(packageDeps).map(processModulePkgDeps).flat();
}
exports.toPackageDependencies = toPackageDependencies;
//# sourceMappingURL=rules.js.map