//! Minimal ASN.1 DER encoder/decoder.
//!
//! Supports only the tags needed by PKCS#1, PKCS#8, SEC1 and SPKI:
//! SEQUENCE, INTEGER, OCTET STRING, BIT STRING, OID, context-tagged
//! (explicit), and NULL.

// ====================================================================
// Tag constants
// ====================================================================

/// ASN.1 tag bytes.
pub const TAG_INTEGER: u8 = 0x02;
/// BIT STRING tag.
pub const TAG_BIT_STRING: u8 = 0x03;
/// OCTET STRING tag.
pub const TAG_OCTET_STRING: u8 = 0x04;
/// NULL tag.
pub const TAG_NULL: u8 = 0x05;
/// OBJECT IDENTIFIER tag.
pub const TAG_OID: u8 = 0x06;
/// SEQUENCE (constructed) tag.
pub const TAG_SEQUENCE: u8 = 0x30;

// ====================================================================
// Encoder
// ====================================================================

/// DER encoder: builds a byte vector by appending TLV items.
pub struct DerEncoder {
    buf: Vec<u8>,
}

impl DerEncoder {
    /// Create a new empty encoder.
    pub fn new() -> Self {
        Self { buf: Vec::new() }
    }

    /// Return the encoded bytes.
    pub fn finish(self) -> Vec<u8> {
        self.buf
    }

    /// Append a raw DER length.
    pub fn write_length(&mut self, len: usize) {
        if len < 0x80 {
            self.buf.push(len as u8);
        } else if len < 0x100 {
            self.buf.push(0x81);
            self.buf.push(len as u8);
        } else if len < 0x10000 {
            self.buf.push(0x82);
            self.buf.push((len >> 8) as u8);
            self.buf.push((len & 0xFF) as u8);
        } else {
            self.buf.push(0x83);
            self.buf.push((len >> 16) as u8);
            self.buf.push(((len >> 8) & 0xFF) as u8);
            self.buf.push((len & 0xFF) as u8);
        }
    }

    /// Append a SEQUENCE wrapping the given content.
    pub fn sequence(&mut self, content: &[u8]) {
        self.buf.push(TAG_SEQUENCE);
        self.write_length(content.len());
        self.buf.extend_from_slice(content);
    }

    /// Append an INTEGER from big-endian unsigned bytes.
    /// Adds a leading 0x00 if the MSB is set (to keep it positive).
    pub fn integer(&mut self, value: &[u8]) {
        // Strip leading zeros but keep at least one byte.
        let mut start = 0;
        while start < value.len() - 1 && value[start] == 0 {
            start += 1;
        }
        let val = &value[start..];
        let needs_pad = val[0] & 0x80 != 0;
        let total = val.len() + if needs_pad { 1 } else { 0 };

        self.buf.push(TAG_INTEGER);
        self.write_length(total);
        if needs_pad {
            self.buf.push(0x00);
        }
        self.buf.extend_from_slice(val);
    }

    /// Append a small non-negative INTEGER from a u64 (for version fields).
    pub fn integer_u64(&mut self, v: u64) {
        if v == 0 {
            self.buf.extend_from_slice(&[TAG_INTEGER, 1, 0]);
            return;
        }
        let be = v.to_be_bytes();
        let start = be.iter().position(|&b| b != 0).unwrap_or(7);
        self.integer(&be[start..]);
    }

    /// Append an OCTET STRING.
    pub fn octet_string(&mut self, data: &[u8]) {
        self.buf.push(TAG_OCTET_STRING);
        self.write_length(data.len());
        self.buf.extend_from_slice(data);
    }

    /// Append a BIT STRING (with zero unused-bits prefix).
    pub fn bit_string(&mut self, data: &[u8]) {
        self.buf.push(TAG_BIT_STRING);
        self.write_length(data.len() + 1);
        self.buf.push(0x00); // unused bits
        self.buf.extend_from_slice(data);
    }

    /// Append an OBJECT IDENTIFIER from its encoded byte form.
    pub fn oid(&mut self, encoded: &[u8]) {
        self.buf.push(TAG_OID);
        self.write_length(encoded.len());
        self.buf.extend_from_slice(encoded);
    }

    /// Append a NULL.
    pub fn null(&mut self) {
        self.buf.extend_from_slice(&[TAG_NULL, 0x00]);
    }

    /// Append an explicit context-tagged \[N\] CONSTRUCTED wrapping.
    pub fn context_explicit(&mut self, tag_num: u8, content: &[u8]) {
        self.buf.push(0xA0 | tag_num);
        self.write_length(content.len());
        self.buf.extend_from_slice(content);
    }

    /// Append raw bytes (for building inner content).
    pub fn raw(&mut self, data: &[u8]) {
        self.buf.extend_from_slice(data);
    }
}

// ====================================================================
// Decoder
// ====================================================================

