'use strict';

const { query, queryOne, transaction } = require('../config/db');

// ── Derive next address for a coin ────────────────────────────────
async function deriveAddress(userId, coin, storeId = null) {
  // Get xPub config
  const xpubRow = await queryOne(
    'SELECT * FROM xpub_keys WHERE user_id = ? AND coin = ?',
    [userId, coin]
  );

  if (!xpubRow) return null;

  const index   = xpubRow.current_index;
  let   address = null;

  // Attempt BIP32 derivation via Tatum API (handles all chains)
  const tatumKey = process.env.TATUM_API_KEY;
  if (tatumKey) {
    address = await deriveViaTatum(xpubRow.xpub, coin, index, tatumKey);
  }

  // Local BIP32 fallback for BTC/LTC
  if (!address && ['BTC','LTC','DOGE'].includes(coin)) {
    address = await deriveLocalBtc(xpubRow.xpub, coin, index, xpubRow.derivation);
  }

  // Final fallback — deterministic hash (development only)
  if (!address) {
    address = localFallback(coin, xpubRow.xpub, index);
  }

  // Update index and store address
  await transaction(async (conn) => {
    await conn.execute(
      'UPDATE xpub_keys SET current_index = ? WHERE id = ?',
      [index + 1, xpubRow.id]
    );
    await conn.execute(
      `INSERT IGNORE INTO derived_addresses (user_id, store_id, coin, address, idx, used)
       VALUES (?, ?, ?, ?, ?, 0)`,
      [userId, storeId, coin, address, index]
    );
  });

  return address;
}

// ── Tatum multi-chain derivation ──────────────────────────────────
async function deriveViaTatum(xpub, coin, index, apiKey) {
  const COINS = require('../config/coins');
  const coinData = COINS[coin];
  if (!coinData || !coinData.tatum_chain) return null;

  try {
    const axios = require('axios');
    // Tatum v3 generate address from xpub
    const chain = coinData.tatum_chain;
    const r = await axios.get(
      `https://api.tatum.io/v3/${chain.toLowerCase()}/address/${xpub}/${index}`,
      {
        headers: { 'x-api-key': apiKey },
        timeout: 8000,
      }
    );
    return r.data && r.data.address ? r.data.address : null;
  } catch (err) {
    console.warn(`[WALLET] Tatum derivation failed for ${coin}:`, err.message);
    return null;
  }
}

// ── Local BIP32 for BTC-like chains ──────────────────────────────
async function deriveLocalBtc(xpub, coin, index, derivationPath) {
  try {
    const ecc     = require('tiny-secp256k1');
    const { BIP32Factory } = require('bip32');
    const bitcoin = require('bitcoinjs-lib');

    const bip32 = BIP32Factory(ecc);

    // Select network
    let network = bitcoin.networks.bitcoin;
    if (coin === 'LTC') {
      network = {
        messagePrefix:  '\x19Litecoin Signed Message:\n',
        bech32:         'ltc',
        bip32:          { public: 0x019da462, private: 0x019d9cfe },
        pubKeyHash:     0x30,
        scriptHash:     0x32,
        wif:            0xb0,
      };
    } else if (coin === 'DOGE') {
      network = {
        messagePrefix:  '\x19Dogecoin Signed Message:\n',
        bech32:         'doge',
        bip32:          { public: 0x02facafd, private: 0x02fac398 },
        pubKeyHash:     0x1e,
        scriptHash:     0x16,
        wif:            0x9e,
      };
    }

    const root  = bip32.fromBase58(xpub, network);
    const child = root.derive(0).derive(index);

    let address;
    if (xpub.startsWith('zpub') || xpub.startsWith('vpub') || derivationPath.startsWith("m/84'")) {
      // Native SegWit (bech32)
      const { address: addr } = bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network });
      address = addr;
    } else if (xpub.startsWith('ypub') || derivationPath.startsWith("m/49'")) {
      // Wrapped SegWit (P2SH-P2WPKH)
      const p2wpkh = bitcoin.payments.p2wpkh({ pubkey: child.publicKey, network });
      const { address: addr } = bitcoin.payments.p2sh({ redeem: p2wpkh, network });
      address = addr;
    } else {
      // Legacy P2PKH
      const { address: addr } = bitcoin.payments.p2pkh({ pubkey: child.publicKey, network });
      address = addr;
    }

    return address;
  } catch (err) {
    console.warn(`[WALLET] Local BIP32 failed for ${coin}:`, err.message);
    return null;
  }
}

// ── Development fallback ──────────────────────────────────────────
function localFallback(coin, xpub, index) {
  const crypto  = require('crypto');
  const hash    = crypto.createHash('sha256').update(`${xpub}:${coin}:${index}`).digest('hex');
  const prefix  = { BTC:'bc1q', LTC:'ltc1q', ETH:'0x', DOGE:'D', TRX:'T' };
  const pre     = prefix[coin] || '';
  return pre + hash.slice(0, 30);
}

// ── Test xPub key (return sample addresses) ───────────────────────
async function testDerivation(xpub, coin, count = 5) {
  if (!xpub || xpub.length < 10) {
    return { valid: false, error: 'Invalid xPub key.' };
  }

  const addresses = [];
  const tatumKey  = process.env.TATUM_API_KEY;

  for (let i = 0; i < count; i++) {
    let addr = null;
    if (tatumKey) {
      addr = await deriveViaTatum(xpub, coin, i, tatumKey);
    }
    if (!addr && ['BTC','LTC','DOGE'].includes(coin)) {
      addr = await deriveLocalBtc(xpub, coin, i, '');
    }
    if (!addr) {
      addr = localFallback(coin, xpub, i);
    }
    addresses.push(addr);
  }

  return {
    valid:     true,
    coin,
    addresses,
    note: tatumKey
      ? 'Derived via Tatum API.'
      : 'Local derivation — add TATUM_API_KEY for all chains.',
  };
}

// ── Mark address as used ──────────────────────────────────────────
async function markAddressUsed(address, invoiceId) {
  await query(
    'UPDATE derived_addresses SET used = 1, invoice_id = ? WHERE address = ?',
    [invoiceId, address]
  );
}

module.exports = { deriveAddress, testDerivation, markAddressUsed };
