// SPDX-License-Identifier: Apache-2.0
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var AccountCommand_1;
import chalk from 'chalk';
import { BaseCommand } from './base.js';
import { IllegalArgumentError } from '../core/errors/illegal-argument-error.js';
import { SoloError } from '../core/errors/solo-error.js';
import { Flags as flags } from './flags.js';
import { Listr } from 'listr2';
import * as constants from '../core/constants.js';
import * as helpers from '../core/helpers.js';
import { entityId } from '../core/helpers.js';
import { AccountInfo, HbarUnit, Long, NodeUpdateTransaction, PrivateKey, } from '@hiero-ledger/sdk';
import { resolveNamespaceFromDeployment } from '../core/resolvers.js';
import { Templates } from '../core/templates.js';
import { SecretType } from '../integration/kube/resources/secret/secret-type.js';
import { Base64 } from 'js-base64';
import { inject, injectable } from 'tsyringe-neo';
import { InjectTokens } from '../core/dependency-injection/inject-tokens.js';
import { patchInject } from '../core/dependency-injection/container-helper.js';
let AccountCommand = class AccountCommand extends BaseCommand {
    static { AccountCommand_1 = this; }
    accountManager;
    systemAccounts;
    static ACCOUNT_KEY_USER_MESSAGE = 'where:\n' +
        '- privateKey: the hex-encoded private key which is used to sign transactions with in the Hiero SDKs\n' +
        '- privateKeyRaw: the Ethereum compatible private key, without the `0x` prefix\n' +
        '- for more information see: https://docs.hedera.com/hedera/core-concepts/keys-and-signatures';
    accountInfo;
    constructor(accountManager, systemAccounts) {
        super();
        this.accountManager = accountManager;
        this.systemAccounts = systemAccounts;
        this.accountManager = patchInject(accountManager, InjectTokens.AccountManager, this.constructor.name);
        this.accountInfo = null;
        this.systemAccounts = patchInject(systemAccounts, InjectTokens.SystemAccounts, this.constructor.name);
    }
    static INIT_FLAGS_LIST = {
        required: [flags.deployment],
        optional: [flags.nodeAliasesUnparsed, flags.clusterRef],
    };
    static CREATE_FLAGS_LIST = {
        required: [flags.deployment],
        optional: [
            flags.amount,
            flags.createAmount,
            flags.ecdsaPrivateKey,
            flags.privateKey,
            flags.ed25519PrivateKey,
            flags.generateEcdsaKey,
            flags.setAlias,
            flags.clusterRef,
        ],
    };
    static UPDATE_FLAGS_LIST = {
        required: [flags.accountId, flags.deployment],
        optional: [flags.amount, flags.ecdsaPrivateKey, flags.ed25519PrivateKey, flags.clusterRef],
    };
    static GET_FLAGS_LIST = {
        required: [flags.accountId, flags.deployment],
        optional: [flags.privateKey, flags.clusterRef],
    };
    async closeConnections() {
        await this.accountManager.close();
    }
    async buildAccountInfo(accountInfo, namespace, shouldRetrievePrivateKey) {
        if (!accountInfo || !(accountInfo instanceof AccountInfo)) {
            throw new IllegalArgumentError('An instance of AccountInfo is required');
        }
        const newAccountInfo = {
            accountId: accountInfo.accountId.toString(),
            publicKey: accountInfo.key.toString(),
            balance: accountInfo.balance.to(HbarUnit.Hbar).toNumber(),
        };
        if (shouldRetrievePrivateKey) {
            const accountKeys = await this.accountManager.getAccountKeysFromSecret(newAccountInfo.accountId, namespace);
            newAccountInfo.privateKey = accountKeys.privateKey;
            // reconstruct private key to retrieve EVM address if private key is ECDSA type
            try {
                const privateKey = PrivateKey.fromStringDer(newAccountInfo.privateKey);
                newAccountInfo.privateKeyRaw = privateKey.toStringRaw();
            }
            catch {
                throw new SoloError(`failed to retrieve EVM address for accountId ${newAccountInfo.accountId}`);
            }
        }
        return newAccountInfo;
    }
    async createNewAccount(context_) {
        if (context_.config.ecdsaPrivateKey) {
            context_.privateKey = PrivateKey.fromStringECDSA(context_.config.ecdsaPrivateKey);
        }
        else if (context_.config.ed25519PrivateKey) {
            context_.privateKey = PrivateKey.fromStringED25519(context_.config.ed25519PrivateKey);
        }
        else if (context_.config.generateEcdsaKey) {
            context_.privateKey = PrivateKey.generateECDSA();
        }
        else {
            context_.privateKey = PrivateKey.generateED25519();
        }
        return await this.accountManager.createNewAccount(context_.config.namespace, context_.privateKey, context_.config.amount, context_.config.ecdsaPrivateKey || context_.config.generateEcdsaKey ? context_.config.setAlias : false, context_.config.contextName);
    }
    getAccountInfo(context_) {
        return this.accountManager.accountInfoQuery(context_.config.accountId);
    }
    async updateAccountInfo(context_) {
        let amount = context_.config.amount;
        if (context_.config.ed25519PrivateKey) {
            if (!(await this.accountManager.sendAccountKeyUpdate(context_.accountInfo.accountId, context_.config.ed25519PrivateKey, context_.accountInfo.privateKey))) {
                throw new SoloError(`failed to update account keys for accountId ${context_.accountInfo.accountId}`);
            }
        }
        else {
            amount = amount || flags.amount.definition.defaultValue;
        }
        const hbarAmount = Number.parseFloat(amount.toString());
        if (amount && Number.isNaN(hbarAmount)) {
            throw new SoloError(`The HBAR amount was invalid: ${amount}`);
        }
        if (hbarAmount > 0) {
            const deployment = context_.config.deployment;
            if (!(await this.transferAmountFromOperator(context_.accountInfo.accountId, hbarAmount, deployment))) {
                throw new SoloError(`failed to transfer amount for accountId ${context_.accountInfo.accountId}`);
            }
            this.logger.debug(`sent transfer amount for account ${context_.accountInfo.accountId}`);
        }
        return true;
    }
    async transferAmountFromOperator(toAccountId, amount, deploymentName) {
        const operatorAccountId = this.accountManager.getOperatorAccountId(deploymentName);
        return await this.accountManager.transferAmount(operatorAccountId, toAccountId, amount);
    }
    async init(argv) {
        const self = this;
        const tasks = new Listr([
            {
                title: 'Initialize',
                task: async (context_, task) => {
                    await self.localConfig.load();
                    await self.remoteConfig.loadAndValidate(argv);
                    self.configManager.update(argv);
                    flags.disablePrompts([flags.clusterRef]);
                    const clusterReference = this.getClusterReference();
                    const contextName = this.getClusterContext(clusterReference);
                    const config = {
                        deployment: self.configManager.getFlag(flags.deployment),
                        clusterRef: clusterReference,
                        contextName,
                        namespace: await this.resolveNamespaceFromDeployment(task),
                        nodeAliases: helpers.parseNodeAliases(this.configManager.getFlag(flags.nodeAliasesUnparsed), this.remoteConfig.getConsensusNodes(), this.configManager),
                    };
                    await this.throwIfNamespaceIsMissing(config.contextName, config.namespace);
                    // set config in the context for later tasks to use
                    context_.config = config;
                    await self.accountManager.loadNodeClient(config.namespace, self.remoteConfig.getClusterRefs(), self.configManager.getFlag(flags.deployment), self.configManager.getFlag(flags.forcePortForward));
                },
            },
            {
                title: 'Update special account keys',
                task: (_, task) => {
                    return new Listr([
                        {
                            title: 'Prepare for account key updates',
                            task: async (context_) => {
                                const config = context_.config;
                                context_.updateSecrets = await self.k8Factory
                                    .getK8(config.clusterRef)
                                    .secrets()
                                    .list(config.namespace, ['solo.hedera.com/account-id'])
                                    .then((secrets) => secrets.length > 0);
                                context_.accountsBatchedSet = self.accountManager.batchAccounts(this.systemAccounts);
                                context_.resultTracker = {
                                    rejectedCount: 0,
                                    fulfilledCount: 0,
                                    skippedCount: 0,
                                };
                                // do a write transaction to trigger the handler and generate the system accounts to complete genesis
                                const deployment = config.deployment;
                                const treasuryAccountId = this.accountManager.getTreasuryAccountId(deployment);
                                const freezeAccountId = this.accountManager.getFreezeAccountId(deployment);
                                await self.accountManager.transferAmount(treasuryAccountId, freezeAccountId, 1);
                            },
                        },
                        {
                            title: 'Update special account key sets',
                            task: (context_) => {
                                const config = context_.config;
                                const subTasks = [];
                                const realm = this.localConfig.configuration.realmForDeployment(config.deployment);
                                const shard = this.localConfig.configuration.shardForDeployment(config.deployment);
                                for (const currentSet of context_.accountsBatchedSet) {
                                    const accountStart = entityId(shard, realm, currentSet[0]);
                                    const accountEnd = entityId(shard, realm, currentSet.at(-1));
                                    const rangeString = accountStart === accountEnd
                                        ? `${chalk.yellow(accountStart)}`
                                        : `${chalk.yellow(accountStart)} to ${chalk.yellow(accountEnd)}`;
                                    subTasks.push({
                                        title: `Updating accounts [${rangeString}]`,
                                        task: async (context_) => {
                                            const config = context_.config;
                                            context_.resultTracker = await self.accountManager.updateSpecialAccountsKeys(config.namespace, currentSet, context_.updateSecrets, context_.resultTracker, config.deployment);
                                        },
                                    });
                                }
                                // set up the sub-tasks
                                return task.newListr(subTasks, {
                                    concurrent: false,
                                    rendererOptions: {
                                        collapseSubtasks: false,
                                    },
                                });
                            },
                        },
                        {
                            title: 'Update node admin key',
                            task: async ({ config }) => {
                                const adminKey = PrivateKey.fromStringED25519(constants.GENESIS_KEY);
                                for (const nodeAlias of config.nodeAliases) {
                                    const nodeId = Templates.nodeIdFromNodeAlias(nodeAlias);
                                    const nodeClient = await self.accountManager.refreshNodeClient(config.namespace, self.remoteConfig.getClusterRefs(), nodeAlias, config.deployment);
                                    try {
                                        let nodeUpdateTx = new NodeUpdateTransaction().setNodeId(new Long(nodeId));
                                        const newPrivateKey = PrivateKey.generateED25519();
                                        nodeUpdateTx = nodeUpdateTx.setAdminKey(newPrivateKey.publicKey);
                                        nodeUpdateTx = nodeUpdateTx.freezeWith(nodeClient);
                                        nodeUpdateTx = await nodeUpdateTx.sign(newPrivateKey);
                                        const signedTx = await nodeUpdateTx.sign(adminKey);
                                        const txResp = await signedTx.execute(nodeClient);
                                        const nodeUpdateReceipt = await txResp.getReceipt(nodeClient);
                                        self.logger.debug(`NodeUpdateReceipt: ${nodeUpdateReceipt.toString()} for node ${nodeAlias}`);
                                        // save new key in k8s secret
                                        const data = {
                                            privateKey: Base64.encode(newPrivateKey.toString()),
                                            publicKey: Base64.encode(newPrivateKey.publicKey.toString()),
                                        };
                                        await this.k8Factory
                                            .getK8(config.contextName)
                                            .secrets()
                                            .create(config.namespace, Templates.renderNodeAdminKeyName(nodeAlias), SecretType.OPAQUE, data, { 'solo.hedera.com/node-admin-key': 'true' });
                                    }
                                    catch (error) {
                                        throw new SoloError(`Error updating admin key for node ${nodeAlias}: ${error.message}`, error);
                                    }
                                }
                            },
                        },
                        {
                            title: 'Display results',
                            task: ({ resultTracker: { fulfilledCount, skippedCount, rejectedCount } }) => {
                                self.logger.showUser(chalk.green(`Account keys updated SUCCESSFULLY: ${fulfilledCount}`));
                                if (skippedCount > 0) {
                                    self.logger.showUser(chalk.cyan(`Account keys updates SKIPPED: ${skippedCount}`));
                                }
                                if (rejectedCount > 0) {
                                    self.logger.showUser(chalk.yellowBright(`Account keys updates with ERROR: ${rejectedCount}`));
                                }
                                self.logger.showUser(chalk.gray('Waiting for sockets to be closed....'));
                                if (rejectedCount > 0) {
                                    throw new SoloError(`Account keys updates failed for ${rejectedCount} accounts.`);
                                }
                            },
                        },
                    ], {
                        concurrent: false,
                        rendererOptions: {
                            collapseSubtasks: false,
                        },
                    });
                },
            },
        ], constants.LISTR_DEFAULT_OPTIONS.DEFAULT);
        try {
            await tasks.run();
        }
        catch (error) {
            throw new SoloError(`Error in creating account: ${error.message}`, error);
        }
        finally {
            await this.closeConnections();
            // create two accounts to force the handler to trigger
            await self.create(argv);
            await self.create(argv);
        }
        return true;
    }
    async create(argv) {
        const self = this;
        const tasks = new Listr([
            {
                title: 'Initialize',
                task: async (context_, task) => {
                    await self.localConfig.load();
                    await self.remoteConfig.loadAndValidate(argv);
                    self.configManager.update(argv);
                    flags.disablePrompts([flags.clusterRef]);
                    const config = {
                        amount: self.configManager.getFlag(flags.amount),
                        ecdsaPrivateKey: self.configManager.getFlag(flags.ecdsaPrivateKey),
                        namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
                        deployment: self.configManager.getFlag(flags.deployment),
                        ed25519PrivateKey: self.configManager.getFlag(flags.ed25519PrivateKey),
                        setAlias: self.configManager.getFlag(flags.setAlias),
                        generateEcdsaKey: self.configManager.getFlag(flags.generateEcdsaKey),
                        privateKey: self.configManager.getFlag(flags.privateKey),
                        createAmount: self.configManager.getFlag(flags.createAmount),
                        clusterRef: self.configManager.getFlag(flags.clusterRef),
                    };
                    config.contextName =
                        this.localConfig.configuration.clusterRefs.get(config.clusterRef)?.toString() ??
                            self.k8Factory.default().contexts().readCurrent();
                    if (!config.amount) {
                        config.amount = flags.amount.definition.defaultValue;
                    }
                    if (!(await this.k8Factory.getK8(config.contextName).namespaces().has(config.namespace))) {
                        throw new SoloError(`namespace ${config.namespace} does not exist`);
                    }
                    // set config in the context for later tasks to use
                    context_.config = config;
                    await self.accountManager.loadNodeClient(context_.config.namespace, self.remoteConfig.getClusterRefs(), config.deployment, self.configManager.getFlag(flags.forcePortForward));
                },
            },
            {
                title: 'create the new account',
                task: async (context_, task) => {
                    const subTasks = [];
                    for (let index = 0; index < context_.config.createAmount; index++) {
                        subTasks.push({
                            title: `Create accounts [${index}]`,
                            task: async (context_) => {
                                self.accountInfo = await self.createNewAccount(context_);
                                const accountInfoCopy = { ...self.accountInfo };
                                if (!context_.config.privateKey) {
                                    delete accountInfoCopy.privateKey;
                                }
                                this.logger.showJSON('new account created', accountInfoCopy);
                                if (context_.config.privateKey) {
                                    this.logger.showUser(AccountCommand_1.ACCOUNT_KEY_USER_MESSAGE);
                                }
                            },
                        });
                    }
                    // set up the sub-tasks
                    return task.newListr(subTasks, {
                        concurrent: 8,
                        rendererOptions: {
                            collapseSubtasks: false,
                        },
                    });
                },
            },
        ], constants.LISTR_DEFAULT_OPTIONS.DEFAULT);
        try {
            await tasks.run();
        }
        catch (error) {
            throw new SoloError(`Error in creating account: ${error.message}`, error);
        }
        finally {
            await this.closeConnections();
        }
        return true;
    }
    async update(argv) {
        const self = this;
        const tasks = new Listr([
            {
                title: 'Initialize',
                task: async (context_, task) => {
                    await self.localConfig.load();
                    await self.remoteConfig.loadAndValidate(argv);
                    self.configManager.update(argv);
                    flags.disablePrompts([flags.clusterRef]);
                    await self.configManager.executePrompt(task, [flags.accountId]);
                    const config = {
                        accountId: self.configManager.getFlag(flags.accountId),
                        amount: self.configManager.getFlag(flags.amount),
                        namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
                        deployment: self.configManager.getFlag(flags.deployment),
                        ecdsaPrivateKey: self.configManager.getFlag(flags.ecdsaPrivateKey),
                        ed25519PrivateKey: self.configManager.getFlag(flags.ed25519PrivateKey),
                        clusterRef: self.configManager.getFlag(flags.clusterRef),
                    };
                    config.contextName =
                        this.localConfig.configuration.clusterRefs.get(config.clusterRef)?.toString() ??
                            self.k8Factory.default().contexts().readCurrent();
                    if (!(await this.k8Factory.getK8(config.contextName).namespaces().has(config.namespace))) {
                        throw new SoloError(`namespace ${config.namespace} does not exist`);
                    }
                    // set config in the context for later tasks to use
                    context_.config = config;
                    await self.accountManager.loadNodeClient(config.namespace, self.remoteConfig.getClusterRefs(), config.deployment, self.configManager.getFlag(flags.forcePortForward));
                },
            },
            {
                title: 'get the account info',
                task: async (context_) => {
                    context_.accountInfo = await self.buildAccountInfo(await self.getAccountInfo(context_), context_.config.namespace, !!context_.config.ed25519PrivateKey);
                },
            },
            {
                title: 'update the account',
                task: async (context_) => {
                    if (!(await self.updateAccountInfo(context_))) {
                        throw new SoloError(`An error occurred updating account ${context_.accountInfo.accountId}`);
                    }
                },
            },
            {
                title: 'get the updated account info',
                task: async (context_) => {
                    self.accountInfo = await self.buildAccountInfo(await self.getAccountInfo(context_), context_.config.namespace, false);
                    this.logger.showJSON('account info', self.accountInfo);
                    this.logger.showUser(AccountCommand_1.ACCOUNT_KEY_USER_MESSAGE);
                },
            },
        ], constants.LISTR_DEFAULT_OPTIONS.DEFAULT);
        try {
            await tasks.run();
        }
        catch (error) {
            throw new SoloError(`Error in updating account: ${error.message}`, error);
        }
        finally {
            await this.closeConnections();
        }
        return true;
    }
    async get(argv) {
        const self = this;
        const tasks = new Listr([
            {
                title: 'Initialize',
                task: async (context_, task) => {
                    await self.localConfig.load();
                    await self.remoteConfig.loadAndValidate(argv);
                    self.configManager.update(argv);
                    await self.configManager.executePrompt(task, [flags.accountId]);
                    flags.disablePrompts([flags.clusterRef]);
                    const config = {
                        accountId: self.configManager.getFlag(flags.accountId),
                        namespace: await resolveNamespaceFromDeployment(this.localConfig, this.configManager, task),
                        deployment: self.configManager.getFlag(flags.deployment),
                        privateKey: self.configManager.getFlag(flags.privateKey),
                        clusterRef: self.configManager.getFlag(flags.clusterRef),
                    };
                    config.contextName =
                        this.localConfig.configuration.clusterRefs.get(config.clusterRef)?.toString() ??
                            self.k8Factory.default().contexts().readCurrent();
                    if (!(await this.k8Factory.getK8(config.contextName).namespaces().has(config.namespace))) {
                        throw new SoloError(`namespace ${config.namespace} does not exist`);
                    }
                    // set config in the context for later tasks to use
                    context_.config = config;
                    await self.accountManager.loadNodeClient(config.namespace, self.remoteConfig.getClusterRefs(), config.deployment, self.configManager.getFlag(flags.forcePortForward));
                },
            },
            {
                title: 'get the account info',
                task: async (context_) => {
                    self.accountInfo = await self.buildAccountInfo(await self.getAccountInfo(context_), context_.config.namespace, context_.config.privateKey);
                    this.logger.showJSON('account info', self.accountInfo);
                    this.logger.showUser(AccountCommand_1.ACCOUNT_KEY_USER_MESSAGE);
                },
            },
        ], constants.LISTR_DEFAULT_OPTIONS.DEFAULT);
        try {
            await tasks.run();
        }
        catch (error) {
            throw new SoloError(`Error in getting account info: ${error.message}`, error);
        }
        finally {
            await this.closeConnections();
        }
        return true;
    }
    close() {
        return this.closeConnections();
    }
};
AccountCommand = AccountCommand_1 = __decorate([
    injectable(),
    __param(0, inject(InjectTokens.AccountManager)),
    __param(1, inject(InjectTokens.SystemAccounts)),
    __metadata("design:paramtypes", [Function, Array])
], AccountCommand);
export { AccountCommand };
//# sourceMappingURL=account.js.map