"use strict";

const bitcoin = require("bitcoinjs-lib");
const bip32 = require("bip32");
const bip39 = require("bip39");
const ecc = require("tiny-secp256k1");
const { ECPairFactory } = require("ecpair");
const { ethers } = require("ethers");
const { TronWeb } = require("tronweb");
const crypto = require("crypto");

bitcoin.initEccLib(ecc);
const ECPair = ECPairFactory(ecc);
const BIP32 = bip32.BIP32Factory(ecc);

// ---------- Config / Paths ----------
const PATHS = {
  BTC: {
    mainnet: "m/84'/0'/0'/0/0", // BIP84 p2wpkh
    testnet: "m/84'/1'/0'/0/0",
  },
  EVM: "m/44'/60'/0'/0/0",      // ETH + EVM chains
  TRON: "m/44'/195'/0'/0/0",    // TRX
};

// ---------- Helpers ----------
function createMnemonic(strength = 128) {
  // 128 bits = 12 words, 256 bits = 24 words
  if (![128, 160, 192, 224, 256].includes(strength)) {
    throw new Error("Invalid mnemonic strength. Use 128..256 in steps of 32.");
  }
  return bip39.generateMnemonic(strength);
}

function validateMnemonic(mnemonic) {
  return bip39.validateMnemonic(mnemonic);
}

async function seedFromMnemonic(mnemonic, passphrase = "") {
  if (!validateMnemonic(mnemonic)) throw new Error("Invalid mnemonic.");
  // passphrase = BIP39 optional password (NOT the same as wallet password)
  return bip39.mnemonicToSeed(mnemonic, passphrase); // Buffer
}

// ---------- Derivers ----------
function deriveBTC({ seed, testnet = false, account = 0, index = 0 }) {
  const network = testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;

  // If you later want multiple accounts/indices, you can build path dynamically.
  // Keeping it standard and explicit for safety:
  const basePath = testnet ? "m/84'/1'/0'/0" : "m/84'/0'/0'/0";
  const path = `${basePath}/${index}`;

  const root = BIP32.fromSeed(seed, network);
  const child = root.derivePath(path);

  if (!child.privateKey) throw new Error("BTC derivation failed: no private key.");

  const keyPair = ECPair.fromPrivateKey(child.privateKey, { network });
  const { address } = bitcoin.payments.p2wpkh({
    pubkey: keyPair.publicKey,
    network,
  });

  return {
    chain: testnet ? "BTC_TESTNET" : "BTC",
    address,
    derivationPath: path,
    // NOTE: privateKey omitted by default by caller (see deriveAll)
    _privateKeyWIF: keyPair.toWIF(),
    _publicKeyHex: Buffer.from(keyPair.publicKey).toString("hex"),
  };
}

function deriveEVM({ mnemonic, passphrase = "", path = PATHS.EVM }) {
  // ethers v6: deterministic from phrase (+ optional passphrase)
  // fromPhrase(phrase, password, path)
  const wallet = ethers.HDNodeWallet.fromPhrase(mnemonic, passphrase, path);

  return {
    chain: "EVM",
    address: wallet.address,
    derivationPath: path,
    _privateKey: wallet.privateKey,
    _publicKey: wallet.publicKey,
    // Same address can receive ETH and ERC20 tokens (including USDT) on the same EVM network.
    networks: {
      ETH: wallet.address,
      ERC20: wallet.address,
      ARBITRUM: wallet.address,
      OPTIMISM: wallet.address,
      BASE: wallet.address,
      POLYGON: wallet.address,
      BEP20: wallet.address, // BSC is EVM-compatible but different network
      AVAXC: wallet.address, // Avalanche C-Chain
    },
  };
}

function deriveTRON({ seed, path = PATHS.TRON }) {
  const root = BIP32.fromSeed(seed);
  const child = root.derivePath(path);

  if (!child.privateKey) throw new Error("TRON derivation failed");

  const privHex = child.privateKey.toString("hex");

  // ✅ Correct modern way
  const base58 = TronWeb.utils.crypto.getBase58CheckAddress(
    TronWeb.utils.crypto.computeAddress(privHex)
  );

  const hexAddr = TronWeb.address.toHex(base58);

  return {
    chain: "TRON",
    address: base58,
    hexAddress: hexAddr,
    derivationPath: path,
    _privateKey: privHex,
    networks: {
      TRON: base58,
      TRC20: base58,
    },
  };
}


