// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import * as path from 'node:path';
import { JsonFile, JsonSchema, FileSystem, NewlineKind, InternalError } from '@rushstack/node-core-library';
import { JsonSchemaUrls } from '../logic/JsonSchemaUrls';
import schemaJson from '../schemas/approved-packages.schema.json';
import { RushConstants } from '../logic/RushConstants';
/**
 * An item returned by ApprovedPackagesConfiguration
 * @public
 */
export class ApprovedPackagesItem {
    /**
     * @internal
     */
    constructor(packageName) {
        /**
         * The project categories that are allowed to use this package.
         */
        this.allowedCategories = new Set();
        this.packageName = packageName;
    }
}
/**
 * This represents the JSON file specified via the "approvedPackagesFile" option in rush.json.
 * @public
 */
export class ApprovedPackagesConfiguration {
    constructor(jsonFilename) {
        this.items = [];
        this._itemsByName = new Map();
        this._jsonFilename = jsonFilename;
        this.clear();
    }
    /**
     * Clears all the settings, returning to an empty state.
     */
    clear() {
        this._itemsByName.clear();
        this._loadedJson = {
            // Ensure this comes first in the key ordering
            $schema: '',
            packages: []
        };
    }
    getItemByName(packageName) {
        return this._itemsByName.get(packageName);
    }
    addOrUpdatePackage(packageName, reviewCategory) {
        let changed = false;
        let item = this._itemsByName.get(packageName);
        if (!item) {
            item = new ApprovedPackagesItem(packageName);
            this._addItem(item);
            changed = true;
        }
        if (reviewCategory && !item.allowedCategories.has(reviewCategory)) {
            item.allowedCategories.add(reviewCategory);
            changed = true;
        }
        return changed;
    }
    /**
     * If the file exists, calls loadFromFile().
     */
    tryLoadFromFile(approvedPackagesPolicyEnabled) {
        if (!FileSystem.exists(this._jsonFilename)) {
            return false;
        }
        this.loadFromFile();
        if (!approvedPackagesPolicyEnabled) {
            // eslint-disable-next-line no-console
            console.log(`Warning: Ignoring "${path.basename(this._jsonFilename)}" because the` +
                ` "approvedPackagesPolicy" setting was not specified in ${RushConstants.rushJsonFilename}`);
        }
        return false;
    }
    /**
     * Loads the configuration data from the filename that was passed to the constructor.
     */
    loadFromFile() {
        const approvedPackagesJson = JsonFile.loadAndValidate(this._jsonFilename, ApprovedPackagesConfiguration._jsonSchema);
        this.clear();
        for (const browserPackage of approvedPackagesJson.packages) {
            this._addItemJson(browserPackage, this._jsonFilename);
        }
    }
    /**
     * Loads the configuration data to the filename that was passed to the constructor.
     */
    saveToFile() {
        // Update the JSON structure that we already loaded, preserving any existing state
        // (which passed schema validation).
        // eslint-disable-next-line dot-notation
        this._loadedJson['$schema'] = JsonSchemaUrls.approvedPackages;
        this._loadedJson.packages = [];
        this.items.sort((a, b) => {
            return a.packageName.localeCompare(b.packageName);
        });
        for (const item of this.items) {
            // Sort the items from the set.
            const allowedCategories = Array.from(item.allowedCategories);
            allowedCategories.sort();
            const itemJson = {
                name: item.packageName,
                allowedCategories: allowedCategories
            };
            this._loadedJson.packages.push(itemJson);
        }
        // Save the file
        let body = JsonFile.stringify(this._loadedJson);
        // Unindent the allowedCategories array to improve readability
        body = body.replace(/("allowedCategories": +\[)([^\]]+)/g, (substring, ...args) => {
            return args[0] + args[1].replace(/\s+/g, ' ');
        });
        // Add a header
        body = '// DO NOT ADD COMMENTS IN THIS FILE.  They will be lost when the Rush tool resaves it.\n' + body;
        FileSystem.writeFile(this._jsonFilename, body, {
            convertLineEndings: NewlineKind.CrLf
        });
    }
    /**
     * Helper function only used by the constructor when loading the file.
     */
    _addItemJson(itemJson, jsonFilename) {
        if (this._itemsByName.has(itemJson.name)) {
            throw new Error(`Error loading package review file ${jsonFilename}:\n` +
                ` the name "${itemJson.name}" appears more than once`);
        }
        const item = new ApprovedPackagesItem(itemJson.name);
        if (itemJson.allowedCategories) {
            for (const allowedCategory of itemJson.allowedCategories) {
                item.allowedCategories.add(allowedCategory);
            }
        }
        this._addItem(item);
    }
    /**
     * Helper function that adds an already created ApprovedPackagesItem to the
     * list and set.
     */
    _addItem(item) {
        if (this._itemsByName.has(item.packageName)) {
            throw new InternalError('Duplicate key');
        }
        this.items.push(item);
        this._itemsByName.set(item.packageName, item);
    }
}
ApprovedPackagesConfiguration._jsonSchema = JsonSchema.fromLoadedObject(schemaJson);
//# sourceMappingURL=ApprovedPackagesConfiguration.js.map