#ifndef NOSTR_SECP256K1_H
#define NOSTR_SECP256K1_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stddef.h>

/** Opaque data structure that holds a parsed and valid public key.
 *  Guaranteed to be 64 bytes in size, and can be safely copied/moved.
 */
typedef struct nostr_secp256k1_pubkey {
    unsigned char data[64];
} nostr_secp256k1_pubkey;

/** Opaque data structure that holds a parsed keypair.
 *  Guaranteed to be 96 bytes in size, and can be safely copied/moved.
 */
typedef struct nostr_secp256k1_keypair {
    unsigned char data[96];
} nostr_secp256k1_keypair;

/** Opaque data structure that holds a parsed x-only public key.
 *  Guaranteed to be 64 bytes in size, and can be safely copied/moved.
 */
typedef struct nostr_secp256k1_xonly_pubkey {
    unsigned char data[64];
} nostr_secp256k1_xonly_pubkey;

/** Initialize the secp256k1 library. Must be called before any other functions.
 *  Returns: 1 on success, 0 on failure.
 */
int nostr_secp256k1_context_create(void);

/** Clean up the secp256k1 library resources.
 */
void nostr_secp256k1_context_destroy(void);

/** Verify an elliptic curve secret key.
 *  Returns: 1: secret key is valid, 0: secret key is invalid
 *  In: seckey: pointer to a 32-byte secret key.
 */
int nostr_secp256k1_ec_seckey_verify(const unsigned char *seckey);

/** Compute the public key for a secret key.
 *  Returns: 1: secret was valid, public key stored. 0: secret was invalid.
 *  Out: pubkey: pointer to the created public key.
 *  In:  seckey: pointer to a 32-byte secret key.
 */
int nostr_secp256k1_ec_pubkey_create(nostr_secp256k1_pubkey *pubkey, const unsigned char *seckey);

/** Create a keypair from a secret key.
 *  Returns: 1: keypair created, 0: secret key invalid.
 *  Out: keypair: pointer to the created keypair.
 *  In:  seckey:  pointer to a 32-byte secret key.
 */
int nostr_secp256k1_keypair_create(nostr_secp256k1_keypair *keypair, const unsigned char *seckey);

/** Get the x-only public key from a keypair.
 *  Returns: 1 always.
 *  Out: pubkey: pointer to storage for the x-only public key.
 *  In:  keypair: pointer to a keypair.
 */
int nostr_secp256k1_keypair_xonly_pub(nostr_secp256k1_xonly_pubkey *pubkey, const nostr_secp256k1_keypair *keypair);

/** Parse an x-only public key from bytes.
 *  Returns: 1: public key parsed, 0: invalid public key.
 *  Out: pubkey: pointer to the created x-only public key.
 *  In:  input32: pointer to a 32-byte x-only public key.
 */
int nostr_secp256k1_xonly_pubkey_parse(nostr_secp256k1_xonly_pubkey *pubkey, const unsigned char *input32);

/** Serialize an x-only public key to bytes.
 *  Returns: 1 always.
 *  Out: output32: pointer to a 32-byte array to store the serialized key.
 *  In:  pubkey:   pointer to an x-only public key.
 */
int nostr_secp256k1_xonly_pubkey_serialize(unsigned char *output32, const nostr_secp256k1_xonly_pubkey *pubkey);

/** Create a Schnorr signature.
 *  Returns: 1: signature created, 0: nonce generation failed or secret key invalid.
 *  Out: sig64:     pointer to a 64-byte array where the signature will be placed.
 *  In:  msghash32: the 32-byte message hash being signed.
 *       keypair:   pointer to an initialized keypair.
 *       aux_rand32: pointer to 32 bytes of auxiliary randomness (can be NULL).
 */
int nostr_secp256k1_schnorrsig_sign32(unsigned char *sig64, const unsigned char *msghash32, const nostr_secp256k1_keypair *keypair, const unsigned char *aux_rand32);

/** Verify a Schnorr signature.
 *  Returns: 1: correct signature, 0: incorrect signature
 *  In: sig64:     pointer to the 64-byte signature being verified.
 *      msghash32: the 32-byte message hash being verified.
 *      pubkey:    pointer to an x-only public key to verify with.
 */
int nostr_secp256k1_schnorrsig_verify(const unsigned char *sig64, const unsigned char *msghash32, const nostr_secp256k1_xonly_pubkey *pubkey);

/** Serialize a pubkey object into a serialized byte sequence.
 *  Returns: 1 always.
 *  Out: output:     pointer to a 33-byte array to place the serialized key in.
 *  In:  pubkey:     pointer to a secp256k1_pubkey containing an initialized public key.
 *
 *  The output will be a 33-byte compressed public key (0x02 or 0x03 prefix + 32 bytes x coordinate).
 */
int nostr_secp256k1_ec_pubkey_serialize_compressed(unsigned char *output, const nostr_secp256k1_pubkey *pubkey);

/** Tweak a secret key by adding a 32-byte tweak to it.
 *  Returns: 1: seckey was valid, 0: seckey invalid or resulting key invalid
 *  In/Out: seckey: pointer to a 32-byte secret key. Will be modified in-place.
 *  In:     tweak:  pointer to a 32-byte tweak.
 */
int nostr_secp256k1_ec_seckey_tweak_add(unsigned char *seckey, const unsigned char *tweak);

/** Parse a variable-length public key into the pubkey object.
 *  Returns: 1: public key parsed, 0: invalid public key.
 *  Out: pubkey: pointer to the created public key.
 *  In:  input:  pointer to a serialized public key
 *       inputlen: length of the array pointed to by input
 */
int nostr_secp256k1_ec_pubkey_parse(nostr_secp256k1_pubkey *pubkey, const unsigned char *input, size_t inputlen);

/** Compute an EC Diffie-Hellman secret in constant time.
 *  Returns: 1: exponentiation was successful, 0: scalar was invalid (zero or overflow)
 *  Out: result: a 32-byte array which will be populated by an ECDH secret computed from point and scalar
 *  In:  pubkey: a pointer to a secp256k1_pubkey containing an initialized public key
 *       seckey: a 32-byte scalar with which to multiply the point
 */
int nostr_secp256k1_ecdh(unsigned char *result, const nostr_secp256k1_pubkey *pubkey, const unsigned char *seckey, void *hashfp, void *data);

/** Generate cryptographically secure random bytes.
 *  Returns: 1: success, 0: failure
 *  Out: buf: buffer to fill with random bytes
 *  In:  len: number of bytes to generate
 */
int nostr_secp256k1_get_random_bytes(unsigned char *buf, size_t len);

#ifdef __cplusplus
}
#endif

#endif /* NOSTR_SECP256K1_H */
