#include "nostr_secp256k1.h"
#include <secp256k1.h>
#include <secp256k1_schnorrsig.h>
#include <secp256k1_ecdh.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

// Global context for secp256k1 operations
static secp256k1_context* g_ctx = NULL;

int nostr_secp256k1_context_create(void) {
    if (g_ctx != NULL) {
        return 1; // Already initialized
    }
    
    g_ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
    if (g_ctx == NULL) {
        return 0;
    }
    
    // Add some randomization to the context for better security
    unsigned char randomize[32];
    // In a real implementation, you'd want better randomness
    // For now, just use a simple pattern
    for (int i = 0; i < 32; i++) {
        randomize[i] = (unsigned char)(i * 7 + 13);
    }
    
    if (!secp256k1_context_randomize(g_ctx, randomize)) {
        secp256k1_context_destroy(g_ctx);
        g_ctx = NULL;
        return 0;
    }
    
    return 1;
}

void nostr_secp256k1_context_destroy(void) {
    if (g_ctx != NULL) {
        secp256k1_context_destroy(g_ctx);
        g_ctx = NULL;
    }
}

int nostr_secp256k1_ec_seckey_verify(const unsigned char *seckey) {
    if (g_ctx == NULL || seckey == NULL) {
        return 0;
    }
    
    return secp256k1_ec_seckey_verify(g_ctx, seckey);
}

int nostr_secp256k1_ec_pubkey_create(nostr_secp256k1_pubkey *pubkey, const unsigned char *seckey) {
    if (g_ctx == NULL || pubkey == NULL || seckey == NULL) {
        return 0;
    }
    
    secp256k1_pubkey internal_pubkey;
    if (!secp256k1_ec_pubkey_create(g_ctx, &internal_pubkey, seckey)) {
        return 0;
    }
    
    // Copy the internal representation to our wrapper
    memcpy(pubkey->data, &internal_pubkey, sizeof(secp256k1_pubkey));
    
    return 1;
}

int nostr_secp256k1_keypair_create(nostr_secp256k1_keypair *keypair, const unsigned char *seckey) {
    if (g_ctx == NULL || keypair == NULL || seckey == NULL) {
        return 0;
    }
    
    secp256k1_keypair internal_keypair;
    if (!secp256k1_keypair_create(g_ctx, &internal_keypair, seckey)) {
        return 0;
    }
    
    // Copy the internal representation to our wrapper
    memcpy(keypair->data, &internal_keypair, sizeof(secp256k1_keypair));
    
    return 1;
}

int nostr_secp256k1_keypair_xonly_pub(nostr_secp256k1_xonly_pubkey *pubkey, const nostr_secp256k1_keypair *keypair) {
    if (g_ctx == NULL || pubkey == NULL || keypair == NULL) {
        return 0;
    }
    
    secp256k1_keypair internal_keypair;
    secp256k1_xonly_pubkey internal_xonly;
    
    // Copy from our wrapper to internal representation
    memcpy(&internal_keypair, keypair->data, sizeof(secp256k1_keypair));
    
    if (!secp256k1_keypair_xonly_pub(g_ctx, &internal_xonly, NULL, &internal_keypair)) {
        return 0;
    }
    
    // Copy the internal representation to our wrapper
    memcpy(pubkey->data, &internal_xonly, sizeof(secp256k1_xonly_pubkey));
    
    return 1;
}

int nostr_secp256k1_xonly_pubkey_parse(nostr_secp256k1_xonly_pubkey *pubkey, const unsigned char *input32) {
    if (g_ctx == NULL || pubkey == NULL || input32 == NULL) {
        return 0;
    }
    
    secp256k1_xonly_pubkey internal_xonly;
    if (!secp256k1_xonly_pubkey_parse(g_ctx, &internal_xonly, input32)) {
        return 0;
    }
    
    // Copy the internal representation to our wrapper
    memcpy(pubkey->data, &internal_xonly, sizeof(secp256k1_xonly_pubkey));
    
    return 1;
}

int nostr_secp256k1_xonly_pubkey_serialize(unsigned char *output32, const nostr_secp256k1_xonly_pubkey *pubkey) {
    if (g_ctx == NULL || output32 == NULL || pubkey == NULL) {
        return 0;
    }
    
    secp256k1_xonly_pubkey internal_xonly;
    
    // Copy from our wrapper to internal representation
    memcpy(&internal_xonly, pubkey->data, sizeof(secp256k1_xonly_pubkey));
    
    return secp256k1_xonly_pubkey_serialize(g_ctx, output32, &internal_xonly);
}