/// DER decoder: reads TLV items from a byte slice.
pub struct DerDecoder<'a> {
    data: &'a [u8],
    pos: usize,
}

impl<'a> DerDecoder<'a> {
    /// Create a decoder over a byte slice.
    pub fn new(data: &'a [u8]) -> Self {
        Self { data, pos: 0 }
    }

    /// Bytes remaining.
    pub fn remaining(&self) -> usize {
        self.data.len() - self.pos
    }

    /// True if all bytes consumed.
    pub fn is_empty(&self) -> bool {
        self.pos >= self.data.len()
    }

    /// Peek at the next tag byte without advancing.
    pub fn peek_tag(&self) -> Option<u8> {
        self.data.get(self.pos).copied()
    }

    /// Read tag + length, return (tag, content_slice).
    pub fn read_tlv(&mut self) -> Option<(u8, &'a [u8])> {
        let tag = *self.data.get(self.pos)?;
        self.pos += 1;
        let len = self.read_length()?;
        if self.pos + len > self.data.len() {
            return None;
        }
        let content = &self.data[self.pos..self.pos + len];
        self.pos += len;
        Some((tag, content))
    }

    fn read_length(&mut self) -> Option<usize> {
        let first = *self.data.get(self.pos)?;
        self.pos += 1;
        if first < 0x80 {
            Some(first as usize)
        } else if first == 0x81 {
            let b = *self.data.get(self.pos)? as usize;
            self.pos += 1;
            Some(b)
        } else if first == 0x82 {
            let hi = *self.data.get(self.pos)? as usize;
            let lo = *self.data.get(self.pos + 1)? as usize;
            self.pos += 2;
            Some((hi << 8) | lo)
        } else if first == 0x83 {
            let a = *self.data.get(self.pos)? as usize;
            let b = *self.data.get(self.pos + 1)? as usize;
            let c = *self.data.get(self.pos + 2)? as usize;
            self.pos += 3;
            Some((a << 16) | (b << 8) | c)
        } else {
            None
        }
    }

    /// Read a SEQUENCE, return a sub-decoder over its content.
    pub fn read_sequence(&mut self) -> Option<DerDecoder<'a>> {
        let (tag, content) = self.read_tlv()?;
        if tag != TAG_SEQUENCE {
            return None;
        }
        Some(DerDecoder::new(content))
    }

    /// Read an INTEGER, return the big-endian unsigned bytes
    /// (leading zero stripped).
    pub fn read_integer(&mut self) -> Option<&'a [u8]> {
        let (tag, content) = self.read_tlv()?;
        if tag != TAG_INTEGER || content.is_empty() {
            return None;
        }
        // Strip leading zero used for sign padding.
        if content[0] == 0x00 && content.len() > 1 {
            Some(&content[1..])
        } else {
            Some(content)
        }
    }

    /// Read an INTEGER as u64 (small values like version fields).
    pub fn read_integer_u64(&mut self) -> Option<u64> {
        let bytes = self.read_integer()?;
        let mut v = 0u64;
        for &b in bytes {
            v = (v << 8) | b as u64;
        }
        Some(v)
    }

    /// Read an OCTET STRING.
    pub fn read_octet_string(&mut self) -> Option<&'a [u8]> {
        let (tag, content) = self.read_tlv()?;
        if tag != TAG_OCTET_STRING {
            return None;
        }
        Some(content)
    }

    /// Read a BIT STRING, return the data bytes (skip unused-bits byte).
    pub fn read_bit_string(&mut self) -> Option<&'a [u8]> {
        let (tag, content) = self.read_tlv()?;
        if tag != TAG_BIT_STRING || content.is_empty() {
            return None;
        }
        // First byte = number of unused bits in the last byte.
        Some(&content[1..])
    }

    /// Read an OID, return the encoded bytes.
    pub fn read_oid(&mut self) -> Option<&'a [u8]> {
        let (tag, content) = self.read_tlv()?;
        if tag != TAG_OID {
            return None;
        }
        Some(content)
    }

    /// Read a NULL.
    pub fn read_null(&mut self) -> Option<()> {
        let (tag, content) = self.read_tlv()?;
        if tag != TAG_NULL || !content.is_empty() {
            return None;
        }
        Some(())
    }

    /// Read an explicit context-tagged \[N\] CONSTRUCTED, return a
    /// sub-decoder over its content. Returns None if the tag number
    /// doesn't match or the tag is absent.
    pub fn read_context_explicit(&mut self, tag_num: u8) -> Option<DerDecoder<'a>> {
        let expected = 0xA0 | tag_num;
        if self.peek_tag() != Some(expected) {
            return None;
        }
        let (_, content) = self.read_tlv()?;
        Some(DerDecoder::new(content))
    }

    /// Skip the next TLV element (any tag).
    pub fn skip(&mut self) -> Option<()> {
        self.read_tlv().map(|_| ())
    }
}

