/*
 * NOSTR Core Library - NIP-021: nostr: URI scheme
 */

#include "nip021.h"
#include "nip019.h"  // For existing bech32 functions
#include "utils.h"
#include "nostr_common.h"  // For error codes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "../cjson/cJSON.h"

// Forward declarations for internal parsing functions
static int parse_nprofile_data(const uint8_t* data, size_t data_len, nostr_nprofile_t* nprofile);
static int parse_nevent_data(const uint8_t* data, size_t data_len, nostr_nevent_t* nevent);
static int parse_naddr_data(const uint8_t* data, size_t data_len, nostr_naddr_t* naddr);

// Bech32 constants and functions (copied from nip019.c for internal use)
static const char bech32_charset[] = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
static const int8_t bech32_charset_rev[128] = {
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    15, -1, 10, 17, 21, 20, 26, 30,  7,  5, -1, -1, -1, -1, -1, -1,
    -1, 29, -1, 24, 13, 25,  9,  8, 23, -1, 18, 22, 31, 27, 19, -1,
    1,  0,  3, 16, 11, 28, 12, 14,  6,  4,  2, -1, -1, -1, -1, -1,
    -1, 29, -1, 24, 13, 25,  9,  8, 23, -1, 18, 22, 31, 27, 19, -1,
    1,  0,  3, 16, 11, 28, 12, 14,  6,  4,  2, -1, -1, -1, -1, -1
};

static uint32_t bech32_polymod_step(uint32_t pre) {
    uint8_t b = pre >> 25;
    return ((pre & 0x1FFFFFF) << 5) ^
           (-((b >> 0) & 1) & 0x3b6a57b2UL) ^
           (-((b >> 1) & 1) & 0x26508e6dUL) ^
           (-((b >> 2) & 1) & 0x1ea119faUL) ^
           (-((b >> 3) & 1) & 0x3d4233ddUL) ^
           (-((b >> 4) & 1) & 0x2a1462b3UL);
}

static int convert_bits(uint8_t *out, size_t *outlen, int outbits, const uint8_t *in, size_t inlen, int inbits, int pad) {
    uint32_t val = 0;
    int bits = 0;
    uint32_t maxv = (((uint32_t)1) << outbits) - 1;
    *outlen = 0;
    while (inlen--) {
        val = (val << inbits) | *(in++);
        bits += inbits;
        while (bits >= outbits) {
            bits -= outbits;
            out[(*outlen)++] = (val >> bits) & maxv;
        }
    }
    if (pad) {
        if (bits) {
            out[(*outlen)++] = (val << (outbits - bits)) & maxv;
        }
    } else if (((val << (outbits - bits)) & maxv) || bits >= inbits) {
        return 0;
    }
    return 1;
}

static int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len) {
    uint32_t chk = 1;
    size_t i, hrp_len = strlen(hrp);

    for (i = 0; i < hrp_len; ++i) {
        int ch = hrp[i];
        if (ch < 33 || ch > 126) return 0;
        if (ch >= 'A' && ch <= 'Z') return 0;
        chk = bech32_polymod_step(chk) ^ (ch >> 5);
    }

    chk = bech32_polymod_step(chk);
    for (i = 0; i < hrp_len; ++i) {
        chk = bech32_polymod_step(chk) ^ (hrp[i] & 0x1f);
        *(output++) = hrp[i];
    }

    *(output++) = '1';
    for (i = 0; i < data_len; ++i) {
        if (*data >> 5) return 0;
        chk = bech32_polymod_step(chk) ^ (*data);
        *(output++) = bech32_charset[*(data++)];
    }

    for (i = 0; i < 6; ++i) {
        chk = bech32_polymod_step(chk);
    }

    chk ^= 1;
    for (i = 0; i < 6; ++i) {
        *(output++) = bech32_charset[(chk >> ((5 - i) * 5)) & 0x1f];
    }

    *output = 0;
    return 1;
}

