import { crypto } from "bitcoinjs-lib";
import { pathArrayToString } from "../bip32";
import { BufferWriter } from "@ledgerhq/psbtv2";
import { hashLeaf, Merkle } from "./merkle";

export type DefaultDescriptorTemplate = "pkh(@0/**)" | "sh(wpkh(@0/**))" | "wpkh(@0/**)" | "tr(@0/**)";

/**
 * The Bitcon hardware app uses a descriptors-like thing to describe
 * how to construct output scripts from keys. A "Wallet Policy" consists
 * of a "Descriptor Template" and a list of "keys". A key is basically
 * a serialized BIP32 extended public key with some added derivation path
 * information. This is documented at
 * https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/wallet.md
 */
export class WalletPolicy {
  descriptorTemplate: string;
  keys: string[];
  /**
   * For now, we only support default descriptor templates.
   */
  constructor(descriptorTemplate: DefaultDescriptorTemplate, key: string) {
    this.descriptorTemplate = descriptorTemplate;
    this.keys = [key];
  }

  getWalletId(): Buffer {
    // wallet_id (sha256 of the wallet serialization),
    return crypto.sha256(this.serialize());
  }

  serialize(): Buffer {
    const keyBuffers = this.keys.map(k => {
      return Buffer.from(k, "ascii");
    });
    const m = new Merkle(keyBuffers.map(k => hashLeaf(k)));

    const buf = new BufferWriter();
    buf.writeUInt8(0x02); // wallet policy version (2 is supported from version 2.1.0 of the app)
    buf.writeUInt8(0); // length of wallet name (empty string for default wallets)
    buf.writeVarInt(this.descriptorTemplate.length); // length of descriptor template
    buf.writeSlice(crypto.sha256(Buffer.from(this.descriptorTemplate, "ascii"))); // hash of descriptor template
    buf.writeVarInt(this.keys.length), buf.writeSlice(m.getRoot());
    return buf.buffer();
  }
}

export function createKey(masterFingerprint: Buffer, path: number[], xpub: string): string {
  const accountPath = pathArrayToString(path);
  return `[${masterFingerprint.toString("hex")}${accountPath.substring(1)}]${xpub}`;
}
