#include <iostream>
#include <iomanip>
#include <cstring>
#include <cstdint>

#include "crypto/ecc_p256.h"
#include "crypto/ecc_u256.h"

#include "../src/random.h"

using namespace netplus;

// ------------------------------------------------------------
// Dump helpers
// ------------------------------------------------------------
static void dump_hex(const char* name, const uint8_t* p, size_t n) {
    std::cout << name << " (" << std::dec << n << " bytes): ";
    for (size_t i = 0; i < n; i++)
        std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)p[i];
    std::cout << std::dec << "\n";
}

static void dump_u256_be(const char* name, const u256& a) {
    uint8_t b[32];
    u256_to_be(b, a);
    dump_hex(name, b, 32);
}

static void dump_u256_words(const char* name, const u256& a) {
    std::cout << name << " (LE words w0..w7): ";
    for(int i=0;i<8;i++){
        std::cout << std::hex << std::setw(8) << std::setfill('0') << a.w[i];
        if(i!=7) std::cout << " ";
    }
    std::cout << std::dec << "\n";
}

static void dump_u512_words(const char* name, const u512& x) {
    std::cout << name << " (w15..w0): ";
    for(int i=15;i>=0;--i){
        std::cout << std::hex << std::setw(8) << std::setfill('0') << x.w[i];
        if(i!=0) std::cout << " ";
    }
    std::cout << std::dec << "\n";
}

static void dump_point(const char* tag, const netplus::P256Point& P){
    uint8_t xb[32], yb[32];
    netplus::u256_to_be(xb, P.x);
    netplus::u256_to_be(yb, P.y);
    std::cout << tag << " x=";
    for(int i=0;i<32;i++) printf("%02x", xb[i]);
    std::cout << "\n" << tag << " y=";
    for(int i=0;i<32;i++) printf("%02x", yb[i]);
    std::cout << "\n";
}

[[maybe_unused]] static netplus::P256Point to_mont_point(const netplus::P256Point& Pstd)
{
    netplus::P256Point P = Pstd;
    if (!P.inf) {
        P.x = netplus::to_mont(P.x);
        P.y = netplus::to_mont(P.y);
    }
    return P;
}

// ------------------------------------------------------------
// Generate a valid P-256 scalar: 1 <= k < n
// ------------------------------------------------------------
static void gen_valid_scalar(uint8_t out_priv[32])
{
    for (;;) {
        netplus::fillRandomBytes(out_priv, 32);

        // reject 0
        bool allZero = true;
        for (int i=0;i<32;i++) if (out_priv[i]) { allZero=false; break; }
        if (allZero) continue;

        u256 k = bytes_be_to_u256(out_priv);

        // reject >= n
        if (u256_cmp(k, P256_N) >= 0) continue;

        return;
    }
}

// ------------------------------------------------------------
// ECDH Symmetry Test: ECDH(a, bG) == ECDH(b, aG)
// ------------------------------------------------------------
static bool test_ecdh_symmetry()
{
    std::cout << "\n=== ECDH Symmetry Test (P-256) ===\n";

    uint8_t a_priv[32], b_priv[32];
    gen_valid_scalar(a_priv);
    gen_valid_scalar(b_priv);

    dump_hex("a_priv", a_priv, 32);
    dump_hex("b_priv", b_priv, 32);

    // Compute public keys
    P256Point A = scalar_mul_G(a_priv);
    P256Point B = scalar_mul_G(b_priv);

    if (A.inf || B.inf) {
        std::cout << "❌ FAIL: pubkey infinity\n";
        return false;
    }

    std::vector<uint8_t> Aenc = encode_tls_point(A);
    std::vector<uint8_t> Benc = encode_tls_point(B);
    dump_hex("A(pub)", Aenc.data(), Aenc.size());
    dump_hex("B(pub)", Benc.data(), Benc.size());

    // ECDH
    uint8_t s1[32], s2[32];
    bool ok1 = ecdh_shared_secret(s1, a_priv, B);
    bool ok2 = ecdh_shared_secret(s2, b_priv, A);

    if (!ok1 || !ok2) {
        std::cout << "❌ FAIL: ecdh_shared_secret returned false (ok1=" << ok1 << " ok2=" << ok2 << ")\n";
        return false;
    }

    dump_hex("ECDH(a,B)", s1, 32);
    dump_hex("ECDH(b,A)", s2, 32);

    if (std::memcmp(s1, s2, 32) != 0) {
        std::cout << "❌ FAIL: symmetry mismatch\n";
        return false;
    }

    std::cout << "✅ PASS ECDH symmetry\n";
    return true;
}