static int bech32_decode(const char* input, const char* hrp, unsigned char* data, size_t* data_len) {
    if (!input || !hrp || !data || !data_len) {
        return 0;
    }

    size_t input_len = strlen(input);
    size_t hrp_len = strlen(hrp);

    if (input_len < hrp_len + 7) return 0;
    if (strncmp(input, hrp, hrp_len) != 0) return 0;
    if (input[hrp_len] != '1') return 0;

    const char* data_part = input + hrp_len + 1;
    size_t data_part_len = input_len - hrp_len - 1;

    uint8_t values[256];
    for (size_t i = 0; i < data_part_len; i++) {
        unsigned char c = (unsigned char)data_part[i];
        if (c >= 128) return 0;
        int8_t val = bech32_charset_rev[c];
        if (val == -1) return 0;
        values[i] = (uint8_t)val;
    }

    if (data_part_len < 6) return 0;

    uint32_t chk = 1;
    for (size_t i = 0; i < hrp_len; i++) {
        chk = bech32_polymod_step(chk) ^ (hrp[i] >> 5);
    }
    chk = bech32_polymod_step(chk);
    for (size_t i = 0; i < hrp_len; i++) {
        chk = bech32_polymod_step(chk) ^ (hrp[i] & 0x1f);
    }
    for (size_t i = 0; i < data_part_len; i++) {
        chk = bech32_polymod_step(chk) ^ values[i];
    }

    if (chk != 1) return 0;

    size_t payload_len = data_part_len - 6;
    size_t decoded_len;
    if (!convert_bits(data, &decoded_len, 8, values, payload_len, 5, 0)) {
        return 0;
    }

    *data_len = decoded_len;
    return 1;
}

// TLV (Type-Length-Value) constants for structured data
#define TLV_SPECIAL 0
#define TLV_RELAY 1
#define TLV_AUTHOR 2
#define TLV_KIND 3
#define TLV_CREATED_AT 4
#define TLV_IDENTIFIER 5

// Forward declarations for internal functions
static int tlv_encode(const uint8_t* data, size_t data_len, uint8_t type, uint8_t** output, size_t* output_len);
static int encode_structured_bech32(const char* hrp, const uint8_t* data, size_t data_len, char* output, size_t output_size);
static int decode_structured_bech32(const char* input, const char* expected_hrp, uint8_t** data, size_t* data_len);

// Utility function to duplicate string array (removed - not used)

// Free string array
static void free_string_array(char** array, int count) {
    if (!array) return;
    for (int i = 0; i < count; i++) {
        free(array[i]);
    }
    free(array);
}

// TLV encoding: Type (1 byte) + Length (1 byte) + Value
static int tlv_encode(const uint8_t* data, size_t data_len, uint8_t type, uint8_t** output, size_t* output_len) {
    if (data_len > 255) return 0; // Length must fit in 1 byte

    *output_len = 2 + data_len;
    *output = malloc(*output_len);
    if (!*output) return 0;

    (*output)[0] = type;
    (*output)[1] = (uint8_t)data_len;
    memcpy(*output + 2, data, data_len);
    return 1;
}

// TLV decoding (removed - not used)

// Encode structured data to bech32
static int encode_structured_bech32(const char* hrp, const uint8_t* data, size_t data_len, char* output, size_t output_size) {
    // For simple cases like note (32 bytes), use the existing key encoding
    if (strcmp(hrp, "note") == 0 && data_len == 32) {
        return nostr_key_to_bech32(data, "note", output);
    }

    uint8_t conv[256];
    size_t conv_len;

    if (!convert_bits(conv, &conv_len, 5, data, data_len, 8, 1)) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    if (!bech32_encode(output, hrp, conv, conv_len)) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    if (strlen(output) >= output_size) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    return NOSTR_SUCCESS;
}

