// Wallet operations: mnemonic generation, HD derivation, signing.
// All crypto delegated to ethers.js.

const { Mnemonic, HDNodeWallet, Wallet } = require("ethers");
const { DEBUG, DEBUG_MNEMONIC, BIP44_ETH_PATH } = require("./constants");

function generateMnemonic() {
    // This must stay the compile-time DEBUG constant. Do NOT switch it to
    // isDebug() from log.js: that also ORs in the runtime debugMode flag the
    // settings toggle drives, which would let a user of a release build turn
    // the hardcoded, publicly known test phrase back on for real wallets.
    if (DEBUG) return DEBUG_MNEMONIC;
    const m = Mnemonic.fromEntropy(
        globalThis.crypto.getRandomValues(new Uint8Array(16)),
    );
    return m.phrase;
}

function deriveAddressFromXpub(xpub, index) {
    const node = HDNodeWallet.fromExtendedKey(xpub);
    return node.deriveChild(index).address;
}

function hdWalletFromMnemonic(mnemonic) {
    const node = HDNodeWallet.fromPhrase(mnemonic, "", BIP44_ETH_PATH);
    const xpub = node.neuter().extendedKey;
    const firstAddress = node.deriveChild(0).address;
    return { xpub, firstAddress };
}

function hdWalletFromXprv(xprv) {
    const root = HDNodeWallet.fromExtendedKey(xprv);
    if (!root.privateKey) {
        throw new Error("Not an extended private key (xprv).");
    }
    const node = root.derivePath("44'/60'/0'/0");
    const xpub = node.neuter().extendedKey;
    const firstAddress = node.deriveChild(0).address;
    return { xpub, firstAddress };
}

function isValidXprv(key) {
    try {
        const node = HDNodeWallet.fromExtendedKey(key);
        return !!node.privateKey;
    } catch {
        return false;
    }
}

function addressFromPrivateKey(key) {
    const w = new Wallet(key);
    return w.address;
}

function getSignerForAddress(walletData, addrIndex, decryptedSecret) {
    if (walletData.type === "hd") {
        const node = HDNodeWallet.fromPhrase(
            decryptedSecret,
            "",
            BIP44_ETH_PATH,
        );
        return node.deriveChild(addrIndex);
    }
    if (walletData.type === "xprv") {
        const root = HDNodeWallet.fromExtendedKey(decryptedSecret);
        const node = root.derivePath("44'/60'/0'/0");
        return node.deriveChild(addrIndex);
    }
    return new Wallet(decryptedSecret);
}

function isValidMnemonic(mnemonic) {
    return Mnemonic.isValidMnemonic(mnemonic);
}

module.exports = {
    generateMnemonic,
    deriveAddressFromXpub,
    hdWalletFromMnemonic,
    hdWalletFromXprv,
    isValidXprv,
    addressFromPrivateKey,
    getSignerForAddress,
    isValidMnemonic,
};
