//! Specification compliance tests.
//!
//! Tests that verify the implementation conforms to the ClockinChain
//! Big Integer specifications.

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

#[test]
fn test_zero_representation() {
    // Spec: Zero MUST be encoded as sign=0, n=1, limbs[0]=0
    let zero = BigInt::from_u64(0, 10);
    assert!(!zero.sign()); // sign must be 0
    assert_eq!(zero.limb_count(), 1); // n = 1
    assert_eq!(zero.limbs()[0], 0); // limbs[0] = 0
}

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

    // Verify canonical encoding
    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_no_leading_zeros() {
    // Spec: For nonzero values, limbs[n-1] ≠ 0
    let bi = BigInt::from_u64(42, 10);
    let encoded = encode(&bi);
    let decoded = decode(&encoded).unwrap();

    let limbs = decoded.limbs();
    assert!(!limbs.is_empty());
    assert_ne!(limbs[limbs.len() - 1], 0); // Last limb must be nonzero
}

#[test]
fn test_canonical_encoding_uniqueness() {
    // Spec: Exactly one valid encoding exists per integer
    let value = 42u64;
    let bi1 = BigInt::from_u64(value, 10);
    let bi2 = BigInt::from_u64(value, 10);

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

    assert_eq!(enc1, enc2, "Same value must produce identical encoding");
}

#[test]
fn test_reject_negative_zero() {
    // Spec: Negative zero is forbidden
    let mut invalid = vec![1u8]; // sign = 1
    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_reject_non_canonical_leading_zeros() {
    // Spec: No leading zero limbs for nonzero values
    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_minimal_length() {
    // Spec: Minimal length - smallest n satisfying canonical form rules
    let bi = BigInt::from_u64(42, 10);
    let encoded = encode(&bi);
    let decoded = decode(&encoded).unwrap();

    // Should have exactly 1 limb for a value < 2^64
    assert_eq!(decoded.limb_count(), 1);
}

#[test]
fn test_cross_platform_determinism() {
    // Spec: Deterministic across all platforms
    let values = vec![0, 1, 42, 100, u64::MAX];

    for val in values {
        let bi = BigInt::from_u64(val, 10);
        let encoded = encode(&bi);

        // Encoding should be deterministic
        let encoded2 = encode(&bi);
        assert_eq!(encoded, encoded2);

        // Decoding should produce same result
        let decoded1 = decode(&encoded).unwrap();
        let decoded2 = decode(&encoded2).unwrap();
        assert_eq!(decoded1.limbs()[0], decoded2.limbs()[0]);
    }
}