// Decode structured bech32 data
static int decode_structured_bech32(const char* input, const char* expected_hrp, uint8_t** data, size_t* data_len) {
    // bech32_decode already converts from 5-bit to 8-bit internally
    *data = malloc(256);  // Max size
    if (!*data) return NOSTR_ERROR_MEMORY_FAILED;

    if (!bech32_decode(input, expected_hrp, *data, data_len)) {
        free(*data);
        return NOSTR_ERROR_INVALID_INPUT;
    }

    return NOSTR_SUCCESS;
}

// Detect URI type from string
nostr_uri_type_t nostr_detect_uri_type(const char* uri) {
    if (!uri) return NOSTR_URI_INVALID;

    // Check for nostr: prefix
    if (strncmp(uri, "nostr:", 6) != 0) {
        return NOSTR_URI_INVALID;
    }

    const char* bech32_part = uri + 6;

    // Check prefixes
    if (strncmp(bech32_part, "npub1", 5) == 0) return NOSTR_URI_NPUB;
    if (strncmp(bech32_part, "nsec1", 5) == 0) return NOSTR_URI_NSEC;
    if (strncmp(bech32_part, "note1", 5) == 0) return NOSTR_URI_NOTE;
    if (strncmp(bech32_part, "nprofile1", 9) == 0) return NOSTR_URI_NPROFILE;
    if (strncmp(bech32_part, "nevent1", 7) == 0) return NOSTR_URI_NEVENT;
    if (strncmp(bech32_part, "naddr1", 6) == 0) return NOSTR_URI_NADDR;

    return NOSTR_URI_INVALID;
}

// Free URI result resources
void nostr_uri_result_free(nostr_uri_result_t* result) {
    if (!result) return;

    switch (result->type) {
        case NOSTR_URI_NPROFILE:
            free_string_array(result->data.nprofile.relays, result->data.nprofile.relay_count);
            break;
        case NOSTR_URI_NEVENT:
            free_string_array(result->data.nevent.relays, result->data.nevent.relay_count);
            free(result->data.nevent.author);
            free(result->data.nevent.kind);
            free(result->data.nevent.created_at);
            break;
        case NOSTR_URI_NADDR:
            free(result->data.naddr.identifier);
            free_string_array(result->data.naddr.relays, result->data.naddr.relay_count);
            break;
        default:
            break;
    }
}

// Main URI parsing function
int nostr_parse_uri(const char* uri, nostr_uri_result_t* result) {
    if (!uri || !result) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    memset(result, 0, sizeof(nostr_uri_result_t));

    nostr_uri_type_t type = nostr_detect_uri_type(uri);
    if (type == NOSTR_URI_INVALID) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    const char* bech32_part = uri + 6; // Skip "nostr:"
    result->type = type;

    int ret;
    switch (type) {
        case NOSTR_URI_NPUB: {
            ret = nostr_decode_npub(bech32_part, result->data.pubkey);
            break;
        }
        case NOSTR_URI_NSEC: {
            ret = nostr_decode_nsec(bech32_part, result->data.privkey);
            break;
        }
        case NOSTR_URI_NOTE: {
            // Note is similar to npub but with "note" prefix
            uint8_t* decoded;
            size_t decoded_len;
            ret = decode_structured_bech32(bech32_part, "note", &decoded, &decoded_len);
            if (ret == NOSTR_SUCCESS) {
                if (decoded_len == 32) {
                    memcpy(result->data.event_id, decoded, 32);
                } else {
                    ret = NOSTR_ERROR_INVALID_INPUT;
                }
                free(decoded);
            }
            break;
        }
        case NOSTR_URI_NPROFILE: {
            uint8_t* decoded;
            size_t decoded_len;
            ret = decode_structured_bech32(bech32_part, "nprofile", &decoded, &decoded_len);
            if (ret == NOSTR_SUCCESS) {
                ret = parse_nprofile_data(decoded, decoded_len, &result->data.nprofile);
                free(decoded);
            }
            break;
        }
        case NOSTR_URI_NEVENT: {
            uint8_t* decoded;
            size_t decoded_len;
            ret = decode_structured_bech32(bech32_part, "nevent", &decoded, &decoded_len);
            if (ret == NOSTR_SUCCESS) {
                ret = parse_nevent_data(decoded, decoded_len, &result->data.nevent);
                free(decoded);
            }
            break;
        }
        case NOSTR_URI_NADDR: {
            uint8_t* decoded;
            size_t decoded_len;
            ret = decode_structured_bech32(bech32_part, "naddr", &decoded, &decoded_len);
            if (ret == NOSTR_SUCCESS) {
                ret = parse_naddr_data(decoded, decoded_len, &result->data.naddr);
                free(decoded);
            }
            break;
        }
        default:
            ret = NOSTR_ERROR_INVALID_INPUT;
            break;
    }

    if (ret != NOSTR_SUCCESS) {
        nostr_uri_result_free(result);
        memset(result, 0, sizeof(nostr_uri_result_t));
    }

    return ret;
}

