//! Encoding and decoding tests.

use clock_bigint::{BigInt, BigIntCore, BigIntError, decode, encode};

#[test]
fn test_encode_decode_roundtrip() {
    let values = vec![0, 1, 42, 100, u64::MAX];

    for val in values {
        let bi = BigInt::from_u64(val, 10);
        let encoded = encode(&bi);
        let decoded = decode(&encoded).unwrap();
        assert_eq!(decoded.limbs()[0], val);
        assert_eq!(decoded.sign(), bi.sign());
    }
}

#[test]
fn test_encode_zero() {
    let zero = BigInt::from_u64(0, 10);
    let encoded = encode(&zero);

    // Check format: [sign][limb_count][limbs...]
    assert_eq!(encoded[0], 0); // sign = 0
    let limb_count = u32::from_le_bytes(encoded[1..5].try_into().unwrap());
    assert_eq!(limb_count, 1);
    let limb = u64::from_le_bytes(encoded[5..13].try_into().unwrap());
    assert_eq!(limb, 0);
}

#[test]
fn test_decode_rejects_negative_zero() {
    // Create invalid encoding: sign=1, but value is zero
    let mut invalid = vec![1u8]; // sign = 1 (negative)
    invalid.extend_from_slice(&1u32.to_le_bytes()); // limb_count = 1
    invalid.extend_from_slice(&0u64.to_le_bytes()); // limb[0] = 0

    let result = decode(&invalid);
    assert!(matches!(result, Err(BigIntError::InvalidEncoding)));
}

#[test]
fn test_decode_rejects_leading_zeros() {
    // Create invalid encoding: nonzero value with leading zero
    let mut invalid = vec![0u8]; // sign = 0
    invalid.extend_from_slice(&2u32.to_le_bytes()); // limb_count = 2
    invalid.extend_from_slice(&42u64.to_le_bytes()); // limb[0] = 42
    invalid.extend_from_slice(&0u64.to_le_bytes()); // limb[1] = 0 (invalid!)

    let result = decode(&invalid);
    assert!(matches!(result, Err(BigIntError::InvalidEncoding)));
}

#[test]
fn test_decode_rejects_too_short() {
    let invalid = vec![0u8, 1u8, 2u8, 3u8]; // Too short
    let result = decode(&invalid);
    assert!(matches!(result, Err(BigIntError::InvalidEncoding)));
}

#[test]
fn test_decode_rejects_invalid_sign() {
    let mut invalid = vec![2u8]; // sign = 2 (invalid, must be 0 or 1)
    invalid.extend_from_slice(&1u32.to_le_bytes());
    invalid.extend_from_slice(&42u64.to_le_bytes());

    let result = decode(&invalid);
    assert!(matches!(result, Err(BigIntError::InvalidEncoding)));
}

#[test]
fn test_canonical_encoding_uniqueness() {
    // Same value should always encode to the same bytes
    let bi1 = BigInt::from_u64(42, 10);
    let bi2 = BigInt::from_u64(42, 10);

    let enc1 = encode(&bi1);
    let enc2 = encode(&bi2);

    assert_eq!(enc1, enc2);
}