// ------------------------------------------------------------
// Correct: 64 bytes Big Endian -> u512 with w[0]=LSW
// ------------------------------------------------------------
static u512 u512_from_be64(const uint8_t be[64]) {
    u512 r;
    for(int i=0;i<16;i++){
        int o = 4*i;
        uint32_t w =
            ((uint32_t)be[o+0] << 24) |
            ((uint32_t)be[o+1] << 16) |
            ((uint32_t)be[o+2] <<  8) |
            ((uint32_t)be[o+3] <<  0);

        r.w[15 - i] = w; // store LE
    }
    return r;
}

// ------------------------------------------------------------
// Pattern sanity: 00..3F must map to w15..w0 = 00010203 ... 3C3D3E3F
// ------------------------------------------------------------
static bool test_u512_loader() {
    std::cout << "\n=== u512_from_be64() sanity test (00..3F pattern) ===\n";

    uint8_t pat[64];
    for(int i=0;i<64;i++) pat[i]=(uint8_t)i;

    u512 x = u512_from_be64(pat);
    dump_u512_words("PATTERN", x);

    uint32_t exp_w15_to_w0[16] = {
        0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f,
        0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f,
        0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f,
        0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f
    };

    bool ok=true;
    for(int i=0;i<16;i++){
        uint32_t want = exp_w15_to_w0[i];
        uint32_t got  = x.w[15 - i];
        if(got != want) ok=false;
    }

    std::cout << (ok ? "✅ PASS\n" : "❌ FAIL\n");
    return ok;
}

// ------------------------------------------------------------
// Prime check
// ------------------------------------------------------------
static void verify_prime_constant() {
    std::cout << "\n--- Prime Constant Check ---\n";
    dump_u256_be("P256_P", P256_P);
}

// ------------------------------------------------------------
// Montgomery field ops test
// ------------------------------------------------------------
static bool test_field_ops_mont() {
    std::cout << "\n=== Field Operations Test (Montgomery) ===\n";
    bool ok = true;

    u256 two_std   = {{2,0,0,0,0,0,0,0}};
    u256 three_std = {{3,0,0,0,0,0,0,0}};
    u256 one_std   = {{1,0,0,0,0,0,0,0}};

    u256 prod_std = from_mont(fp_mul(to_mont(two_std), to_mont(three_std)));
    if(prod_std.w[0] != 6) {
        ok=false;
        std::cout << "FAIL: 2*3 != 6\n";
        dump_u256_be("Got", prod_std);
    } else std::cout << "PASS: 2*3 == 6\n";

    uint32_t br=0;
    u256 pm1_std = u256_sub_raw(P256_P, one_std, br);

    u256 sum_std = from_mont(fp_add(to_mont(pm1_std), to_mont(one_std)));
    if(!u256_is_zero(sum_std)) {
        ok=false;
        std::cout << "FAIL: (P-1)+1 != 0\n";
        dump_u256_be("Got", sum_std);
    } else std::cout << "PASS: (P-1)+1 == 0\n";

    u256 sq_std = from_mont(fp_mul(to_mont(pm1_std), to_mont(pm1_std)));
    if(u256_cmp(sq_std, one_std)!=0) {
        ok=false;
        std::cout << "FAIL: (P-1)^2 != 1\n";
        dump_u256_be("sq", sq_std);
        dump_u256_words("sq", sq_std);
    } else std::cout << "PASS: (P-1)^2 == 1\n";

    return ok;
}