// Parse nprofile structured data
static int parse_nprofile_data(const uint8_t* data, size_t data_len, nostr_nprofile_t* nprofile) {
    size_t offset = 0;

    while (offset < data_len) {
        if (offset + 2 > data_len) return NOSTR_ERROR_INVALID_INPUT;

        uint8_t type = data[offset];
        uint8_t length = data[offset + 1];
        offset += 2;

        if (offset + length > data_len) return NOSTR_ERROR_INVALID_INPUT;

        switch (type) {
            case TLV_SPECIAL: // pubkey
                if (length != 32) return NOSTR_ERROR_INVALID_INPUT;
                memcpy(nprofile->pubkey, data + offset, 32);
                break;
            case TLV_RELAY: // relay URL
                {
                    char* relay = malloc(length + 1);
                    if (!relay) return NOSTR_ERROR_MEMORY_FAILED;
                    memcpy(relay, data + offset, length);
                    relay[length] = '\0';

                    char** new_relays = realloc(nprofile->relays, (nprofile->relay_count + 1) * sizeof(char*));
                    if (!new_relays) {
                        free(relay);
                        return NOSTR_ERROR_MEMORY_FAILED;
                    }
                    nprofile->relays = new_relays;
                    nprofile->relays[nprofile->relay_count++] = relay;
                }
                break;
            default:
                // Ignore unknown types
                break;
        }
        offset += length;
    }

    return NOSTR_SUCCESS;
}

