// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import crypto from 'node:crypto';
import * as path from 'node:path';
import { JsonFile, JsonSchema, MapExtensions, ProtectableMap, FileSystem, Sort } from '@rushstack/node-core-library';
import { PackageNameParsers } from './PackageNameParsers';
import { JsonSchemaUrls } from '../logic/JsonSchemaUrls';
import { RushConstants } from '../logic/RushConstants';
import schemaJson from '../schemas/common-versions.schema.json';
/**
 * Use this class to load and save the "common/config/rush/common-versions.json" config file.
 * This config file stores dependency version information that affects all projects in the repo.
 * @public
 */
export class CommonVersionsConfiguration {
    constructor(commonVersionsJson, filePath, rushConfiguration) {
        var _a;
        this._modified = false;
        this._preferredVersions = new ProtectableMap({
            onSet: this._onSetPreferredVersions.bind(this)
        });
        this.preferredVersions = this._preferredVersions.protectedView;
        if (commonVersionsJson && commonVersionsJson.implicitlyPreferredVersions !== undefined) {
            this.implicitlyPreferredVersions = commonVersionsJson.implicitlyPreferredVersions;
        }
        else {
            this.implicitlyPreferredVersions = undefined;
        }
        this._allowedAlternativeVersions = new ProtectableMap({
            onSet: this._onSetAllowedAlternativeVersions.bind(this)
        });
        this.allowedAlternativeVersions = this._allowedAlternativeVersions.protectedView;
        const subspacesFeatureEnabled = rushConfiguration === null || rushConfiguration === void 0 ? void 0 : rushConfiguration.subspacesFeatureEnabled;
        const rushJsonEnsureConsistentVersions = rushConfiguration === null || rushConfiguration === void 0 ? void 0 : rushConfiguration._ensureConsistentVersionsJsonValue;
        const commonVersionsEnsureConsistentVersions = commonVersionsJson === null || commonVersionsJson === void 0 ? void 0 : commonVersionsJson.ensureConsistentVersions;
        if (subspacesFeatureEnabled && rushJsonEnsureConsistentVersions !== undefined) {
            throw new Error(`When using subspaces, the ensureConsistentVersions config is now defined in the ${RushConstants.commonVersionsFilename} file, ` +
                `you must remove the old setting "ensureConsistentVersions" from ${RushConstants.rushJsonFilename}`);
        }
        else if (!subspacesFeatureEnabled &&
            rushJsonEnsureConsistentVersions !== undefined &&
            commonVersionsEnsureConsistentVersions !== undefined) {
            throw new Error(`When the ensureConsistentVersions config is defined in the ${RushConstants.rushJsonFilename} file, ` +
                `it cannot also be defined in the ${RushConstants.commonVersionsFilename} file`);
        }
        this.ensureConsistentVersions =
            (_a = commonVersionsEnsureConsistentVersions !== null && commonVersionsEnsureConsistentVersions !== void 0 ? commonVersionsEnsureConsistentVersions : rushJsonEnsureConsistentVersions) !== null && _a !== void 0 ? _a : false;
        this._commonVersionsJsonHasEnsureConsistentVersionsProperty =
            commonVersionsEnsureConsistentVersions !== undefined;
        if (commonVersionsJson) {
            try {
                CommonVersionsConfiguration._deserializeTable(this.preferredVersions, commonVersionsJson.preferredVersions);
                CommonVersionsConfiguration._deserializeTable(this.allowedAlternativeVersions, commonVersionsJson.allowedAlternativeVersions);
            }
            catch (e) {
                throw new Error(`Error loading "${path.basename(filePath)}": ${e.message}`);
            }
        }
        this.filePath = filePath;
    }
    /**
     * Loads the common-versions.json data from the specified file path.
     * If the file has not been created yet, then an empty object is returned.
     */
    static loadFromFile(jsonFilePath, rushConfiguration) {
        let commonVersionsJson = undefined;
        if (FileSystem.exists(jsonFilePath)) {
            commonVersionsJson = JsonFile.loadAndValidate(jsonFilePath, CommonVersionsConfiguration._jsonSchema);
        }
        return new CommonVersionsConfiguration(commonVersionsJson, jsonFilePath, rushConfiguration);
    }
    static _deserializeTable(map, object) {
        if (object) {
            for (const [key, value] of Object.entries(object)) {
                map.set(key, value);
            }
        }
    }
    static _serializeTable(map) {
        const table = {};
        const keys = [...map.keys()];
        keys.sort();
        for (const key of keys) {
            table[key] = map.get(key);
        }
        return table;
    }
    /**
     * Get a sha1 hash of the preferred versions.
     */
    getPreferredVersionsHash() {
        // Sort so that the hash is stable
        const orderedPreferredVersions = new Map(this._preferredVersions.protectedView);
        Sort.sortMapKeys(orderedPreferredVersions);
        // JSON.stringify does not support maps, so we need to convert to an object first
        const preferredVersionsObj = MapExtensions.toObject(orderedPreferredVersions);
        return crypto.createHash('sha1').update(JSON.stringify(preferredVersionsObj)).digest('hex');
    }
    /**
     * Writes the "common-versions.json" file to disk, using the filename that was passed to loadFromFile().
     */
    save() {
        if (this._modified) {
            JsonFile.save(this._serialize(), this.filePath, {
                updateExistingFile: true,
                ignoreUndefinedValues: true
            });
            this._modified = false;
            return true;
        }
        return false;
    }
    /**
     * Returns preferredVersions.
     */
    getAllPreferredVersions() {
        const allPreferredVersions = new Map();
        MapExtensions.mergeFromMap(allPreferredVersions, this.preferredVersions);
        return allPreferredVersions;
    }
    _onSetPreferredVersions(source, key, value) {
        PackageNameParsers.permissive.validate(key);
        this._modified = true;
        return value;
    }
    _onSetAllowedAlternativeVersions(source, key, value) {
        PackageNameParsers.permissive.validate(key);
        this._modified = true;
        return value;
    }
    _serialize() {
        let preferredVersions;
        if (this._preferredVersions.size) {
            preferredVersions = CommonVersionsConfiguration._serializeTable(this.preferredVersions);
        }
        let allowedAlternativeVersions;
        if (this._allowedAlternativeVersions.size) {
            allowedAlternativeVersions = CommonVersionsConfiguration._serializeTable(this.allowedAlternativeVersions);
        }
        const result = {
            $schema: JsonSchemaUrls.commonVersions,
            preferredVersions,
            implicitlyPreferredVersions: this.implicitlyPreferredVersions,
            allowedAlternativeVersions,
            ensureConsistentVersions: this._commonVersionsJsonHasEnsureConsistentVersionsProperty
                ? this.ensureConsistentVersions
                : undefined
        };
        return result;
    }
}
CommonVersionsConfiguration._jsonSchema = JsonSchema.fromLoadedObject(schemaJson);
//# sourceMappingURL=CommonVersionsConfiguration.js.map