// ====================================================================
// Well-known OIDs (encoded form, without tag+length)
// ====================================================================

/// rsaEncryption (1.2.840.113549.1.1.1)
pub const OID_RSA: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01];

/// id-ecPublicKey (1.2.840.10045.2.1)
pub const OID_EC_PUBLIC_KEY: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01];

/// secp256r1 / P-256 (1.2.840.10045.3.1.7)
pub const OID_SECP256R1: &[u8] = &[0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];

/// secp384r1 / P-384 (1.3.132.0.34)
pub const OID_SECP384R1: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x22];

/// secp521r1 / P-521 (1.3.132.0.35)
pub const OID_SECP521R1: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x23];

/// secp256k1 (1.3.132.0.10)
pub const OID_SECP256K1: &[u8] = &[0x2B, 0x81, 0x04, 0x00, 0x0A];

/// brainpoolP256r1 (1.3.36.3.3.2.8.1.1.7)
pub const OID_BRAINPOOL_P256R1: &[u8] = &[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07];

/// brainpoolP384r1 (1.3.36.3.3.2.8.1.1.11)
pub const OID_BRAINPOOL_P384R1: &[u8] = &[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B];

/// brainpoolP512r1 (1.3.36.3.3.2.8.1.1.13)
pub const OID_BRAINPOOL_P512R1: &[u8] = &[0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D];

/// id-Ed25519 (1.3.101.112)
pub const OID_ED25519: &[u8] = &[0x2B, 0x65, 0x70];

/// id-X25519 (1.3.101.110)
pub const OID_X25519: &[u8] = &[0x2B, 0x65, 0x6E];

/// id-X448 (1.3.101.111)
pub const OID_X448: &[u8] = &[0x2B, 0x65, 0x6F];

// ====================================================================
// Tests
// ====================================================================

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn roundtrip_integer() {
        let val = [0x00, 0x80, 0xFF]; // needs leading-zero strip then pad
        let mut enc = DerEncoder::new();
        enc.integer(&val);
        let der = enc.finish();
        let mut dec = DerDecoder::new(&der);
        let got = dec.read_integer().unwrap();
        assert_eq!(got, &[0x80, 0xFF]);
    }

    #[test]
    fn roundtrip_integer_u64() {
        for v in [0u64, 1, 127, 128, 255, 256, 65535, 0xDEADBEEF] {
            let mut enc = DerEncoder::new();
            enc.integer_u64(v);
            let der = enc.finish();
            let mut dec = DerDecoder::new(&der);
            assert_eq!(dec.read_integer_u64().unwrap(), v, "v={}", v);
        }
    }

    #[test]
    fn roundtrip_sequence() {
        let mut inner = DerEncoder::new();
        inner.integer_u64(42);
        inner.octet_string(b"hello");
        let content = inner.finish();

        let mut outer = DerEncoder::new();
        outer.sequence(&content);
        let der = outer.finish();

        let mut dec = DerDecoder::new(&der);
        let mut seq = dec.read_sequence().unwrap();
        assert_eq!(seq.read_integer_u64().unwrap(), 42);
        assert_eq!(seq.read_octet_string().unwrap(), b"hello");
        assert!(seq.is_empty());
    }

    #[test]
    fn roundtrip_bit_string() {
        let data = [0x04, 0x01, 0x02]; // uncompressed EC point prefix
        let mut enc = DerEncoder::new();
        enc.bit_string(&data);
        let der = enc.finish();

        let mut dec = DerDecoder::new(&der);
        let got = dec.read_bit_string().unwrap();
        assert_eq!(got, &data);
    }

    #[test]
    fn roundtrip_oid() {
        let mut enc = DerEncoder::new();
        enc.oid(OID_RSA);
        let der = enc.finish();

        let mut dec = DerDecoder::new(&der);
        assert_eq!(dec.read_oid().unwrap(), OID_RSA);
    }

    #[test]
    fn context_explicit_tag() {
        let mut inner = DerEncoder::new();
        inner.integer_u64(1);
        let content = inner.finish();

        let mut enc = DerEncoder::new();
        enc.context_explicit(0, &content);
        let der = enc.finish();

        let mut dec = DerDecoder::new(&der);
        let mut ctx = dec.read_context_explicit(0).unwrap();
        assert_eq!(ctx.read_integer_u64().unwrap(), 1);
    }

    #[test]
    fn large_length_encoding() {
        // 256 bytes → 0x82 0x01 0x00 encoding
        let data = vec![0xABu8; 256];
        let mut enc = DerEncoder::new();
        enc.octet_string(&data);
        let der = enc.finish();

        let mut dec = DerDecoder::new(&der);
        let got = dec.read_octet_string().unwrap();
        assert_eq!(got, &data[..]);
    }
}