// Parse nevent structured data
static int parse_nevent_data(const uint8_t* data, size_t data_len, nostr_nevent_t* nevent) {
    size_t offset = 0;

    while (offset < data_len) {
        if (offset + 2 > data_len) return NOSTR_ERROR_INVALID_INPUT;

        uint8_t type = data[offset];
        uint8_t length = data[offset + 1];
        offset += 2;

        if (offset + length > data_len) return NOSTR_ERROR_INVALID_INPUT;

        switch (type) {
            case TLV_SPECIAL: // event ID
                if (length != 32) return NOSTR_ERROR_INVALID_INPUT;
                memcpy(nevent->event_id, data + offset, 32);
                break;
            case TLV_RELAY: // relay URL
                {
                    char* relay = malloc(length + 1);
                    if (!relay) return NOSTR_ERROR_MEMORY_FAILED;
                    memcpy(relay, data + offset, length);
                    relay[length] = '\0';

                    char** new_relays = realloc(nevent->relays, (nevent->relay_count + 1) * sizeof(char*));
                    if (!new_relays) {
                        free(relay);
                        return NOSTR_ERROR_MEMORY_FAILED;
                    }
                    nevent->relays = new_relays;
                    nevent->relays[nevent->relay_count++] = relay;
                }
                break;
            case TLV_AUTHOR: // author pubkey
                if (length != 32) return NOSTR_ERROR_INVALID_INPUT;
                nevent->author = malloc(32);
                if (!nevent->author) return NOSTR_ERROR_MEMORY_FAILED;
                memcpy(nevent->author, data + offset, 32);
                break;
            case TLV_KIND: // kind
                if (length != 4) return NOSTR_ERROR_INVALID_INPUT;
                nevent->kind = malloc(sizeof(int));
                if (!nevent->kind) return NOSTR_ERROR_MEMORY_FAILED;
                *nevent->kind = (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];
                break;
            case TLV_CREATED_AT: // created_at
                if (length != 8) return NOSTR_ERROR_INVALID_INPUT;
                nevent->created_at = malloc(sizeof(time_t));
                if (!nevent->created_at) return NOSTR_ERROR_MEMORY_FAILED;
                *nevent->created_at = ((time_t)data[offset] << 56) | ((time_t)data[offset+1] << 48) |
                                     ((time_t)data[offset+2] << 40) | ((time_t)data[offset+3] << 32) |
                                     ((time_t)data[offset+4] << 24) | ((time_t)data[offset+5] << 16) |
                                     ((time_t)data[offset+6] << 8) | (time_t)data[offset+7];
                break;
            default:
                // Ignore unknown types
                break;
        }
        offset += length;
    }

    return NOSTR_SUCCESS;
}

// Parse naddr structured data
static int parse_naddr_data(const uint8_t* data, size_t data_len, nostr_naddr_t* naddr) {
    size_t offset = 0;

    while (offset < data_len) {
        if (offset + 2 > data_len) return NOSTR_ERROR_INVALID_INPUT;

        uint8_t type = data[offset];
        uint8_t length = data[offset + 1];
        offset += 2;

        if (offset + length > data_len) return NOSTR_ERROR_INVALID_INPUT;

        switch (type) {
            case TLV_IDENTIFIER: // identifier
                naddr->identifier = malloc(length + 1);
                if (!naddr->identifier) return NOSTR_ERROR_MEMORY_FAILED;
                memcpy(naddr->identifier, data + offset, length);
                naddr->identifier[length] = '\0';
                break;
            case TLV_SPECIAL: // pubkey
                if (length != 32) return NOSTR_ERROR_INVALID_INPUT;
                memcpy(naddr->pubkey, data + offset, 32);
                break;
            case TLV_KIND: // kind
                if (length != 4) return NOSTR_ERROR_INVALID_INPUT;
                naddr->kind = (data[offset] << 24) | (data[offset+1] << 16) | (data[offset+2] << 8) | data[offset+3];
                break;
            case TLV_RELAY: // relay URL
                {
                    char* relay = malloc(length + 1);
                    if (!relay) return NOSTR_ERROR_MEMORY_FAILED;
                    memcpy(relay, data + offset, length);
                    relay[length] = '\0';

                    char** new_relays = realloc(naddr->relays, (naddr->relay_count + 1) * sizeof(char*));
                    if (!new_relays) {
                        free(relay);
                        return NOSTR_ERROR_MEMORY_FAILED;
                    }
                    naddr->relays = new_relays;
                    naddr->relays[naddr->relay_count++] = relay;
                }
                break;
            default:
                // Ignore unknown types
                break;
        }
        offset += length;
    }

    return NOSTR_SUCCESS;
}


// URI construction functions

int nostr_build_uri_npub(const unsigned char* pubkey, char* output, size_t output_size) {
    if (!pubkey || !output || output_size < 70) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    char bech32[100];
    int ret = nostr_key_to_bech32(pubkey, "npub", bech32);
    if (ret != NOSTR_SUCCESS) return ret;

    size_t len = strlen(bech32);
    if (len + 7 >= output_size) return NOSTR_ERROR_INVALID_INPUT;

    strcpy(output, "nostr:");
    strcpy(output + 6, bech32);
    return NOSTR_SUCCESS;
}

