"use strict";
/*
 * Copyright 2022 Fernando Boucquez
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModifyMultisigService = void 0;
const inquirer_1 = require("inquirer");
const symbol_sdk_1 = require("symbol-sdk");
const AnnounceService_1 = require("./AnnounceService");
const BootstrapUtils_1 = require("./BootstrapUtils");
const ConfigLoader_1 = require("./ConfigLoader");
const TransactionUtils_1 = require("./TransactionUtils");
class ModifyMultisigService {
    constructor(logger, params) {
        this.logger = logger;
        this.params = params;
        this.configLoader = new ConfigLoader_1.ConfigLoader(logger);
    }
    async run(passedPresetData, passedAddresses) {
        const presetData = passedPresetData !== null && passedPresetData !== void 0 ? passedPresetData : this.configLoader.loadExistingPresetData(this.params.target, this.params.password);
        const addresses = passedAddresses !== null && passedAddresses !== void 0 ? passedAddresses : this.configLoader.loadExistingAddresses(this.params.target, this.params.password);
        const customPreset = this.configLoader.loadCustomPreset(this.params.customPreset, this.params.password);
        await new AnnounceService_1.AnnounceService(this.logger).announce(this.params.url, this.params.maxFee, this.params.useKnownRestGateways, this.params.ready, this.params.target, this.configLoader.mergePresets(presetData, customPreset), addresses, this, 'some', this.params.serviceProviderPublicKey);
    }
    async createTransactions({ presetData, deadline, maxFee, mainAccount }) {
        const networkType = presetData.networkType;
        const addressAdditions = await this.resolveAddressAdditions(networkType, this.params.addressAdditions);
        const addressDeletions = await this.resolveAddressDeletions(networkType, this.params.addressDeletions);
        const minApprovalDelta = await this.resolveMinApprovalDelta(this.params.minApprovalDelta);
        const minRemovalDelta = await this.resolveMinRemovalDelta(this.params.minRemovalDelta);
        const url = this.params.url.replace(/\/$/, '');
        const repositoryFactory = await TransactionUtils_1.TransactionUtils.getRepositoryFactory(this.logger, presetData, this.params.useKnownRestGateways ? undefined : url);
        const multisigInfo = await TransactionUtils_1.TransactionUtils.getMultisigAccount(repositoryFactory, mainAccount.address);
        this.validateParams(addressAdditions, addressDeletions, minRemovalDelta, minApprovalDelta, multisigInfo);
        this.logger.info(`Creating multisig account modification transaction [addressAdditions: "${addressAdditions === null || addressAdditions === void 0 ? void 0 : addressAdditions.map((a) => a.plain()).join(' , ')}", addressDeletions: "${addressDeletions === null || addressDeletions === void 0 ? void 0 : addressDeletions.map((a) => a.plain()).join(' , ')}", minApprovalDelta: ${minApprovalDelta}, minRemovalDelta: ${minRemovalDelta}]`);
        const multisigAccountModificationTransaction = symbol_sdk_1.MultisigAccountModificationTransaction.create(deadline, minApprovalDelta, minRemovalDelta, addressAdditions ? addressAdditions : [], addressDeletions ? addressDeletions : [], networkType, maxFee);
        return [multisigAccountModificationTransaction];
    }
    async resolveMinRemovalDelta(delta) {
        return this.resolveDelta('minRemovalDelta', 'Minimum removal delta:', delta);
    }
    async resolveMinApprovalDelta(delta) {
        return this.resolveDelta('minApprovalDelta', 'Minimum approval delta:', delta);
    }
    async resolveDelta(name, message, delta) {
        return delta !== undefined
            ? delta
            : (await inquirer_1.prompt([
                {
                    name,
                    message,
                    type: 'number',
                    default: 0,
                },
            ]))[name];
    }
    async resolveAddressAdditions(networkType, cosigners) {
        return this.resolveCosigners(networkType, 'addressAdditions', 'Enter the cosignatory addresses to add (separated by a comma) <Press enter to skip>:', cosigners);
    }
    async resolveAddressDeletions(networkType, cosigners) {
        return this.resolveCosigners(networkType, 'addressDeletions', 'Enter the cosignatory addresses to remove (separated by a comma) <Press enter to skip>:', cosigners);
    }
    async resolveCosigners(networkType, name, message, cosigners) {
        const resolution = cosigners !== undefined
            ? cosigners
            : (await inquirer_1.prompt([
                {
                    name,
                    message,
                },
            ]))[name];
        if (!resolution) {
            return [];
        }
        const cosignatoryAddresses = resolution.split(',');
        return this.toAddresses(networkType, cosignatoryAddresses);
    }
    toAddresses(networkType, addresses) {
        return ((addresses === null || addresses === void 0 ? void 0 : addresses.map((addressString) => {
            return this.toAddress(addressString.trim(), networkType);
        })) || []);
    }
    toAddress(addressString, networkType) {
        if (!symbol_sdk_1.Address.isValidRawAddress(addressString)) {
            throw new Error(`Address ${addressString} is not valid!`);
        }
        const address = symbol_sdk_1.Address.createFromRawAddress(addressString);
        if (address.networkType !== networkType) {
            throw new Error(`Address ${addressString} invalid network type. Expected ${networkType} but got ${address.networkType}`);
        }
        return address;
    }
    validateParams(addressAdditions, addressDeletions, minRemovalDelta, minApprovalDelta, currentMultisigInfo) {
        // calculate new min approval
        const newMinApproval = currentMultisigInfo ? currentMultisigInfo.minApproval + (minApprovalDelta || 0) : minApprovalDelta || 0;
        // calculate new min approval
        const newMinRemoval = currentMultisigInfo ? currentMultisigInfo.minRemoval + (minRemovalDelta || 0) : minRemovalDelta || 0;
        // calculate the delta of added cosigners
        const numberOfAddedCosigners = ((addressAdditions === null || addressAdditions === void 0 ? void 0 : addressAdditions.length) || 0) - ((addressDeletions === null || addressDeletions === void 0 ? void 0 : addressDeletions.length) || 0);
        const newCosignatoryNumber = currentMultisigInfo
            ? currentMultisigInfo.cosignatoryAddresses.length + numberOfAddedCosigners
            : numberOfAddedCosigners;
        for (const addressToAdd of addressAdditions || []) {
            if (currentMultisigInfo === null || currentMultisigInfo === void 0 ? void 0 : currentMultisigInfo.cosignatoryAddresses.some((ca) => ca && ca.plain() === addressToAdd.plain())) {
                throw new Error(`Cannot add cosignatory! ${addressToAdd.plain()} is already a cosignatory!`);
            }
        }
        for (const addressToRemove of addressDeletions || []) {
            if (!(currentMultisigInfo === null || currentMultisigInfo === void 0 ? void 0 : currentMultisigInfo.cosignatoryAddresses.some((ca) => ca && ca.plain() === addressToRemove.plain()))) {
                throw new Error(`Cannot remove cosignatory! ${addressToRemove.plain()} is not an actual cosignatory!`);
            }
        }
        if (newCosignatoryNumber < newMinApproval) {
            throw new Error(`There are ${newMinApproval - newCosignatoryNumber} more required cosignatories than available cosignatories for min. approval. Please add cosignatories or reduce the min. approval delta.`);
        }
        if (newCosignatoryNumber < newMinRemoval) {
            throw new Error(`There are ${newMinRemoval - newCosignatoryNumber}  more required cosignatories than available cosignatories for min removal. Please add cosignatories or reduce the min. removal delta.`);
        }
        if (newCosignatoryNumber > 0 && (newMinApproval == 0 || newMinRemoval == 0)) {
            throw new Error(`Minimum approval and/or minimum removal cannot be set to 0 while there are ${newCosignatoryNumber} cosignatories in your list.`);
        }
    }
}
exports.ModifyMultisigService = ModifyMultisigService;
ModifyMultisigService.defaultParams = {
    target: BootstrapUtils_1.BootstrapUtils.defaultTargetFolder,
    useKnownRestGateways: false,
    ready: false,
    url: 'http://localhost:3000',
    maxFee: 100000,
};