int nostr_secp256k1_schnorrsig_sign32(unsigned char *sig64, const unsigned char *msghash32, const nostr_secp256k1_keypair *keypair, const unsigned char *aux_rand32) {
    if (g_ctx == NULL || sig64 == NULL || msghash32 == NULL || keypair == NULL) {
        return 0;
    }
    
    secp256k1_keypair internal_keypair;
    
    // Copy from our wrapper to internal representation
    memcpy(&internal_keypair, keypair->data, sizeof(secp256k1_keypair));
    
    return secp256k1_schnorrsig_sign32(g_ctx, sig64, msghash32, &internal_keypair, aux_rand32);
}

int nostr_secp256k1_schnorrsig_verify(const unsigned char *sig64, const unsigned char *msghash32, const nostr_secp256k1_xonly_pubkey *pubkey) {
    if (g_ctx == NULL || sig64 == NULL || msghash32 == NULL || pubkey == NULL) {
        return 0;
    }
    
    secp256k1_xonly_pubkey internal_xonly;
    
    // Copy from our wrapper to internal representation
    memcpy(&internal_xonly, pubkey->data, sizeof(secp256k1_xonly_pubkey));
    
    return secp256k1_schnorrsig_verify(g_ctx, sig64, msghash32, 32, &internal_xonly);
}

int nostr_secp256k1_ec_pubkey_serialize_compressed(unsigned char *output, const nostr_secp256k1_pubkey *pubkey) {
    if (g_ctx == NULL || output == NULL || pubkey == NULL) {
        return 0;
    }
    
    secp256k1_pubkey internal_pubkey;
    size_t outputlen = 33;
    
    // Copy from our wrapper to internal representation
    memcpy(&internal_pubkey, pubkey->data, sizeof(secp256k1_pubkey));
    
    return secp256k1_ec_pubkey_serialize(g_ctx, output, &outputlen, &internal_pubkey, SECP256K1_EC_COMPRESSED);
}

int nostr_secp256k1_ec_seckey_tweak_add(unsigned char *seckey, const unsigned char *tweak) {
    if (g_ctx == NULL || seckey == NULL || tweak == NULL) {
        return 0;
    }
    
    return secp256k1_ec_seckey_tweak_add(g_ctx, seckey, tweak);
}

int nostr_secp256k1_ec_pubkey_parse(nostr_secp256k1_pubkey *pubkey, const unsigned char *input, size_t inputlen) {
    if (g_ctx == NULL || pubkey == NULL || input == NULL) {
        return 0;
    }
    
    secp256k1_pubkey internal_pubkey;
    if (!secp256k1_ec_pubkey_parse(g_ctx, &internal_pubkey, input, inputlen)) {
        return 0;
    }
    
    // Copy the internal representation to our wrapper
    memcpy(pubkey->data, &internal_pubkey, sizeof(secp256k1_pubkey));
    
    return 1;
}

int nostr_secp256k1_ecdh(unsigned char *result, const nostr_secp256k1_pubkey *pubkey, const unsigned char *seckey, void *hashfp, void *data) {
    if (g_ctx == NULL || result == NULL || pubkey == NULL || seckey == NULL) {
        return 0;
    }
    
    secp256k1_pubkey internal_pubkey;
    
    // Copy from our wrapper to internal representation
    memcpy(&internal_pubkey, pubkey->data, sizeof(secp256k1_pubkey));
    
    return secp256k1_ecdh(g_ctx, result, &internal_pubkey, seckey, hashfp, data);
}

int nostr_secp256k1_get_random_bytes(unsigned char *buf, size_t len) {
    if (buf == NULL || len == 0) {
        return 0;
    }
    
    // Try to use /dev/urandom for good randomness
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd >= 0) {
        ssize_t result = read(fd, buf, len);
        close(fd);
        if (result == (ssize_t)len) {
            return 1;
        }
    }
    
    // Fallback to a simple PRNG (not cryptographically secure, but better than nothing)
    // In a real implementation, you'd want to use a proper CSPRNG
    static unsigned long seed = 1;
    for (size_t i = 0; i < len; i++) {
        seed = seed * 1103515245 + 12345;
        buf[i] = (unsigned char)(seed >> 16);
    }
    
    return 1;
}
