// @ts-nocheck
import { fromBase58Check, toBase58Check, Writer, Reader, toArray, toHex } from '../primitives/utils.js';
import * as Hash from '../primitives/Hash.js';
import Curve from '../primitives/Curve.js';
import PrivateKey from '../primitives/PrivateKey.js';
import PublicKey from '../primitives/PublicKey.js';
import Random from '../primitives/Random.js';
import BigNumber from '../primitives/BigNumber.js';
/**
 * @deprecated
 * The HD class implements the Bitcoin Improvement Proposal 32 (BIP32) hierarchical deterministic wallets.
 * It allows the generation of child keys from a master key, ensuring a tree-like structure of keys and addresses.
 * This class is deprecated due to the introduction of BRC-42, which offers an enhanced key derivation scheme.
 * BRC-42 uses invoice numbers for key derivation, improving privacy and scalability compared to BIP32.
 *
 * @class HD
 * @deprecated Replaced by BRC-42 which uses invoice numbers and supports private derivation.
 */
export default class HD {
    versionBytesNum;
    depth;
    parentFingerPrint;
    childIndex;
    chainCode;
    privKey;
    pubKey;
    constants = {
        pubKey: 0x0488b21e,
        privKey: 0x0488ade4
    };
    /**
     * Constructor for the BIP32 HD wallet.
     * Initializes an HD wallet with optional parameters for version bytes, depth, parent fingerprint, child index, chain code, private key, and public key.
     * @param versionBytesNum - Version bytes number for the wallet.
     * @param depth - Depth of the key in the hierarchy.
     * @param parentFingerPrint - Fingerprint of the parent key.
     * @param childIndex - Index of the child key.
     * @param chainCode - Chain code for key derivation.
     * @param privKey - Private key of the wallet.
     * @param pubKey - Public key of the wallet.
     */
    constructor(versionBytesNum, depth, parentFingerPrint, childIndex, chainCode, privKey, pubKey) {
        this.versionBytesNum = versionBytesNum;
        this.depth = depth;
        this.parentFingerPrint = parentFingerPrint;
        this.childIndex = childIndex;
        this.chainCode = chainCode;
        this.privKey = privKey;
        this.pubKey = pubKey;
    }
    /**
     * Generates a new HD wallet with random keys.
     * This method creates a root HD wallet with randomly generated private and public keys.
     * @returns {HD} The current HD instance with generated keys.
     */
    fromRandom() {
        this.versionBytesNum = this.constants.privKey;
        this.depth = 0x00;
        this.parentFingerPrint = [0, 0, 0, 0];
        this.childIndex = 0;
        this.chainCode = Random(32);
        this.privKey = PrivateKey.fromRandom();
        this.pubKey = this.privKey.toPublicKey();
        return this;
    }
    /**
     * Generates a new HD wallet with random keys.
     * This method creates a root HD wallet with randomly generated private and public keys.
     * @returns {HD} A new HD instance with generated keys.
     * @static
     */
    static fromRandom() {
        return new this().fromRandom();
    }
    /**
     * Initializes the HD wallet from a given base58 encoded string.
     * This method decodes a provided string to set up the HD wallet's properties.
     * @param str - A base58 encoded string representing the wallet.
     * @returns {HD} The new instance with properties set from the string.
     */
    static fromString(str) {
        return new this().fromString(str);
    }
    /**
     * Initializes the HD wallet from a given base58 encoded string.
     * This method decodes a provided string to set up the HD wallet's properties.
     * @param str - A base58 encoded string representing the wallet.
     * @returns {HD} The current instance with properties set from the string.
     */
    fromString(str) {
        const decoded = fromBase58Check(str);
        return this.fromBinary([...decoded.prefix, ...decoded.data]);
    }
    /**
     * Initializes the HD wallet from a seed.
     * This method generates keys and other properties from a given seed, conforming to the BIP32 specification.
     * @param bytes - An array of bytes representing the seed.
     * @returns {HD} The current instance with properties set from the seed.
     */
    static fromSeed(bytes) {
        return new this().fromSeed(bytes);
    }
    /**
     * Initializes the HD wallet from a seed.
     * This method generates keys and other properties from a given seed, conforming to the BIP32 specification.
     * @param bytes - An array of bytes representing the seed.
     * @returns {HD} The current instance with properties set from the seed.
     */
    fromSeed(bytes) {
        if (bytes.length < 128 / 8) {
            throw new Error('Need more than 128 bits of entropy');
        }
        if (bytes.length > 512 / 8) {
            throw new Error('More than 512 bits of entropy is nonstandard');
        }
        const hash = Hash.sha512hmac(toArray('Bitcoin seed', 'utf8'), bytes);
        this.depth = 0x00;
        this.parentFingerPrint = [0, 0, 0, 0];
        this.childIndex = 0;
        this.chainCode = hash.slice(32, 64);
        this.versionBytesNum = this.constants.privKey;
        this.privKey = new PrivateKey(hash.slice(0, 32));
        this.pubKey = this.privKey.toPublicKey();
        return this;
    }
    /**
     * Initializes the HD wallet from a binary buffer.
     * Parses a binary buffer to set up the wallet's properties.
     * @param buf - A buffer containing the wallet data.
     * @returns {HD} The new instance with properties set from the buffer.
     */
    static fromBinary(buf) {
        return new this().fromBinary(buf);
    }
    /**
     * Initializes the HD wallet from a binary buffer.
     * Parses a binary buffer to set up the wallet's properties.
     * @param buf - A buffer containing the wallet data.
     * @returns {HD} The current instance with properties set from the buffer.
     */
    fromBinary(buf) {
        // Both pub and private extended keys are 78 buf
        if (buf.length !== 78) {
            throw new Error('incorrect bip32 data length');
        }
        const reader = new Reader(buf);
        this.versionBytesNum = reader.readUInt32BE();
        this.depth = reader.readUInt8();
        this.parentFingerPrint = reader.read(4);
        this.childIndex = reader.readUInt32BE();
        this.chainCode = reader.read(32);
        const keyBytes = reader.read(33);
        const isPrivate = this.versionBytesNum === this.constants.privKey;
        const isPublic = this.versionBytesNum === this.constants.pubKey;
        if (isPrivate && keyBytes[0] === 0) {
            this.privKey = new PrivateKey(keyBytes.slice(1, 33));
            this.pubKey = this.privKey.toPublicKey();
        }
        else if (isPublic && (keyBytes[0] === 0x02 || keyBytes[0] === 0x03)) {
            this.pubKey = PublicKey.fromString(toHex(keyBytes));
        }
        else {
            throw new Error('Invalid key');
        }
        return this;
    }
    /**
     * Converts the HD wallet to a base58 encoded string.
     * This method provides a string representation of the HD wallet's current state.
     * @returns {string} A base58 encoded string of the HD wallet.
     */
    toString() {
        const bin = this.toBinary();
        return toBase58Check(bin, []);
    }
    /**
     * Derives a child HD wallet based on a given path.
     * The path specifies the hierarchy of the child key to be derived.
     * @param path - A string representing the derivation path (e.g., 'm/0'/1).
     * @returns {HD} A new HD instance representing the derived child wallet.
     */
    derive(path) {
        if (path === 'm') {
            return this;
        }
        const e = path.split('/');
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        let bip32 = this;
        for (const [i, c] of e.entries()) {
            if (i === 0) { // Since `i` is now a number, compare it to 0
                if (c !== 'm') {
                    throw new Error('invalid path');
                }
                continue;
            }
            const childMatch = /^(\d+)('?)$/.exec(c);
            if (childMatch === null) {
                throw new Error('invalid path');
            }
            const childIndexValue = Number.parseInt(childMatch[1], 10);
            if (childIndexValue > 0x7fffffff) {
                throw new Error('invalid path');
            }
            const usePrivate = childMatch[2] === "'";
            let childIndex = childIndexValue;
            if (usePrivate) {
                childIndex += 0x80000000;
            }
            bip32 = bip32.deriveChild(childIndex);
        }
        return bip32;
    }
    /**
     * Derives a child HD wallet from the current wallet based on an index.
     * This method generates either a private or public child key depending on the current wallet's state.
     * @param i - The index of the child key to derive.
     * @returns {HD} A new HD instance representing the derived child wallet.
     */
    deriveChild(i) {
        if (typeof i !== 'number') {
            throw new TypeError('i must be a number');
        }
        const ibc = [];
        ibc.push((i >> 24) & 0xff);
        ibc.push((i >> 16) & 0xff);
        ibc.push((i >> 8) & 0xff);
        ibc.push(i & 0xff);
        const ib = [...ibc];
        const usePrivate = (i & 0x80000000) !== 0;
        const isPrivate = this.versionBytesNum === this.constants.privKey;
        if (usePrivate && (this.privKey === null || this.privKey === undefined || !isPrivate)) {
            throw new Error('Cannot do private key derivation without private key');
        }
        let ret = null;
        if (this.privKey !== null && this.privKey !== undefined) {
            let data = null;
            if (usePrivate) {
                data = [0, ...this.privKey.toArray('be', 32), ...ib];
            }
            else {
                data = [...this.pubKey.encode(true), ...ib];
            }
            const hash = Hash.sha512hmac(this.chainCode, data);
            const il = new BigNumber(hash.slice(0, 32));
            const ir = hash.slice(32, 64);
            // ki = IL + kpar (mod n).
            const k = il.add(this.privKey).mod(new Curve().n);
            ret = new HD();
            ret.chainCode = ir;
            ret.privKey = new PrivateKey(k.toArray());
            ret.pubKey = ret.privKey.toPublicKey();
        }
        else {
            const data = [...this.pubKey.encode(true), ...ib];
            const hash = Hash.sha512hmac(this.chainCode, data);
            const il = new BigNumber(hash.slice(0, 32));
            const ir = hash.slice(32, 64);
            // Ki = (IL + kpar)*G = IL*G + Kpar
            const ilG = new Curve().g.mul(il);
            const Kpar = this.pubKey;
            const Ki = ilG.add(Kpar);
            const newpub = new PublicKey(Ki.x, Ki.y);
            ret = new HD();
            ret.chainCode = ir;
            ret.pubKey = newpub;
        }
        ret.childIndex = i;
        const pubKeyhash = Hash.hash160(this.pubKey.encode(true));
        ret.parentFingerPrint = pubKeyhash.slice(0, 4);
        ret.versionBytesNum = this.versionBytesNum;
        ret.depth = this.depth + 1;
        return ret;
    }
    /**
     * Converts the current HD wallet to a public-only wallet.
     * This method strips away the private key information, leaving only the public part.
     * @returns {HD} A new HD instance representing the public-only wallet.
     */
    toPublic() {
        const bip32 = new HD(this.versionBytesNum, this.depth, this.parentFingerPrint, this.childIndex, this.chainCode, this.privKey, this.pubKey);
        bip32.versionBytesNum = this.constants.pubKey;
        bip32.privKey = undefined;
        return bip32;
    }
    /**
     * Converts the HD wallet into a binary representation.
     * This method serializes the wallet's properties into a binary format.
     * @returns {number[]} An array of numbers representing the binary data of the wallet.
     */
    toBinary() {
        const isPrivate = this.versionBytesNum === this.constants.privKey;
        const isPublic = this.versionBytesNum === this.constants.pubKey;
        if (isPrivate) {
            return new Writer()
                .writeUInt32BE(this.versionBytesNum)
                .writeUInt8(this.depth)
                .write(this.parentFingerPrint)
                .writeUInt32BE(this.childIndex)
                .write(this.chainCode)
                .writeUInt8(0)
                .write(this.privKey.toArray('be', 32))
                .toArray();
        }
        else if (isPublic) {
            return new Writer()
                .writeUInt32BE(this.versionBytesNum)
                .writeUInt8(this.depth)
                .write(this.parentFingerPrint)
                .writeUInt32BE(this.childIndex)
                .write(this.chainCode)
                .write(this.pubKey.encode(true))
                .toArray();
        }
        else {
            throw new Error('bip32: invalid versionBytesNum byte');
        }
    }
    /**
     * Checks if the HD wallet contains a private key.
     * This method determines whether the wallet is a private key wallet or a public key only wallet.
     * @returns {boolean} A boolean value indicating whether the wallet has a private key (true) or not (false).
     */
    isPrivate() {
        return this.versionBytesNum === this.constants.privKey;
    }
}
//# sourceMappingURL=HD.js.map