// ------------------------------------------------------------
// FIPS-ish test vector: input = P||payload  => result = payload
// ------------------------------------------------------------
static bool test_fips_vector() {
    std::cout << "\n=== NIST P-256 Reduction Test (P||payload) ===\n";

    uint8_t be[64] = {
        // MS 256 bits = P
        0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x01,
        0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
        0x00,0x00,0x00,0x00, 0xff,0xff,0xff,0xff,
        0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,
        // LS 256 bits = payload
        0x11,0x22,0x33,0x44, 0x55,0x66,0x77,0x88,
        0x99,0xaa,0xbb,0xcc, 0xdd,0xee,0xff,0x00,
        0x01,0x02,0x03,0x04, 0x05,0x06,0x07,0x08,
        0x09,0x0a,0x0b,0x0c, 0x0d,0x0e,0x0f,0x10
    };

    u512 input = u512_from_be64(be);
    dump_u512_words("Input", input);

    // since input = P*2^256 + payload, input mod P = payload
    u256 expected = {{
        0x0d0e0f10, 0x090a0b0c, 0x05060708, 0x01020304,
        0xddeeff00, 0x99aabbcc, 0x55667788, 0x11223344
    }};

    u256 got = mod_p256(input);

    if(u256_cmp(got, expected) != 0) {
        std::cout << "❌ FAIL\n";
        dump_u256_be("Got", got);
        dump_u256_be("Expected", expected);
        dump_u256_words("Got", got);
        dump_u256_words("Expected", expected);
        return false;
    }

    std::cout << "✅ PASS\n";
    return true;
}

// ------------------------------------------------------------
// NIST vector: x = 2^512-1
// ------------------------------------------------------------
// ------------------------------------------------------------
// NIST vector: x = 2^512-1
// ------------------------------------------------------------
static bool test_nist_vector() {
    std::cout << "\n=== NIST P-256 Reduction Vector Test (All F's) ===\n";

    u512 input;
    for(int i = 0; i < 16; i++) input.w[i] = 0xFFFFFFFFu;

    // ✅ Correct expected:
    // (2^512 - 1) mod p =
    // 00000004fffffffdfffffffffffffffefffffffbffffffff0000000000000002
    u256 expected = {{
        0x00000002,  // ✅ FIXED (LSW)
        0x00000000,
        0xFFFFFFFF,
        0xFFFFFFFB,
        0xFFFFFFFE,
        0xFFFFFFFF,
        0xFFFFFFFD,
        0x00000004   // MSW
    }};

    u256 got = mod_p256(input);

    if(u256_cmp(got, expected) != 0) {
        std::cout << "❌ FAIL\n";
        dump_u256_be("Got", got);
        dump_u256_be("Expected", expected);
        dump_u256_words("Got", got);
        dump_u256_words("Expected", expected);
        return false;
    }

    std::cout << "✅ PASS\n";
    return true;
}

static const uint8_t VEC_A_IN[64] = {
  0x11,0x22,0x33,0x44,0x38,0x47,0x56,0x21,
  0x19,0x28,0x37,0x46,0x48,0x29,0x30,0x21,
  0x39,0x53,0x79,0x01,0x18,0x73,0x13,0x2e,
  0x45,0x25,0x38,0x11,0x1f,0x49,0x67,0x3b,
  0x93,0x30,0x10,0xad,0x48,0x11,0xa2,0xf9,
  0xad,0x52,0x07,0x7e,0x7c,0x26,0x37,0x99,
  0x22,0x80,0x2d,0x3f,0x11,0x1c,0x0a,0xd1,
  0x36,0x7f,0x8a,0x7a,0x4b,0xc4,0x21,0xc4
};

