// ECDH key exchange for P-256.
// Shared secret = x-coordinate of (private_key * peer_public_key).

/// Compute ECDH shared secret (32 bytes, x-coordinate of the shared point).
pub fn ecdh(
  private_key : Array[UInt],
  peer_public_key : Array[UInt],
) -> Result[Array[UInt], String] {
  if private_key.length() != 32 {
    return Err("Private key must be 32 bytes")
  }

  let d = sc_from_bytes(private_key)
  if !sc_is_valid(d) {
    return Err("Private key out of range")
  }

  let peer = match parse_public_key(peer_public_key) {
    Err(e) => return Err(e)
    Ok(p) => p
  }

  // shared_point = d * peer_public_key
  let shared = point_mul(d, peer)
  let shared_affine = point_to_affine(shared)
  if shared_affine.infinity {
    return Err("ECDH produced identity point")
  }

  // Return x-coordinate as 32 bytes (big-endian)
  Ok(fe_to_bytes(shared_affine.x))
}

/// Compute ECDH shared secret (Bytes API).
pub fn ecdh_bytes(
  private_key : Bytes,
  peer_public_key : Bytes,
) -> Result[Bytes, String] {
  match ecdh(bytes_to_uint_array(private_key), bytes_to_uint_array(peer_public_key)) {
    Err(e) => Err(e)
    Ok(shared) => Ok(uints_to_bytes(shared))
  }
}

/// Derive public key from private key (32 bytes -> 65 bytes uncompressed).
pub fn derive_public_key(private_key : Array[UInt]) -> Result[Array[UInt], String] {
  if private_key.length() != 32 {
    return Err("Private key must be 32 bytes")
  }
  let d = sc_from_bytes(private_key)
  if !sc_is_valid(d) {
    return Err("Private key out of range")
  }
  let q = point_mul(d, point_generator())
  let q_affine = point_to_affine(q)
  if q_affine.infinity {
    return Err("Invalid private key")
  }
  let pub_key : Array[UInt] = []
  pub_key.push(0x04U)
  for v in fe_to_bytes(q_affine.x) {
    pub_key.push(v)
  }
  for v in fe_to_bytes(q_affine.y) {
    pub_key.push(v)
  }
  Ok(pub_key)
}