int nostr_build_uri_nsec(const unsigned char* privkey, char* output, size_t output_size) {
    if (!privkey || !output || output_size < 70) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    char bech32[100];
    int ret = nostr_key_to_bech32(privkey, "nsec", bech32);
    if (ret != NOSTR_SUCCESS) return ret;

    size_t len = strlen(bech32);
    if (len + 7 >= output_size) return NOSTR_ERROR_INVALID_INPUT;

    strcpy(output, "nostr:");
    strcpy(output + 6, bech32);
    return NOSTR_SUCCESS;
}

// Helper to build URI with prefix
static int build_uri_with_prefix(const char* bech32, char* output, size_t output_size) {
    size_t len = strlen(bech32);
    if (len + 7 >= output_size) return NOSTR_ERROR_INVALID_INPUT;

    strcpy(output, "nostr:");
    strcpy(output + 6, bech32);
    return NOSTR_SUCCESS;
}

int nostr_build_uri_note(const unsigned char* event_id, char* output, size_t output_size) {
    if (!event_id || !output || output_size < 70) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    char bech32[100];
    int ret = encode_structured_bech32("note", event_id, 32, bech32, sizeof(bech32));
    if (ret != NOSTR_SUCCESS) return ret;

    return build_uri_with_prefix(bech32, output, output_size);
}

int nostr_build_uri_nprofile(const unsigned char* pubkey, const char** relays, int relay_count,
                            char* output, size_t output_size) {
    if (!pubkey || !output) return NOSTR_ERROR_INVALID_INPUT;

    // Build TLV data
    uint8_t* data = NULL;
    size_t data_len = 0;

    // Add pubkey (special)
    uint8_t* pubkey_tlv;
    size_t pubkey_tlv_len;
    if (!tlv_encode(pubkey, 32, TLV_SPECIAL, &pubkey_tlv, &pubkey_tlv_len)) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    data = realloc(data, data_len + pubkey_tlv_len);
    if (!data) {
        free(pubkey_tlv);
        return NOSTR_ERROR_MEMORY_FAILED;
    }
    memcpy(data + data_len, pubkey_tlv, pubkey_tlv_len);
    data_len += pubkey_tlv_len;
    free(pubkey_tlv);

    // Add relays
    for (int i = 0; i < relay_count; i++) {
        size_t relay_len = strlen(relays[i]);
        uint8_t* relay_tlv;
        size_t relay_tlv_len;
        if (!tlv_encode((uint8_t*)relays[i], relay_len, TLV_RELAY, &relay_tlv, &relay_tlv_len)) {
            free(data);
            return NOSTR_ERROR_INVALID_INPUT;
        }

        data = realloc(data, data_len + relay_tlv_len);
        if (!data) {
            free(relay_tlv);
            return NOSTR_ERROR_MEMORY_FAILED;
        }
        memcpy(data + data_len, relay_tlv, relay_tlv_len);
        data_len += relay_tlv_len;
        free(relay_tlv);
    }

    // Encode to bech32
    char bech32[500];
    int ret = encode_structured_bech32("nprofile", data, data_len, bech32, sizeof(bech32));
    free(data);
    if (ret != NOSTR_SUCCESS) return ret;

    return build_uri_with_prefix(bech32, output, output_size);
}

