#include "x509.h"
#include "crypto.h"
#include "ecc.h"
#include "util.h"
#include "fb.h"
#include "dbg.h"

/* ══════════════════════════════════════════════
 *  ASN.1 DER helpers
 * ══════════════════════════════════════════════ */

static int asn1_read_tag(const uint8_t *data, uint32_t len,
                         uint32_t *pos, uint32_t *tag, uint32_t *tag_len)
{
    if(*pos >= len) return -1;

    uint32_t t = data[*pos];
    (*pos)++;

    /* Check constructed bit (for SEQUENCE, etc) */
    int constructed = (t & 0x20) != 0;
    (void)constructed;

    /* Read length */
    if(*pos >= len) return -1;
    uint32_t l = data[*pos];
    (*pos)++;

    if(l & 0x80) {
        int num_bytes = l & 0x7F;
        if(num_bytes == 0) return -1; /* indefinite length not supported */
        l = 0;
        for(int i = 0; i < num_bytes; i++) {
            if(*pos >= len) return -1;
            l = (l << 8) | data[*pos];
            (*pos)++;
        }
    }

    *tag = t;
    *tag_len = l;
    return 0;
}

/* Skip an ASN.1 element */
static int asn1_skip(const uint8_t *data, uint32_t len, uint32_t *pos)
{
    uint32_t tag, tag_len;
    if(asn1_read_tag(data, len, pos, &tag, &tag_len) < 0) return -1;
    *pos += tag_len;
    return 0;
}

/* Read INTEGER into uint64_t (for small values) */
static uint32_t asn1_read_int(const uint8_t *data, uint32_t len, uint32_t *pos)
{
    uint32_t tag, tag_len;
    if(asn1_read_tag(data, len, pos, &tag, &tag_len) < 0) return 0;
    uint32_t val = 0;
    for(uint32_t i = 0; i < tag_len && i < 4; i++)
        val = (val << 8) | data[*pos + i];
    *pos += tag_len;
    return val;
}

/* Read bit string (returns pointer to content, length) */
static const uint8_t *asn1_read_bitstring(const uint8_t *data, uint32_t len,
                                           uint32_t *pos, uint32_t *out_len)
{
    uint32_t tag, tag_len;
    if(asn1_read_tag(data, len, pos, &tag, &tag_len) < 0) return NULL;
    if(tag != 0x03) return NULL; /* not BIT STRING */
    if(tag_len < 1) return NULL;
    uint8_t unused_bits = data[*pos];
    (*pos)++;
    tag_len--;
    (void)unused_bits;
    *out_len = tag_len;
    return data + *pos;
}

/* ══════════════════════════════════════════════
 *  X.509 Certificate Parser (minimal)
 * ══════════════════════════════════════════════ */

