import {AccountIndexes, AccountKeys, AddressKey} from "../../../models/wallet.entities";
import {BaseAccount} from "../interfaces/BaseAccountInterface";
import {
    AccountType, classifyTransaction,
    fetchTotalBalance,
    fetchTransactionsHistory,
    sumBalance,
    sumTokensBalance
} from "../../../utils/WalletUtils";
import {PrivateKey} from "libnexa-ts";
import {TransactionEntity} from "../../../models/transaction.entities";

export default class DefaultAccount extends BaseAccount {

    private readonly _accountIndexes: AccountIndexes; // either a list of indexes or a single bip44 account index
    private _accountKeys: AccountKeys; // either a list of keys or a single key

    constructor(bip44Account: number, accountIndexes: AccountIndexes, accountKeys: AccountKeys) {
        super(bip44Account)
        if (accountIndexes.rIndex < 0) {
            throw new Error(`Can not create nexa account with an rindex of ${accountIndexes.rIndex}. must be >= 0.`);
        }
        if (accountIndexes.cIndex < 0) {
            throw new Error(`Can not create nexa account with an cindex of ${accountIndexes.cIndex}. must be >= 0.`);
        }
        this._accountIndexes = accountIndexes;
        this._accountKeys = accountKeys;
    }

    // this is used in AccountStore.ts to get the key to be used in the map for this account
    public getAccountStoreKey() {
        return String(this._bip44Account);
    }

    public getAccountType(): AccountType {
        return AccountType.NEXA_ACCOUNT
    }

    public getNewAddress() {
        return this._accountKeys.receiveKeys[this._accountKeys.receiveKeys.length - 1]?.address ?? '';
    }

    public getNewChangeAddress(): string {
        return this._accountKeys.changeKeys[this._accountKeys.changeKeys.length - 1]?.address ?? ''
    }

    get accountIndexes(): AccountIndexes {
        return this._accountIndexes;
    }

    get accountKeys(): AccountKeys {
        return this._accountKeys;
    }

    async loadBalances(): Promise<void> {
        let balances = await fetchTotalBalance(this._accountKeys.receiveKeys.concat(this._accountKeys.changeKeys));
        let tokenBalances = this._accountKeys.receiveKeys.concat(this._accountKeys.changeKeys).map(k => k.tokensBalance);
        super.balance = sumBalance(balances)
        super.tokenBalances = sumTokensBalance(tokenBalances)
    }

    getKeyFromAddress(address: string): AddressKey {
        const allKeys = this._accountKeys.receiveKeys.concat(this._accountKeys.changeKeys)
        const keyFound = allKeys.find(key => key.address === address)
        return keyFound!
    }

    async getTransactions(fromHeight?: number, address?: string): Promise<Map<string, TransactionEntity>> {

        let receiveAddresses = this.accountKeys.receiveKeys.map(ak => ak.address);
        let changeAddresses = this.accountKeys.changeKeys.map(ak => ak.address);
        let allAddresses = receiveAddresses.concat(changeAddresses);

        let rTxs = fetchTransactionsHistory(address != null ? [address]: receiveAddresses, fromHeight ?? 0);
        let cTxs = fetchTransactionsHistory(address != null ? [address] : receiveAddresses, fromHeight ?? 0);

        let [rData, cData] = await Promise.all([rTxs, cTxs]);

        let txHistory = rData.txs;
        for (let tx of cData.txs.values()) {
            txHistory.set(tx.tx_hash, tx);
        }

        let txPromises: TransactionEntity[] = [];
        for (let tx of txHistory.values()) {
            let t = await classifyTransaction(tx, allAddresses);
            txPromises.push(t);
        }
        await Promise.all(txPromises)

        for (let txEntity of txPromises){
            this.transactions.set(txEntity.txId, txEntity)
        }
        return this.transactions
    }
}