// ---------- One-call: Single mnemonic => all chains ----------
async function deriveAll({
  mnemonic,
  mnemonicStrength = 128,
  passphrase = "",
  testnetBTC = false,
  indexBTC = 0,
  includeSecrets = false, // <-- default SAFE: no private keys/mnemonic in output
} = {}) {
  const phrase = mnemonic ?? createMnemonic(mnemonicStrength);
  if (!validateMnemonic(phrase)) throw new Error("Invalid mnemonic.");

  const seed = await seedFromMnemonic(phrase, passphrase);

  const btc = deriveBTC({ seed, testnet: testnetBTC, index: indexBTC });
  const evm = deriveEVM({ mnemonic: phrase, passphrase, path: PATHS.EVM });
  //const tron = deriveTRON({ seed, path: PATHS.TRON });

  // Strip secrets unless explicitly requested
  if (!includeSecrets) {
    delete btc._privateKeyWIF;
    delete btc._publicKeyHex;
     delete evm._privateKey;
    delete evm._publicKey;
   //delete tron._privateKey;
    return {
      mnemonicPresent: Boolean(mnemonic),
      // don’t return seed or mnemonic in safe mode
      btc: { chain: btc.chain, address: btc.address, derivationPath: btc.derivationPath },
      evm: { chain: evm.chain, address: evm.address, derivationPath: evm.derivationPath, networks: evm.networks },
      //tron: { chain: tron.chain, address: tron.address, hexAddress: tron.hexAddress, derivationPath: tron.derivationPath, networks: tron.networks },
    };
  }

  // If you really need secrets (e.g., internal wallet service), return them explicitly.
  return {
    mnemonic: phrase,
    seedHex: seed.toString("hex"),
    btc: { ...btc, privateKeyWIF: btc._privateKeyWIF, publicKeyHex: btc._publicKeyHex },
    evm: { ...evm, privateKey: evm._privateKey, publicKey: evm._publicKey },
    //tron: { ...tron, privateKey: tron._privateKey },
  };
}

// ---------- Encryption helpers (for storing mnemonic safely) ----------
// AES-256-GCM with scrypt KDF.
// Store: { salt, iv, tag, ciphertext } (all hex)
function encryptMnemonic({ mnemonic, password }) {
  if (!password || password.length < 10) {
    throw new Error("Use a strong password (>= 10 chars) for mnemonic encryption.");
  }
  if (!validateMnemonic(mnemonic)) throw new Error("Invalid mnemonic.");

  const salt = crypto.randomBytes(16);
  const iv = crypto.randomBytes(12); // recommended for GCM
  const key = crypto.scryptSync(password, salt, 32);

  const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
  const ciphertext = Buffer.concat([cipher.update(mnemonic, "utf8"), cipher.final()]);
  const tag = cipher.getAuthTag();

  return {
    salt: salt.toString("hex"),
    iv: iv.toString("hex"),
    tag: tag.toString("hex"),
    ciphertext: ciphertext.toString("hex"),
    algo: "aes-256-gcm",
    kdf: "scrypt",
  };
}

function decryptMnemonic({ payload, password }) {
  const { salt, iv, tag, ciphertext } = payload || {};
  if (!salt || !iv || !tag || !ciphertext) throw new Error("Invalid encrypted payload.");

  const key = crypto.scryptSync(password, Buffer.from(salt, "hex"), 32);
  const decipher = crypto.createDecipheriv("aes-256-gcm", key, Buffer.from(iv, "hex"));
  decipher.setAuthTag(Buffer.from(tag, "hex"));

  const mnemonic = Buffer.concat([
    decipher.update(Buffer.from(ciphertext, "hex")),
    decipher.final(),
  ]).toString("utf8");

  if (!validateMnemonic(mnemonic)) throw new Error("Decryption succeeded but mnemonic is invalid.");
  return mnemonic;
}

module.exports = {
  PATHS,
  createMnemonic,
  validateMnemonic,
  seedFromMnemonic,
  deriveBTC,
  deriveEVM,
  deriveTRON,
  deriveAll,
  encryptMnemonic,
  decryptMnemonic,
};