static const uint8_t VEC_A_OUT[32] = {
  0x32,0xc8,0x1e,0x1e,0xc7,0xe5,0xa3,0xdf,
  0x0f,0x8c,0xc9,0x62,0x19,0x78,0x11,0x49,
  0xc9,0x10,0xe4,0x39,0x00,0x50,0xd6,0x54,
  0xe9,0x5c,0xe4,0xec,0xdd,0x46,0x8a,0x87
};

static const uint8_t VEC_B_IN[64] = {
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
};

static const uint8_t VEC_B_OUT[32] = {
  0x00,0x00,0x00,0x04,0xff,0xff,0xff,0xfd,
  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,
  0xff,0xff,0xff,0xfb,0xff,0xff,0xff,0xff,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02
};

static const uint8_t VEC_C_OUT[32] = {
  0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,
  0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x00,
  0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
  0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10
};




static bool run_vec(const char* name, const uint8_t in64[64], const uint8_t out32[32]) {
    u512 x = u512_from_be64(in64);
    u256 got = mod_p256(x);

    uint8_t got_be[32];
    u256_to_be(got_be, got);

    if (std::memcmp(got_be, out32, 32) != 0) {
        std::cout << "❌ FAIL " << name << "\n";
        dump_hex("Got", got_be, 32);
        dump_hex("Expected", out32, 32);
        return false;
    }

    std::cout << "✅ PASS " << name << "\n";
    return true;
}

static bool selftest_modp() {
    int fails = 0;

    fails += !run_vec("VEC_A_FIPS", VEC_A_IN, VEC_A_OUT);
    fails += !run_vec("VEC_B_ALLF", VEC_B_IN, VEC_B_OUT);

    if (fails != 0){
        std::cout << "❌ MODP FAILS: " << fails << "\n";
        return false;
    }
    std::cout << "✅ ALL MODP TESTS PASSED\n";
    return true;
}

static bool test_kp_plus_x()
{
    u256 x = {{0xdeadbeef,0x11111111,0x22222222,0x33333333,
              0x44444444,0x55555555,0x66666666,0x77777777}};

    // build u512 input = p*2^256 + x  (k=2^256)
    u512 in;
    for(int i=0;i<8;i++) in.w[i] = x.w[i];
    for(int i=0;i<8;i++) in.w[i+8] = P256_P.w[i];

    u256 got = mod_p256(in);
    bool test = u256_cmp(got, x) == 0 ? true : false;

    if(!test)
        std::cout << "❌ test_kp_plus_x FAILED"<< "\n";

    return test;
}

static bool test_point_roundtrip()
{
    std::cout << "\n=== Point Encode/Decode Roundtrip ===\n";

    uint8_t priv[32];
    gen_valid_scalar(priv);

    P256Point P = scalar_mul_G(priv);
    std::vector<uint8_t> enc = encode_tls_point(P);

    P256Point Q;
    if(!decode_tls_point(Q, enc.data(), enc.size())){
        std::cout << "❌ FAIL decode\n";
        return false;
    }

    std::vector<uint8_t> enc2 = encode_tls_point(Q);

    if(enc != enc2){
        std::cout << "❌ FAIL roundtrip mismatch\n";
        dump_hex("enc", enc.data(), enc.size());
        dump_hex("enc2", enc2.data(), enc2.size());
        return false;
    }

    std::cout << "✅ PASS point roundtrip\n";
    return true;
}

static bool test_pubkey_on_curve()
{
    std::cout << "\n=== Public Key On-Curve Test ===\n";

    uint8_t priv[32];
    gen_valid_scalar(priv);

    P256Point Q = scalar_mul_G(priv);

    if (Q.inf) {
        std::cout << "❌ FAIL: scalar_mul_G produced infinity\n";
        return false;
    }

    if (!is_on_curve(Q)) {   // falls du sowas hast – sonst kann ich dir geben
        std::cout << "❌ FAIL: pubkey not on curve\n";
        return false;
    }

    std::cout << "✅ PASS: pubkey is on curve\n";
    return true;
}

