<?php
// hd_wallet_core.php
// مكتبة توليد عناوين Ethereum (BEP20) من محفظة HD واحدة (BIP32/BIP44)

declare(strict_types=1);

// يمكنك تخفيف الأخطاء لاحقاً إن أحببت
error_reporting(E_ALL);
ini_set('display_errors', '1');

require __DIR__ . '/vendor/autoload.php';

use kornrunner\Keccak;
use Elliptic\EC;

/**
 * تحويل mnemonic إلى seed عبر PBKDF2 (قياسي حسب BIP39)
 */
function mnemonic_to_seed(string $mnemonic, string $passphrase = ''): string
{
    $salt = 'mnemonic' . $passphrase;
    return hash_pbkdf2('sha512', $mnemonic, $salt, 2048, 64, true);
}

/**
 * تحويل مفتاح خاص إلى مفتاح عام باستخدام منحنى secp256k1
 */
function public_key_from_private(string $privKey): string
{
    $ec    = new EC('secp256k1');
    $pkHex = bin2hex($privKey);
    $pub   = $ec->keyFromPrivate($pkHex)->getPublic(false, 'hex'); // uncompressed (0x04...)
    return hex2bin($pub);
}

/**
 * تحويل public key إلى عنوان ETH (Keccak-256)
 */
function pub_to_eth_address(string $pubKey): string
{
    // إزالة البايت 0x04 من بداية الـ uncompressed public key
    $pub  = substr($pubKey, 1);
    $hash = Keccak::hash($pub, 256);
    // آخر 20 بايت (40 hex)
    return '0x' . strtolower(substr($hash, 24));
}

/**
 * اشتقاق مفتاح خاص child عبر BIP32 باستخدام hex + GMP فقط
 *
 * @param string $seed BIP39 seed (binary)
 * @param string $path مسار BIP32 كامل مثل m/44'/60'/0'/0/5
 *
 * @return string private key (binary 32 bytes)
 */
function derive_child_key(string $seed, string $path): string
{
    // master key من seed
    $I          = hash_hmac('sha512', $seed, 'Bitcoin seed', true);
    $masterPriv = substr($I, 0, 32);
    $masterChain= substr($I, 32);

    $priv  = $masterPriv;
    $chain = $masterChain;

    $segments = explode('/', $path);

    // order المنحنى secp256k1 كثابت
    // n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
    $n = gmp_init('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16);

    foreach ($segments as $seg) {
        if ($seg === 'm' || $seg === '') {
            continue;
        }

        $isHardened = false;
        if (str_ends_with($seg, "'")) {
            $isHardened = true;
            $seg        = (int) $seg;
            $seg       += 0x80000000;
        }

        $index = pack('N', (int) $seg);

        if ($isHardened) {
            $data = "\x00" . $priv . $index;
        } else {
            $pub  = public_key_from_private($priv);
            $data = $pub . $index;
        }

        $I  = hash_hmac('sha512', $data, $chain, true);
        $Il = substr($I, 0, 32);
        $Ir = substr($I, 32);

        // التحويل إلى أعداد عبر hex → GMP
        $Il_num   = gmp_init(bin2hex($Il), 16);
        $priv_num = gmp_init(bin2hex($priv), 16);

        $child_num = gmp_mod(gmp_add($Il_num, $priv_num), $n);
        $child_hex = str_pad(gmp_strval($child_num, 16), 64, '0', STR_PAD_LEFT);

        $priv  = hex2bin($child_hex);
        $chain = $Ir;
    }

    return $priv;
}

/**
 * توليد محفظة ETH من index معيّن
 *
 * @param string $mnemonic  جملة BIP39
 * @param int    $index     رقم المشتق (0,1,2,...)
 * @param string $basePath  مسار الأساس BIP44 (افتراضياً m/44'/60'/0'/0)
 *
 * @return array {
 *   index       : رقم المشتق,
 *   path        : المسار الكامل m/44'/60'/0'/0/{index},
 *   private_key : hex 64 خانة,
 *   address     : عنوان ETH يبدأ بـ 0x
 * }
 */
function eth_wallet(string $mnemonic, int $index, string $basePath = "m/44'/60'/0'/0"): array
{
    $seed = mnemonic_to_seed($mnemonic);

    // ضمان عدم وجود / في النهاية
    $basePath = rtrim($basePath, '/');
    $path     = $basePath . '/' . $index;

    $privBin = derive_child_key($seed, $path);
    $pubBin  = public_key_from_private($privBin);
    $addr    = pub_to_eth_address($pubBin);

    return [
        'index'       => $index,
        'path'        => $path,
        'private_key' => bin2hex($privBin),
        'address'     => $addr,
    ];
}