int x509_parse_cert(const uint8_t *der, uint32_t len, struct x509_cert *out)
{
    memset(out, 0, sizeof(*out));
    memcpy(out->raw, der, len);
    out->raw_len = len;

    uint32_t pos = 0;
    uint32_t tag, tag_len;

    /* Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue } */
    if(asn1_read_tag(der, len, &pos, &tag, &tag_len) < 0) return -1;
    if(tag != 0x30) return -1;

    /* tbsCertificate ::= SEQUENCE */
    uint32_t tbs_start = pos;
    if(asn1_read_tag(der, len, &pos, &tag, &tag_len) < 0) return -1;
    if(tag != 0x30) return -1;
    uint32_t tbs_end = pos + tag_len;

    out->tbs = (uint8_t *)(der + tbs_start);
    out->tbs_len = tbs_end - tbs_start;

    /* Version [0] EXPLICIT INTEGER DEFAULT v1 */
    if(asn1_read_tag(der, len, &pos, &tag, &tag_len) < 0) return -1;
    if(tag == 0xA0) { /* context-specific [0] = version */
        uint32_t inner_tag, inner_len;
        uint32_t inner_pos = pos;
        if(asn1_read_tag(der, len, &inner_pos, &inner_tag, &inner_len) < 0) return -1;
        pos = tbs_end;
    }

    /* serialNumber INTEGER */
    pos = tbs_start + 2; /* skip TBS SEQUENCE tag+len */
    if(asn1_read_tag(der, len, &pos, &tag, &tag_len) < 0) return -1;

    /* signatureAlgorithm AlgorithmIdentifier */
    uint32_t sigalgo_start = pos;
    if(asn1_read_tag(der, len, &pos, &tag, &tag_len) < 0) return -1;

    /* Parse OID */
    if(tag == 0x30 && tag_len > 0) {
        uint32_t oid_pos = pos;
        uint32_t oid_tag, oid_len;
        if(asn1_read_tag(der, len, &oid_pos, &oid_tag, &oid_len) == 0) {
            if(oid_tag == 0x06) { /* OID */
                memcpy(out->sig_algo, der + oid_pos, oid_len < 16 ? oid_len : 16);
                out->sig_algo_len = oid_len;
            }
        }
    }
    pos = sigalgo_start + 2 + tag_len;

    /* subjectPublicKeyInfo */
    if(asn1_read_tag(der, len, &pos, &tag, &tag_len) < 0) return -1;

    /* AlgorithmIdentifier inside SPKI */
    if(asn1_read_tag(der, len, &pos, &tag, &tag_len) < 0) return -1;
    pos += tag_len;

    /* SubjectPublicKey BIT STRING */
    uint32_t pubkey_len;
    const uint8_t *pubkey_data = asn1_read_bitstring(der, len, &pos, &pubkey_len);
    if(pubkey_data && pubkey_len >= 65 && pubkey_data[0] == 0x04) {
        /* Uncompressed EC point: 04 || X (32) || Y (32) */
        memcpy(out->pubkey, pubkey_data + 1, 64);
    }

    /* Skip extensions if present */
    pos = tbs_end;

    /* signatureAlgorithm (again, after TBS) */
    if(asn1_read_tag(der, len, &pos, &tag, &tag_len) < 0) return -1;
    pos += tag_len;

    /* signatureValue BIT STRING */
    uint32_t sig_len;
    const uint8_t *sig_data = asn1_read_bitstring(der, len, &pos, &sig_len);
    if(sig_data && sig_len > 0) {
        /* Parse ECDSA signature: SEQUENCE { INTEGER r, INTEGER s } */
        uint32_t sig_pos = 0;
        uint32_t inner_tag, inner_len;
        if(asn1_read_tag(sig_data, sig_len, &sig_pos, &inner_tag, &inner_len) == 0 && inner_tag == 0x30) {
            /* r INTEGER */
            uint32_t r_len = 0;
            const uint8_t *r_data = NULL;
            if(asn1_read_tag(sig_data, sig_len, &sig_pos, &inner_tag, &inner_len) == 0 && inner_tag == 0x02) {
                r_data = sig_data + sig_pos;
                r_len = inner_len;
                sig_pos += inner_len;
            }

            /* s INTEGER */
            const uint8_t *s_data = NULL;
            uint32_t s_len = 0;
            if(asn1_read_tag(sig_data, sig_len, &sig_pos, &inner_tag, &inner_len) == 0 && inner_tag == 0x02) {
                s_data = sig_data + sig_pos;
                s_len = inner_len;
            }

            /* Extract r and s as 32-byte big-endian values */
            if(r_data && r_len > 0) {
                memset(out->sig_r, 0, 32);
                if(r_len <= 32)
                    memcpy(out->sig_r + (32 - r_len), r_data, r_len);
                else
                    memcpy(out->sig_r, r_data + (r_len - 32), 32);
            }
            if(s_data && s_len > 0) {
                memset(out->sig_s, 0, 32);
                if(s_len <= 32)
                    memcpy(out->sig_s + (32 - s_len), s_data, s_len);
                else
                    memcpy(out->sig_s, s_data + (s_len - 32), 32);
            }
        }
    }

    return 0;
}

/* ══════════════════════════════════════════════
 *  Certificate chain validation
 * ══════════════════════════════════════════════ */

int x509_validate_chain(struct x509_cert *chain, int count)
{
    if(count < 1) return -1;

    /* For simplicity, verify leaf certificate signature using issuer's public key.
     * We check if each cert's signature is valid using the next cert's public key.
     * The root is trusted by being in our embedded store. */

    for(int i = 0; i < count - 1; i++) {
        /* Verify chain[i] signature using chain[i+1]'s public key */
        int rc = ecdsa_verify(chain[i+1].pubkey,
                              chain[i].tbs, chain[i].tbs_len,
                              chain[i].sig_r, chain[i].sig_s);
        if(rc != 0) {
            dbg_printf("[x509] cert %d signature INVALID\n", i);
            return -1;
        }
        dbg_printf("[x509] cert %d signature OK\n", i);
    }

    return 0;
}

/* ══════════════════════════════════════════════
 *  Embedded root certificates
 * ══════════════════════════════════════════════ */

/* ISRG Root X2 public key (ECDSA P-384, but we only need the EC key for verification) */
/* For prismsoftware.ru, the chain is:
 *   Leaf: prismsoftware.ru (ECDSA P-256, signed by YE2)
 *   Intermediate: YE2 (ECDSA P-384, signed by Root YE)
 *   Root YE (ECDSA P-384, signed by ISRG Root X2)
 *   ISRG Root X2 (self-signed, ECDSA P-384)
 *
 * Since we can only do P-256 verification, we'll skip chain validation
 * and accept the leaf certificate. This is a demo, not production security.
 */

const uint8_t *x509_get_isrg_root_x1_key(void)
{
    /* Not used - we skip full chain validation for demo */
    return NULL;
}