// ============================================================
// ECC P-256 full test suite for ecc.cpp
// ============================================================

static bool point_eq(const netplus::P256Point& A, const netplus::P256Point& B)
{
    if (A.inf != B.inf) return false;
    if (A.inf) return true;
    return netplus::u256_cmp(A.x, B.x) == 0 && netplus::u256_cmp(A.y, B.y) == 0;
}

// ✅ helper: BE bytes -> u256 (needs your bytes_be_to_u256)
static netplus::u256 u256_from_be32(const uint8_t be[32])
{
    return netplus::bytes_be_to_u256(be);
}

static bool priv_is_valid(const uint8_t priv[32])
{
    netplus::u256 x = u256_from_be32(priv);
    if (netplus::u256_is_zero(x)) return false;
    if (netplus::u256_cmp(x, P256_N) >= 0) return false;
    return true;
}

// ✅ generate priv < n
static void gen_priv_lt_n(uint8_t priv[32])
{
    do {
        netplus::fillRandomBytes(priv, 32);
    } while (!priv_is_valid(priv));
}

// ============================================================
// MAIN ECC TEST SUITE
// ============================================================

static bool test_ecc_suite()
{
    int fails = 0;

    std::cout << "\n==============================\n";
    std::cout << "=== ECC CORE TEST SUITE (P-256) ===\n";
    std::cout << "==============================\n";

    // Basepoint in STANDARD domain
    netplus::P256Point G = netplus::P256_G();
    G.inf = false;

    // ------------------------------------------------------------
    // 1) Base point G on curve (STD + MONT check)
    // ------------------------------------------------------------
    std::cout << "\n--- Test 1: is_on_curve(G) STD + MONT ---\n";

    if (!is_on_curve(G)) {
        std::cout << "❌ FAIL: G not on curve (STD check)\n";
        dump_point("G", G);
        fails++;
    } else {
        std::cout << "✅ PASS: STD curve check\n";
    }

    if (!is_on_curve(G)) {
        std::cout << "❌ FAIL: G not on curve (MONT check)\n";
        dump_point("G", G);
        fails++;
    } else {
        std::cout << "✅ PASS: MONT curve check\n";
    }

    // ------------------------------------------------------------
    // 2) scalar_mul_G(0) == INF
    // ------------------------------------------------------------
    std::cout << "\n--- Test 2: scalar_mul_G(0) == INF ---\n";
    uint8_t zero[32] = {0};

    auto P0 = netplus::scalar_mul_G(zero);
    if (!P0.inf) {
        std::cout << "❌ FAIL: scalar_mul_G(0) not infinity\n";
        dump_point("mul(0)", P0);
        fails++;
    } else {
        std::cout << "✅ PASS\n";
    }

    // ------------------------------------------------------------
    // 3) scalar_mul_G(1) == G
    // ------------------------------------------------------------
    std::cout << "\n--- Test 3: scalar_mul_G(1) == G ---\n";
    uint8_t one[32] = {0};
    one[31] = 1;

    auto P1 = netplus::scalar_mul_G(one);
    if (!point_eq(P1, G)) {
        std::cout << "❌ FAIL: scalar_mul_G(1) != G\n";
        dump_point("mul(1)", P1);
        dump_point("G", G);
        fails++;
    } else {
        std::cout << "✅ PASS\n";
    }

    // ------------------------------------------------------------
    // 4) scalar_mul_G(2) should equal point_double(G)
    // ------------------------------------------------------------
    std::cout << "\n--- Test 4: scalar_mul_G(2) == 2G ---\n";
    uint8_t two[32] = {0};
    two[31] = 2;

    auto P2 = netplus::scalar_mul_G(two);

    // reference 2G using Jacobian double
    netplus::P256Point Gj = netplus::toJac(G);        // toJac expects STD
    netplus::P256Point G2j = netplus::point_double(Gj);
    netplus::P256Point G2ref = netplus::toAff(G2j);

    if (!point_eq(P2, G2ref)) {
        std::cout << "❌ FAIL: scalar_mul_G(2) mismatch\n";
        dump_point("mul(2)", P2);
        dump_point("ref(2G)", G2ref);
        fails++;
    } else {
        std::cout << "✅ PASS\n";
    }

    // ------------------------------------------------------------
    // 5) scalar_mul(priv, G) == scalar_mul_G(priv)
    // ------------------------------------------------------------
    std::cout << "\n--- Test 5: scalar_mul(priv,G) == scalar_mul_G(priv) ---\n";
    uint8_t priv[32];
    gen_priv_lt_n(priv);

    auto A1 = netplus::scalar_mul_G(priv);
    auto A2 = netplus::scalar_mul(priv, G); // scalar_mul expects STD point

    if (!point_eq(A1, A2)) {
        std::cout << "❌ FAIL: scalar_mul mismatch\n";
        dump_hex("priv", priv, 32);
        dump_point("A1", A1);
        dump_point("A2", A2);
        fails++;
    } else {
        std::cout << "✅ PASS\n";
    }

    // ------------------------------------------------------------
    // 6) Public key must lie on curve (STD + MONT)
    // ------------------------------------------------------------
    std::cout << "\n--- Test 6: pubkey on curve ---\n";
    if (!is_on_curve(A1)) {
        std::cout << "❌ FAIL: pubkey not on curve (STD)\n";
        dump_point("pub", A1);
        fails++;
    } else {
        std::cout << "✅ PASS STD\n";
    }

    // ------------------------------------------------------------
    // 7) ECDH symmetry test
    // ------------------------------------------------------------
    std::cout << "\n--- Test 7: ECDH symmetry ---\n";

    uint8_t a_priv[32], b_priv[32];
    gen_priv_lt_n(a_priv);
    gen_priv_lt_n(b_priv);

    auto A = netplus::scalar_mul_G(a_priv);
    auto B = netplus::scalar_mul_G(b_priv);

    uint8_t s1[32], s2[32];

    bool ok1 = netplus::ecdh_shared_secret(s1, a_priv, B);
    bool ok2 = netplus::ecdh_shared_secret(s2, b_priv, A);

    if (!ok1 || !ok2) {
        std::cout << "❌ FAIL: ECDH failed ok1=" << ok1 << " ok2=" << ok2 << "\n";
        fails++;
    } else if (std::memcmp(s1, s2, 32) != 0) {
        std::cout << "❌ FAIL: ECDH symmetry mismatch\n";
        dump_hex("a_priv", a_priv, 32);
        dump_hex("b_priv", b_priv, 32);
        dump_point("A(pub)", A);
        dump_point("B(pub)", B);
        dump_hex("ECDH(a,B)", s1, 32);
        dump_hex("ECDH(b,A)", s2, 32);
        fails++;
    } else {
        std::cout << "✅ PASS\n";
    }

    std::cout << "\n==============================\n";
    std::cout << "ECC SUITE RESULT: ";
    if (fails == 0) {
        std::cout << "✅ ALL PASS\n";
        return true;
    }
    std::cout << "❌ FAILS=" << fails << "\n";
    return false;
}