int nostr_build_uri_nevent(const unsigned char* event_id, const char** relays, int relay_count,
                          const unsigned char* author, int kind, time_t created_at,
                          char* output, size_t output_size) {
    if (!event_id || !output) return NOSTR_ERROR_INVALID_INPUT;

    // Build TLV data
    uint8_t* data = NULL;
    size_t data_len = 0;

    // Add event_id (special)
    uint8_t* event_tlv;
    size_t event_tlv_len;
    if (!tlv_encode(event_id, 32, TLV_SPECIAL, &event_tlv, &event_tlv_len)) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    data = realloc(data, data_len + event_tlv_len);
    if (!data) {
        free(event_tlv);
        return NOSTR_ERROR_MEMORY_FAILED;
    }
    memcpy(data + data_len, event_tlv, event_tlv_len);
    data_len += event_tlv_len;
    free(event_tlv);

    // Add relays
    for (int i = 0; i < relay_count; i++) {
        size_t relay_len = strlen(relays[i]);
        uint8_t* relay_tlv;
        size_t relay_tlv_len;
        if (!tlv_encode((uint8_t*)relays[i], relay_len, TLV_RELAY, &relay_tlv, &relay_tlv_len)) {
            free(data);
            return NOSTR_ERROR_INVALID_INPUT;
        }

        data = realloc(data, data_len + relay_tlv_len);
        if (!data) {
            free(relay_tlv);
            return NOSTR_ERROR_MEMORY_FAILED;
        }
        memcpy(data + data_len, relay_tlv, relay_tlv_len);
        data_len += relay_tlv_len;
        free(relay_tlv);
    }

    // Add author if provided
    if (author) {
        uint8_t* author_tlv;
        size_t author_tlv_len;
        if (!tlv_encode(author, 32, TLV_AUTHOR, &author_tlv, &author_tlv_len)) {
            free(data);
            return NOSTR_ERROR_INVALID_INPUT;
        }

        data = realloc(data, data_len + author_tlv_len);
        if (!data) {
            free(author_tlv);
            return NOSTR_ERROR_MEMORY_FAILED;
        }
        memcpy(data + data_len, author_tlv, author_tlv_len);
        data_len += author_tlv_len;
        free(author_tlv);
    }

    // Add kind if provided
    if (kind >= 0) {
        uint8_t kind_bytes[4];
        kind_bytes[0] = (kind >> 24) & 0xFF;
        kind_bytes[1] = (kind >> 16) & 0xFF;
        kind_bytes[2] = (kind >> 8) & 0xFF;
        kind_bytes[3] = kind & 0xFF;

        uint8_t* kind_tlv;
        size_t kind_tlv_len;
        if (!tlv_encode(kind_bytes, 4, TLV_KIND, &kind_tlv, &kind_tlv_len)) {
            free(data);
            return NOSTR_ERROR_INVALID_INPUT;
        }

        data = realloc(data, data_len + kind_tlv_len);
        if (!data) {
            free(kind_tlv);
            return NOSTR_ERROR_MEMORY_FAILED;
        }
        memcpy(data + data_len, kind_tlv, kind_tlv_len);
        data_len += kind_tlv_len;
        free(kind_tlv);
    }

    // Add created_at if provided
    if (created_at > 0) {
        uint8_t time_bytes[8];
        time_bytes[0] = (created_at >> 56) & 0xFF;
        time_bytes[1] = (created_at >> 48) & 0xFF;
        time_bytes[2] = (created_at >> 40) & 0xFF;
        time_bytes[3] = (created_at >> 32) & 0xFF;
        time_bytes[4] = (created_at >> 24) & 0xFF;
        time_bytes[5] = (created_at >> 16) & 0xFF;
        time_bytes[6] = (created_at >> 8) & 0xFF;
        time_bytes[7] = created_at & 0xFF;

        uint8_t* time_tlv;
        size_t time_tlv_len;
        if (!tlv_encode(time_bytes, 8, TLV_CREATED_AT, &time_tlv, &time_tlv_len)) {
            free(data);
            return NOSTR_ERROR_INVALID_INPUT;
        }

        data = realloc(data, data_len + time_tlv_len);
        if (!data) {
            free(time_tlv);
            return NOSTR_ERROR_MEMORY_FAILED;
        }
        memcpy(data + data_len, time_tlv, time_tlv_len);
        data_len += time_tlv_len;
        free(time_tlv);
    }

    // Encode to bech32
    char bech32[1000];
    int ret = encode_structured_bech32("nevent", data, data_len, bech32, sizeof(bech32));
    free(data);
    if (ret != NOSTR_SUCCESS) return ret;

    return build_uri_with_prefix(bech32, output, output_size);
}

