#pragma once
#include <vector>
#include <cstdint>
#include <cstring>
#include "ecc_u256.h"   // Must provide u256 type and fp_* arithmetic

namespace netplus {

static const u256 P256_N = {{
    0xFC632551, // w[0] LSW
    0xF3B9CAC2,
    0xA7179E84,
    0xBCE6FAAD,
    0xFFFFFFFF,
    0xFFFFFFFF,
    0x00000000,
    0xFFFFFFFF  // w[7] MSW
}};

static const uint8_t P256_GX_BE[32] = {
  0x6b,0x17,0xd1,0xf2,0xe1,0x2c,0x42,0x47,
  0xf8,0xbc,0xe6,0xe5,0x63,0xa4,0x40,0xf2,
  0x77,0x03,0x7d,0x81,0x2d,0xeb,0x33,0xa0,
  0xf4,0xa1,0x39,0x45,0xd8,0x98,0xc2,0x96
};

static const uint8_t P256_GY_BE[32] = {
  0x4f,0xe3,0x42,0xe2,0xfe,0x1a,0x7f,0x9b,
  0x8e,0xe7,0xeb,0x4a,0x7c,0x0f,0x9e,0x16,
  0x2b,0xce,0x33,0x57,0x6b,0x31,0x5e,0xce,
  0xcb,0xb6,0x40,0x68,0x37,0xbf,0x51,0xf5
};

/**
 * @brief Affine point on the NIST P-256 curve.
 * Equation: y^2 = x^3 - 3x + b (mod p)
 * Internal layout: x.w[0] is the Least Significant Word (Little-Endian math).
 */
struct P256Point {
    u256 x;
    u256 y;
    u256 z;      // only valid if mont=true (Jacobian)
    bool inf = true;
    bool mont = false;
};

// --- NIST P-256 Curve Constants ---

/**
 * @brief Returns a reference to the NIST P-256 Base Point G.
 */
const P256Point& P256_G();

/**
 * @brief The 'b' coefficient of the NIST P-256 curve equation.
 */
extern const u256 P256_B;

// --- Point Operations ---

/** * @brief Converts affine coordinates to Jacobian coordinates.
 * Sets Z = 1.
 */
P256Point toJac(const P256Point& P);

/** * @brief Converts Jacobian coordinates back to Affine.
 * Requires one modular inversion.
 */
P256Point  toAff(const P256Point& a);

/** * @brief Point doubling: R = 2 * P.
 * Optimized for the NIST case where a = -3.
 */
P256Point point_double(const P256Point& a);

/** * @brief Full Jacobian point addition: R = P1 + P2.
 */
P256Point point_add_jac(const P256Point& a, const P256Point& b);

/** * @brief Mixed addition: R = P1 (Jacobian) + P2 (Affine).
 * Faster than full Jacobian addition when one point is already affine.
 */
P256Point point_add_mixed(const P256Point& a, const P256Point& b);

// --- Scalar Multiplication ---

/**
 * @brief Computes R = k * P using a Montgomery Ladder.
 * @param priv 32-byte scalar (Big-Endian, as used in TLS/NIST).
 * @param P Affine point on the curve.
 * @return The resulting affine point.
 */
P256Point scalar_mul(const uint8_t priv[32], const P256Point& P);

/** * @brief Computes R = k * G (Base point multiplication).
 */
P256Point scalar_mul_G(const uint8_t priv[32]);

bool is_on_curve(const P256Point& P);

// --- TLS / Interoperability ---

/**
 * @brief Encodes a point into the uncompressed TLS format (0x04 || X || Y).
 * @return A 65-byte vector in Big-Endian format.
 */
std::vector<uint8_t> encode_tls_point(const P256Point& P);

/**
 * @brief Decodes an uncompressed point and validates that it lies on the curve.
 * Mandatory check for NIST compliance to prevent Invalid Curve attacks.
 * @param p Pointer to input data (starting with 0x04).
 * @param n Length of input (must be 65).
 */
bool decode_tls_point(P256Point& out, const uint8_t* p, size_t n);

/**
 * @brief ECDH Shared Secret calculation.
 * Extracts the X-coordinate of the resulting point (k * Peer_Public).
 * @param out32 Target buffer for the secret (32 bytes, Big-Endian).
 * @param priv Local private key.
 * @param peer Remote peer's public key.
 * @return true on success, false if the resulting point is at infinity.
 */
bool ecdh_shared_secret(uint8_t out32[32], const uint8_t priv[32], const P256Point& peer);

} // namespace netplus