static bool test_fp_mul_vs_modp()
{
    std::cout << "\n--- Test: fp_mul vs (u256_mul_raw + mod_p256) ---\n";

    auto check = [&](const u256& a_std, const u256& b_std, const char* name){
        // expected = (a*b) mod p   via non-mont path
        u512 prod = netplus::u256_mul_raw(a_std, b_std);
        u256 exp  = netplus::mod_p256(prod);

        // got = from_mont( to_mont(a) * to_mont(b) )
        u256 am = netplus::to_mont(a_std);
        u256 bm = netplus::to_mont(b_std);
        u256 gm = netplus::fp_mul(am, bm);
        u256 got = netplus::from_mont(gm);

        if(netplus::u256_cmp(got, exp) != 0){
            std::cout << "❌ FAIL " << name << "\n";
            dump_u256_be("a", a_std);
            dump_u256_be("b", b_std);
            dump_u256_be("got", got);
            dump_u256_be("exp", exp);
            return false;
        }
        std::cout << "✅ PASS " << name << "\n";
        return true;
    };

    u256 one = {{1,0,0,0,0,0,0,0}};
    u256 two = {{2,0,0,0,0,0,0,0}};

    // (P-1)
    uint32_t br=0;
    u256 pm1 = netplus::u256_sub_raw(netplus::P256_P, one, br);

    // random-ish big numbers (handmade)
    u256 a = {{0xdeadbeef,0x11111111,0x22222222,0x33333333,0x44444444,0x55555555,0x66666666,0x77777777}};
    u256 b = {{0xabcdef01,0x01020304,0x0a0b0c0d,0x99aabbcc,0x13579bdf,0x2468ace0,0xf0f0f0f0,0x80000001}};

    int fails = 0;
    fails += !check(two, two, "2*2");
    fails += !check(pm1, pm1, "(P-1)^2");
    fails += !check(a, b, "a*b");
    fails += !check(netplus::bytes_be_to_u256(P256_GX_BE), netplus::bytes_be_to_u256(P256_GY_BE), "Gx*Gy");

    return fails == 0;
}