int nostr_build_uri_naddr(const char* identifier, const unsigned char* pubkey, int kind,
                         const char** relays, int relay_count, char* output, size_t output_size) {
    if (!identifier || !pubkey || !output) return NOSTR_ERROR_INVALID_INPUT;

    // Build TLV data
    uint8_t* data = NULL;
    size_t data_len = 0;

    // Add identifier
    size_t id_len = strlen(identifier);
    uint8_t* id_tlv;
    size_t id_tlv_len;
    if (!tlv_encode((uint8_t*)identifier, id_len, TLV_IDENTIFIER, &id_tlv, &id_tlv_len)) {
        return NOSTR_ERROR_INVALID_INPUT;
    }

    data = realloc(data, data_len + id_tlv_len);
    if (!data) {
        free(id_tlv);
        return NOSTR_ERROR_MEMORY_FAILED;
    }
    memcpy(data + data_len, id_tlv, id_tlv_len);
    data_len += id_tlv_len;
    free(id_tlv);

    // Add pubkey (special)
    uint8_t* pubkey_tlv;
    size_t pubkey_tlv_len;
    if (!tlv_encode(pubkey, 32, TLV_SPECIAL, &pubkey_tlv, &pubkey_tlv_len)) {
        free(data);
        return NOSTR_ERROR_INVALID_INPUT;
    }

    data = realloc(data, data_len + pubkey_tlv_len);
    if (!data) {
        free(pubkey_tlv);
        return NOSTR_ERROR_MEMORY_FAILED;
    }
    memcpy(data + data_len, pubkey_tlv, pubkey_tlv_len);
    data_len += pubkey_tlv_len;
    free(pubkey_tlv);

    // Add kind
    uint8_t kind_bytes[4];
    kind_bytes[0] = (kind >> 24) & 0xFF;
    kind_bytes[1] = (kind >> 16) & 0xFF;
    kind_bytes[2] = (kind >> 8) & 0xFF;
    kind_bytes[3] = kind & 0xFF;

    uint8_t* kind_tlv;
    size_t kind_tlv_len;
    if (!tlv_encode(kind_bytes, 4, TLV_KIND, &kind_tlv, &kind_tlv_len)) {
        free(data);
        return NOSTR_ERROR_INVALID_INPUT;
    }

    data = realloc(data, data_len + kind_tlv_len);
    if (!data) {
        free(kind_tlv);
        return NOSTR_ERROR_MEMORY_FAILED;
    }
    memcpy(data + data_len, kind_tlv, kind_tlv_len);
    data_len += kind_tlv_len;
    free(kind_tlv);

    // Add relays
    for (int i = 0; i < relay_count; i++) {
        size_t relay_len = strlen(relays[i]);
        uint8_t* relay_tlv;
        size_t relay_tlv_len;
        if (!tlv_encode((uint8_t*)relays[i], relay_len, TLV_RELAY, &relay_tlv, &relay_tlv_len)) {
            free(data);
            return NOSTR_ERROR_INVALID_INPUT;
        }

        data = realloc(data, data_len + relay_tlv_len);
        if (!data) {
            free(relay_tlv);
            return NOSTR_ERROR_MEMORY_FAILED;
        }
        memcpy(data + data_len, relay_tlv, relay_tlv_len);
        data_len += relay_tlv_len;
        free(relay_tlv);
    }

    // Encode to bech32
    char bech32[1000];
    int ret = encode_structured_bech32("naddr", data, data_len, bech32, sizeof(bech32));
    free(data);
    if (ret != NOSTR_SUCCESS) return ret;

    return build_uri_with_prefix(bech32, output, output_size);
}