"use strict";
/*
 * Copyright 2020 NEM
 *
 * 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.ConfigService = exports.KeyName = exports.Preset = void 0;
const fs = require("fs");
const fs_1 = require("fs");
const _ = require("lodash");
const path_1 = require("path");
const symbol_sdk_1 = require("symbol-sdk");
const logger_1 = require("../logger");
const LoggerFactory_1 = require("../logger/LoggerFactory");
const model_1 = require("../model");
const AgentCertificateService_1 = require("./AgentCertificateService");
const BootstrapUtils_1 = require("./BootstrapUtils");
const CertificateService_1 = require("./CertificateService");
const CommandUtils_1 = require("./CommandUtils");
const ConfigLoader_1 = require("./ConfigLoader");
const CryptoUtils_1 = require("./CryptoUtils");
const NemgenService_1 = require("./NemgenService");
const ReportService_1 = require("./ReportService");
const RewardProgramService_1 = require("./RewardProgramService");
const VotingService_1 = require("./VotingService");
/**
 * Defined presets.
 */
var Preset;
(function (Preset) {
    Preset["bootstrap"] = "bootstrap";
    Preset["testnet"] = "testnet";
    Preset["mainnet"] = "mainnet";
})(Preset = exports.Preset || (exports.Preset = {}));
var KeyName;
(function (KeyName) {
    KeyName["Main"] = "Main";
    KeyName["Remote"] = "Remote";
    KeyName["Transport"] = "Transport";
    KeyName["Voting"] = "Voting";
    KeyName["VRF"] = "VRF";
    KeyName["NemesisSigner"] = "Nemesis Signer";
    KeyName["NemesisAccount"] = "Nemesis Account";
})(KeyName = exports.KeyName || (exports.KeyName = {}));
const logger = LoggerFactory_1.default.getLogger(logger_1.LogType.System);
class ConfigService {
    constructor(root, params) {
        this.root = root;
        this.params = params;
        this.configLoader = new ConfigLoader_1.ConfigLoader();
    }
    async run() {
        const target = this.params.target;
        try {
            if (this.params.reset) {
                BootstrapUtils_1.BootstrapUtils.deleteFolder(target);
            }
            const presetLocation = this.configLoader.getGeneratedPresetLocation(target);
            const addressesLocation = this.configLoader.getGeneratedAddressLocation(target);
            const password = this.params.password;
            if (fs.existsSync(presetLocation) && !this.params.upgrade) {
                logger.info(`The generated preset ${presetLocation} already exist, ignoring configuration. (run -r to reset or --upgrade to upgrade)`);
                const presetData = this.configLoader.loadExistingPresetData(target, password);
                const addresses = this.configLoader.loadExistingAddresses(target, password);
                if (this.params.report) {
                    await new ReportService_1.ReportService(this.root, this.params).run(presetData);
                }
                return { presetData, addresses };
            }
            const oldPresetData = this.configLoader.loadExistingPresetDataIfPreset(target, password);
            const oldAddresses = this.configLoader.loadExistingAddressesIfPreset(target, password);
            if (oldAddresses && !oldPresetData) {
                throw new BootstrapUtils_1.KnownError(`Configuration cannot be upgraded without a previous ${presetLocation} file. (run -r to reset)`);
            }
            if (!oldAddresses && oldPresetData) {
                throw new BootstrapUtils_1.KnownError(`Configuration cannot be upgraded without a previous ${addressesLocation} file. (run -r to reset)`);
            }
            if (oldAddresses && oldPresetData) {
                logger.info('Upgrading configuration...');
            }
            const presetData = this.resolveCurrentPresetData(oldPresetData, password);
            const addresses = await this.configLoader.generateRandomConfiguration(oldAddresses, presetData);
            if (this.params.pullImages)
                await BootstrapUtils_1.BootstrapUtils.pullImage(presetData.symbolServerToolsImage);
            const privateKeySecurityMode = CryptoUtils_1.CryptoUtils.getPrivateKeySecurityMode(presetData.privateKeySecurityMode);
            await BootstrapUtils_1.BootstrapUtils.mkdir(target);
            this.cleanUpConfiguration(presetData);
            await this.generateNodeCertificates(presetData, addresses);
            await this.generateAgentCertificates(presetData);
            await this.generateNodes(presetData, addresses);
            await this.generateGateways(presetData);
            await this.generateExplorers(presetData);
            await this.generateWallets(presetData);
            const isUpgrade = !!oldPresetData || !!oldAddresses;
            await this.resolveNemesis(presetData, addresses, isUpgrade);
            await this.copyNemesis(addresses);
            if (this.params.report) {
                await new ReportService_1.ReportService(this.root, this.params).run(presetData);
            }
            await BootstrapUtils_1.BootstrapUtils.writeYaml(addressesLocation, CryptoUtils_1.CryptoUtils.removePrivateKeysAccordingToSecurityMode(addresses, privateKeySecurityMode), password);
            await BootstrapUtils_1.BootstrapUtils.writeYaml(presetLocation, CryptoUtils_1.CryptoUtils.removePrivateKeys(presetData), password);
            logger.info(`Configuration generated.`);
            return { presetData, addresses };
        }
        catch (e) {
            if (e.known) {
                logger.error(e.message);
            }
            else {
                logger.error(`Unknown error generating the configuration. ${e.message}`);
                logger.error(`The target folder '${target}' should be deleted!!!`);
                console.log(e);
            }
            throw e;
        }
    }
    resolveCurrentPresetData(oldPresetData, password) {
        return this.configLoader.createPresetData(Object.assign(Object.assign({}, this.params), { root: this.root, password: password, oldPresetData }));
    }
    async copyNemesis(addresses) {
        const target = this.params.target;
        const nemesisSeedFolder = BootstrapUtils_1.BootstrapUtils.getTargetNemesisFolder(target, false, 'seed');
        await this.validateSeedFolder(nemesisSeedFolder, `Invalid final seed folder ${nemesisSeedFolder}`);
        await Promise.all((addresses.nodes || []).map(async (account) => {
            const name = account.name;
            const dataFolder = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(target, false, name, 'data');
            await BootstrapUtils_1.BootstrapUtils.mkdir(dataFolder);
            const seedFolder = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(target, false, name, 'seed');
            await BootstrapUtils_1.BootstrapUtils.generateConfiguration({}, nemesisSeedFolder, seedFolder);
        }));
    }
    async validateSeedFolder(nemesisSeedFolder, message) {
        BootstrapUtils_1.BootstrapUtils.validateFolder(nemesisSeedFolder);
        const seedData = path_1.join(nemesisSeedFolder, '00000', '00001.dat');
        if (!fs_1.existsSync(seedData)) {
            throw new BootstrapUtils_1.KnownError(`File ${seedData} doesn't exist! ${message}`);
        }
        const seedIndex = path_1.join(nemesisSeedFolder, 'index.dat');
        if (!fs_1.existsSync(seedIndex)) {
            throw new BootstrapUtils_1.KnownError(`File ${seedIndex} doesn't exist! ${message}`);
        }
    }
    async resolveNemesis(presetData, addresses, isUpgrade) {
        const target = this.params.target;
        const nemesisSeedFolder = BootstrapUtils_1.BootstrapUtils.getTargetNemesisFolder(target, false, 'seed');
        await BootstrapUtils_1.BootstrapUtils.mkdir(nemesisSeedFolder);
        if (presetData.nemesis) {
            if (isUpgrade) {
                logger.info('Nemesis data cannot be generated when upgrading...');
            }
            else {
                await this.generateNemesisConfig(presetData, addresses);
                await this.validateSeedFolder(nemesisSeedFolder, `Is the generated nemesis seed a valid seed folder?`);
            }
            return;
        }
        if (isUpgrade) {
            logger.info('Upgrading genesis on upgrade!');
        }
        await BootstrapUtils_1.BootstrapUtils.deleteFolder(nemesisSeedFolder);
        await BootstrapUtils_1.BootstrapUtils.mkdir(nemesisSeedFolder);
        if (presetData.nemesisSeedFolder) {
            await this.validateSeedFolder(presetData.nemesisSeedFolder, `Is the provided preset nemesisSeedFolder: ${presetData.nemesisSeedFolder} a valid seed folder?`);
            logger.info(`Using custom nemesis seed folder in ${presetData.nemesisSeedFolder}`);
            await BootstrapUtils_1.BootstrapUtils.generateConfiguration({}, presetData.nemesisSeedFolder, nemesisSeedFolder);
            return;
        }
        const finalNemesisSeed = path_1.join(this.root, 'presets', presetData.preset, 'seed');
        if (fs_1.existsSync(finalNemesisSeed)) {
            await BootstrapUtils_1.BootstrapUtils.generateConfiguration({}, finalNemesisSeed, nemesisSeedFolder);
            await this.validateSeedFolder(nemesisSeedFolder, `Is the ${presetData.preset} preset default seed a valid seed folder?`);
            return;
        }
        logger.warn(`Seed for preset ${presetData.preset} could not be found in ${finalNemesisSeed}`);
        throw new Error('Seed could not be found!!!!');
    }
    async generateNodes(presetData, addresses) {
        await Promise.all((addresses.nodes || []).map(async (account, index) => await this.generateNodeConfiguration(account, index, presetData, addresses)));
    }
    async generateNodeCertificates(presetData, addresses) {
        await Promise.all((addresses.nodes || []).map(async (account) => {
            return await new CertificateService_1.CertificateService(this.root, this.params).run(presetData.networkType, presetData.symbolServerToolsImage, account.name, {
                main: account.main,
                transport: account.transport,
            });
        }));
    }
    async generateAgentCertificates(presetData) {
        await Promise.all((presetData.nodes || [])
            .filter((n) => n.rewardProgram)
            .map(async (account) => {
            return await new AgentCertificateService_1.AgentCertificateService(this.root, this.params).run(presetData.symbolServerToolsImage, account.name);
        }));
    }
    async generateNodeConfiguration(account, index, presetData, addresses) {
        var _a;
        const copyFrom = path_1.join(this.root, 'config', 'node');
        const name = account.name;
        const serverConfig = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(this.params.target, false, name, 'server-config');
        const brokerConfig = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(this.params.target, false, name, 'broker-config');
        const nodePreset = (presetData.nodes || [])[index];
        const harvesterSigningPrivateKey = nodePreset.harvesting
            ? await CommandUtils_1.CommandUtils.resolvePrivateKey(presetData.networkType, account.remote || account.main, account.remote ? KeyName.Remote : KeyName.Main, account.name, 'storing the harvesterSigningPrivateKey in the server properties')
            : '';
        const harvesterVrfPrivateKey = await CommandUtils_1.CommandUtils.resolvePrivateKey(presetData.networkType, account.vrf, KeyName.VRF, account.name, 'storing the harvesterVrfPrivateKey in the server properties');
        const beneficiaryAddress = nodePreset.beneficiaryAddress || presetData.beneficiaryAddress;
        const generatedContext = {
            name: name,
            friendlyName: (nodePreset === null || nodePreset === void 0 ? void 0 : nodePreset.friendlyName) || account.friendlyName,
            harvesterSigningPrivateKey: harvesterSigningPrivateKey,
            harvesterVrfPrivateKey: harvesterVrfPrivateKey,
            unfinalizedBlocksDuration: nodePreset.voting
                ? presetData.votingUnfinalizedBlocksDuration
                : presetData.nonVotingUnfinalizedBlocksDuration,
            beneficiaryAddress: beneficiaryAddress == undefined ? account.main.address : beneficiaryAddress,
            roles: ConfigLoader_1.ConfigLoader.resolveRoles(nodePreset),
        };
        const templateContext = Object.assign(Object.assign(Object.assign({}, presetData), generatedContext), nodePreset);
        const excludeFiles = [];
        // Exclude files depending on the enabled extensions. To complete...
        if (!templateContext.harvesting) {
            excludeFiles.push('config-harvesting.properties');
        }
        if (!templateContext.networkheight) {
            excludeFiles.push('config-networkheight.properties');
        }
        if (nodePreset.rewardProgram) {
            if (!nodePreset.host) {
                throw new Error(`Cannot create reward program configuration. You need to provide a host field in preset: ${nodePreset.name}`);
            }
            const restService = (_a = presetData.gateways) === null || _a === void 0 ? void 0 : _a.find((g) => g.apiNodeName == nodePreset.name);
            if (!restService) {
                throw new Error(`Cannot create reward program configuration. There is not rest gateway for the api node: ${nodePreset.name}`);
            }
            const nodePrivateKey = await CommandUtils_1.CommandUtils.resolvePrivateKey(presetData.networkType, account.transport, KeyName.Transport, account.name, 'creating the agent properties');
            const rewardProgram = RewardProgramService_1.RewardProgramService.getRewardProgram(nodePreset.rewardProgram);
            templateContext.restGatewayUrl = nodePreset.restGatewayUrl || `http://${restService.host || nodePreset.host}:3000`;
            templateContext.rewardProgram = rewardProgram;
            templateContext.serverVersion = nodePreset.serverVersion || presetData.serverVersion;
            templateContext.nodePrivateKey = nodePrivateKey;
            const copyFrom = path_1.join(this.root, 'config', 'agent');
            const agentConfig = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(this.params.target, false, name, 'agent');
            await BootstrapUtils_1.BootstrapUtils.generateConfiguration(templateContext, copyFrom, agentConfig, []);
        }
        const serverRecoveryConfig = {
            addressextractionRecovery: false,
            mongoRecovery: false,
            zeromqRecovery: false,
            filespoolingRecovery: true,
            hashcacheRecovery: true,
        };
        const brokerRecoveryConfig = {
            addressextractionRecovery: true,
            mongoRecovery: true,
            zeromqRecovery: true,
            filespoolingRecovery: false,
            hashcacheRecovery: true,
        };
        logger.info(`Generating ${name} server configuration`);
        await BootstrapUtils_1.BootstrapUtils.generateConfiguration(Object.assign(Object.assign({}, serverRecoveryConfig), templateContext), copyFrom, serverConfig, excludeFiles);
        const peersP2PFile = await this.generateP2PFile(presetData, addresses, presetData.peersP2PListLimit, serverConfig, model_1.NodeType.PEER_NODE, (nodePresetData) => !!nodePresetData.syncsource && nodePresetData != nodePreset, 'peers-p2p.json');
        const peersApiFile = await this.generateP2PFile(presetData, addresses, presetData.peersApiListLimit, serverConfig, model_1.NodeType.API_NODE, (nodePresetData) => nodePresetData.api && nodePresetData != nodePreset, 'peers-api.json');
        if (nodePreset.brokerName) {
            logger.info(`Generating ${nodePreset.brokerName} broker configuration`);
            await BootstrapUtils_1.BootstrapUtils.generateConfiguration(Object.assign(Object.assign({}, brokerRecoveryConfig), templateContext), copyFrom, brokerConfig, excludeFiles);
            fs_1.copyFileSync(peersP2PFile, path_1.join(path_1.join(brokerConfig, 'resources', 'peers-p2p.json')));
            fs_1.copyFileSync(peersApiFile, path_1.join(path_1.join(brokerConfig, 'resources', 'peers-api.json')));
        }
        await new VotingService_1.VotingService(this.params).run(presetData, account, nodePreset);
    }
    async generateP2PFile(presetData, addresses, listLimit, outputFolder, type, nodePresetDataFunction, jsonFileName) {
        var _a;
        const thisNetworkKnownPeers = (presetData.nodes || [])
            .map((nodePresetData, index) => {
            if (!nodePresetDataFunction(nodePresetData)) {
                return undefined;
            }
            const node = (addresses.nodes || [])[index];
            return {
                publicKey: node.main.publicKey,
                endpoint: {
                    host: nodePresetData.host || '',
                    port: 7900,
                },
                metadata: {
                    name: nodePresetData.friendlyName,
                    roles: ConfigLoader_1.ConfigLoader.resolveRoles(nodePresetData),
                },
            };
        })
            .filter((i) => i);
        const globalKnownPeers = ((_a = presetData.knownPeers) === null || _a === void 0 ? void 0 : _a[type]) || [];
        const data = {
            _info: `this file contains a list of ${type} peers`,
            knownPeers: _.sampleSize([...thisNetworkKnownPeers, ...globalKnownPeers], listLimit),
        };
        const peerFile = path_1.join(outputFolder, `resources`, jsonFileName);
        await fs.promises.writeFile(peerFile, JSON.stringify(data, null, 2));
        await fs.promises.chmod(peerFile, 0o600);
        return peerFile;
    }
    async generateNemesisConfig(presetData, addresses) {
        if (!presetData.nemesis) {
            throw new Error('nemesis must not be defined!');
        }
        const target = this.params.target;
        const nemesisWorkingDir = BootstrapUtils_1.BootstrapUtils.getTargetNemesisFolder(target, false);
        const transactionsDirectory = path_1.join(nemesisWorkingDir, presetData.nemesis.transactionsDirectory || presetData.transactionsDirectory);
        await BootstrapUtils_1.BootstrapUtils.mkdir(transactionsDirectory);
        const copyFrom = path_1.join(this.root, `config`, `nemesis`);
        const moveTo = path_1.join(nemesisWorkingDir, `server-config`);
        const templateContext = Object.assign(Object.assign({}, presetData), { addresses });
        await Promise.all((addresses.nodes || []).filter((n) => n.vrf).map((n) => this.createVrfTransaction(transactionsDirectory, presetData, n)));
        await Promise.all((addresses.nodes || [])
            .filter((n) => n.remote)
            .map((n) => this.createAccountKeyLinkTransaction(transactionsDirectory, presetData, n)));
        await Promise.all((addresses.nodes || [])
            .filter((n) => n.voting)
            .map((n) => this.createVotingKeyTransaction(transactionsDirectory, presetData, n)));
        if (presetData.nemesis.mosaics && (presetData.nemesis.transactions || presetData.nemesis.balances)) {
            logger.info('Opt In mode is ON!!! balances or transactions have been provided');
            if (presetData.nemesis.transactions) {
                const transactionHashes = [];
                const transactions = (await Promise.all(Object.entries(presetData.nemesis.transactions || {})
                    .map(([key, payload]) => {
                    const transactionHash = symbol_sdk_1.Transaction.createTransactionHash(payload, Array.from(symbol_sdk_1.Convert.hexToUint8(presetData.nemesisGenerationHashSeed)));
                    if (transactionHashes.indexOf(transactionHash) > -1) {
                        logger.warn(`Transaction ${key} wth hash ${transactionHash} already exist. Excluded from folder.`);
                        return undefined;
                    }
                    transactionHashes.push(transactionHash);
                    return this.storeTransaction(transactionsDirectory, key, payload);
                })
                    .filter((p) => p))).filter((p) => p);
                logger.info(`Found ${transactions.length} opted in transactions.`);
            }
            const currencyMosaic = presetData.nemesis.mosaics[0];
            const nglAccount = currencyMosaic.currencyDistributions[0];
            const originalNglAccountBalance = nglAccount.amount;
            if (!nglAccount) {
                throw Error('"NGL" account could not be found for opt in!');
            }
            let totalOptedInBalance = 0;
            if (presetData.nemesis.balances) {
                Object.entries(presetData.nemesis.balances || {}).forEach(([address, amount]) => {
                    totalOptedInBalance += amount;
                    currencyMosaic.currencyDistributions.push({ address, amount });
                });
                logger.info(`Removing ${Object.keys(presetData.nemesis.balances).length} accounts (total of ${totalOptedInBalance}) from "ngl" account ${nglAccount.address}`);
            }
            nglAccount.amount = nglAccount.amount - totalOptedInBalance;
            const providedBalances = Object.values(currencyMosaic.currencyDistributions)
                .map((d) => d.amount)
                .reduce((a, b) => a + b, 0);
            const currentBalance = providedBalances;
            if (nglAccount.amount < 1) {
                throw new Error(`NGL account didn't have enough balance (${originalNglAccountBalance}) to paid all the supplied optedin namespaces and accounts of ${currentBalance}`);
            }
            if (currentBalance !== currencyMosaic.supply) {
                throw new Error(`Current supplied balance of ${currentBalance} is different from expected supply of ${currencyMosaic.supply}`);
            }
        }
        await BootstrapUtils_1.BootstrapUtils.generateConfiguration(templateContext, copyFrom, moveTo);
        await new NemgenService_1.NemgenService(this.root, this.params).run(presetData);
    }
    async createVrfTransaction(transactionsDirectory, presetData, node) {
        if (!node.vrf) {
            throw new Error('VRF keys should have been generated!!');
        }
        if (!node.main) {
            throw new Error('Main keys should have been generated!!');
        }
        const deadline = symbol_sdk_1.Deadline.createFromDTO('1');
        const vrf = symbol_sdk_1.VrfKeyLinkTransaction.create(deadline, node.vrf.publicKey, symbol_sdk_1.LinkAction.Link, presetData.networkType, symbol_sdk_1.UInt64.fromUint(0));
        const mainPrivateKey = await CommandUtils_1.CommandUtils.resolvePrivateKey(presetData.networkType, node.main, KeyName.Main, node.name, 'creating the vrf key link transactions');
        const account = symbol_sdk_1.Account.createFromPrivateKey(mainPrivateKey, presetData.networkType);
        const signedTransaction = account.sign(vrf, presetData.nemesisGenerationHashSeed);
        return await this.storeTransaction(transactionsDirectory, `vrf_${node.name}`, signedTransaction.payload);
    }
    async createAccountKeyLinkTransaction(transactionsDirectory, presetData, node) {
        if (!node.remote) {
            throw new Error('Remote keys should have been generated!!');
        }
        if (!node.main) {
            throw new Error('Main keys should have been generated!!');
        }
        const deadline = symbol_sdk_1.Deadline.createFromDTO('1');
        const akl = symbol_sdk_1.AccountKeyLinkTransaction.create(deadline, node.remote.publicKey, symbol_sdk_1.LinkAction.Link, presetData.networkType, symbol_sdk_1.UInt64.fromUint(0));
        const mainPrivateKey = await CommandUtils_1.CommandUtils.resolvePrivateKey(presetData.networkType, node.main, KeyName.Main, node.name, 'creating the account link transactions');
        const account = symbol_sdk_1.Account.createFromPrivateKey(mainPrivateKey, presetData.networkType);
        const signedTransaction = account.sign(akl, presetData.nemesisGenerationHashSeed);
        return await this.storeTransaction(transactionsDirectory, `remote_${node.name}`, signedTransaction.payload);
    }
    async createVotingKeyTransaction(transactionsDirectory, presetData, node) {
        if (!node.voting) {
            throw new Error('Voting keys should have been generated!!');
        }
        if (!node.main) {
            throw new Error('Main keys should have been generated!!');
        }
        const voting = BootstrapUtils_1.BootstrapUtils.createVotingKeyTransaction(node.voting.publicKey, symbol_sdk_1.LinkAction.Link, presetData, symbol_sdk_1.Deadline.createFromDTO('1'), symbol_sdk_1.UInt64.fromUint(0));
        const mainPrivateKey = await CommandUtils_1.CommandUtils.resolvePrivateKey(presetData.networkType, node.main, KeyName.Main, node.name, 'creating the voting key link transactions');
        const account = symbol_sdk_1.Account.createFromPrivateKey(mainPrivateKey, presetData.networkType);
        const signedTransaction = account.sign(voting, presetData.nemesisGenerationHashSeed);
        return await this.storeTransaction(transactionsDirectory, `voting_${node.name}`, signedTransaction.payload);
    }
    async storeTransaction(transactionsDirectory, name, payload) {
        const transaction = symbol_sdk_1.TransactionMapping.createFromPayload(payload);
        await fs.promises.writeFile(`${transactionsDirectory}/${name}.bin`, symbol_sdk_1.Convert.hexToUint8(payload));
        return transaction;
    }
    generateGateways(presetData) {
        return Promise.all((presetData.gateways || []).map(async (gatewayPreset, index) => {
            const copyFrom = path_1.join(this.root, 'config', 'rest-gateway');
            const templateContext = Object.assign(Object.assign({}, presetData), gatewayPreset);
            const name = templateContext.name || `rest-gateway-${index}`;
            const moveTo = BootstrapUtils_1.BootstrapUtils.getTargetGatewayFolder(this.params.target, false, name);
            await BootstrapUtils_1.BootstrapUtils.generateConfiguration(templateContext, copyFrom, moveTo);
            const apiNodeConfigFolder = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(this.params.target, false, gatewayPreset.apiNodeName, 'server-config', 'resources');
            const apiNodeCertFolder = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(this.params.target, false, gatewayPreset.apiNodeName, 'cert');
            await BootstrapUtils_1.BootstrapUtils.generateConfiguration({}, apiNodeConfigFolder, path_1.join(moveTo, 'api-node-config'), [], ['config-network.properties', 'config-node.properties']);
            await BootstrapUtils_1.BootstrapUtils.generateConfiguration({}, apiNodeCertFolder, path_1.join(moveTo, 'api-node-config', 'cert'), [], ['node.crt.pem', 'node.key.pem', 'ca.cert.pem']);
        }));
    }
    generateExplorers(presetData) {
        return Promise.all((presetData.explorers || []).map(async (explorerPreset, index) => {
            const copyFrom = path_1.join(this.root, 'config', 'explorer');
            const templateContext = Object.assign(Object.assign({}, presetData), explorerPreset);
            const name = templateContext.name || `explorer-${index}`;
            const moveTo = BootstrapUtils_1.BootstrapUtils.getTargetFolder(this.params.target, false, BootstrapUtils_1.BootstrapUtils.targetExplorersFolder, name);
            await BootstrapUtils_1.BootstrapUtils.generateConfiguration(templateContext, copyFrom, moveTo);
        }));
    }
    generateWallets(presetData) {
        return Promise.all((presetData.wallets || []).map(async (explorerPreset, index) => {
            const copyFrom = path_1.join(this.root, 'config', 'wallet');
            const templateContext = Object.assign(Object.assign({}, presetData), explorerPreset);
            const name = templateContext.name || `wallet-${index}`;
            const moveTo = BootstrapUtils_1.BootstrapUtils.getTargetFolder(this.params.target, false, BootstrapUtils_1.BootstrapUtils.targetWalletsFolder, name);
            await BootstrapUtils_1.BootstrapUtils.generateConfiguration(templateContext, copyFrom, moveTo);
            await fs_1.promises.chmod(path_1.join(moveTo, 'app.conf.js'), 0o777);
            await fs_1.promises.chmod(path_1.join(moveTo, 'fees.conf.js'), 0o777);
            await fs_1.promises.chmod(path_1.join(moveTo, 'network.conf.js'), 0o777);
            await fs_1.promises.chmod(path_1.join(moveTo, 'profileImporter.html'), 0o777);
            await Promise.all((explorerPreset.profiles || []).map(async (profile) => {
                if (!profile.name) {
                    throw new Error('Profile`s name must be provided in the wallets preset when creating wallet profiles.');
                }
                const profileJsonFileName = `wallet-profile-${profile.name}.json`;
                const loadProfileData = async () => {
                    if (profile.data) {
                        return JSON.stringify(profile.data, null, 2);
                    }
                    if (profile.location) {
                        return BootstrapUtils_1.BootstrapUtils.loadFileAsText(profile.location);
                    }
                    return BootstrapUtils_1.BootstrapUtils.loadFileAsText(profileJsonFileName);
                };
                try {
                    const profileData = await loadProfileData();
                    await BootstrapUtils_1.BootstrapUtils.writeTextFile(path_1.join(moveTo, profileJsonFileName), profileData);
                }
                catch (e) {
                    const message = `Cannot create Wallet profile with name '${profile.name}'. Do you have the file '${profileJsonFileName}' in the current folder?. ${e}`;
                    throw new Error(message);
                }
            }));
        }));
    }
    cleanUpConfiguration(presetData) {
        const target = this.params.target;
        (presetData.nodes || []).forEach(({ name }) => {
            const serverConfigFolder = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(target, false, name, 'server-config');
            BootstrapUtils_1.BootstrapUtils.deleteFolder(serverConfigFolder);
            const brokerConfigFolder = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(target, false, name, 'broker-config');
            BootstrapUtils_1.BootstrapUtils.deleteFolder(brokerConfigFolder);
            // Remove old user configs when upgrading.
            const userConfigFolder = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(target, false, name, 'userconfig');
            BootstrapUtils_1.BootstrapUtils.deleteFolder(userConfigFolder);
            const seedFolder = BootstrapUtils_1.BootstrapUtils.getTargetNodesFolder(target, false, name, 'seed');
            BootstrapUtils_1.BootstrapUtils.deleteFolder(seedFolder);
        });
        (presetData.gateways || []).forEach(({ name }) => {
            const configFolder = BootstrapUtils_1.BootstrapUtils.getTargetGatewayFolder(target, false, name);
            BootstrapUtils_1.BootstrapUtils.deleteFolder(configFolder);
        });
    }
}
exports.ConfigService = ConfigService;
ConfigService.defaultParams = {
    target: BootstrapUtils_1.BootstrapUtils.defaultTargetFolder,
    report: false,
    reset: false,
    upgrade: false,
    pullImages: false,
    user: BootstrapUtils_1.BootstrapUtils.CURRENT_USER,
};