static bool test_mont_roundtrip_big(const u256& x)
{
    u256 xm = to_mont(x);
    u256 back = from_mont(xm);

    if(u256_cmp(x, back)!=0){
        std::cout<<"❌ mont roundtrip FAIL\n";
        dump_u256_be("x", x);
        dump_u256_be("back", back);
        return false;
    }
    return true;
}

[[maybe_unused]] static netplus::P256Point scalar_mul_ref_std(const uint8_t k[32], const netplus::P256Point& Pstd)
{
    using namespace netplus;

    // R = INF
    P256Point R;
    R.inf = true;
    u256 z; u256_zero(z);
    R.x = to_mont(z);
    R.y = to_mont(z);
    R.z = to_mont(z); // Z=0 => INF

    // Q = P
    P256Point Q = toJac(Pstd);   // <- WICHTIG: toJac() muss std->mont + Z=1 machen

    // classic double-and-add, MSB->LSB (passt zu get_bit_be)
    for (int i = 0; i < 256; i++) {
        uint32_t bit = get_bit_be(k, i); // i=0 is MSB

        // R = 2R
        R = point_double(R);

        // if (bit) R = R + Q
        if (bit) {
            R = point_add_jac(R, Q);
        }
    }

    return toAff(R); // <- muss mont->std machen
}

int main() {
    int fails = 0;

    dump_u256_be("P256_B", netplus::P256_B);
    dump_point("G_STD", netplus::P256_G() );
    std::cout << "is_on_curve=" << netplus::is_on_curve(netplus::P256_G()) << "\n";

    if(!test_mont_roundtrip_big(netplus::P256_B)) fails++;

    if(!test_mont_roundtrip_big(netplus::P256_G().x)) fails++;
    if(!test_mont_roundtrip_big(netplus::P256_G().y)) fails++;
    if(!test_fp_mul_vs_modp()) fails++;

    if(!test_ecc_suite()) fails++;
    if(!test_ecdh_symmetry()) fails++;
    if(!test_point_roundtrip()) fails++;
    if(!test_pubkey_on_curve()) fails++;
    if(!test_kp_plus_x()) fails++;
    if(!selftest_modp()) fails++;
    if(!test_u512_loader()) fails++;
    verify_prime_constant();
    if(!test_field_ops_mont()) fails++;
    if(!test_fips_vector()) fails++;
    if(!test_nist_vector()) fails++;

    std::cout << "\n==============================\n";
    std::cout << "TEST SUMMARY\n";
    std::cout << "==============================\n";

    if(fails==0) {
        std::cout << "✅ ALL TESTS PASSED\n";
        return 0;
    }
    std::cout << "❌ FAILURES: " << fails << "\n";
    return 1;
}
