//! Tests for timeboxed operations.
//!
//! This module contains tests for time-sliced operations that can be
//! paused and resumed, enabling asynchronous computation.

use clock_bigint::timeboxed::OperationState;
use clock_bigint::{
    BigInt, BigIntCore, MontgomeryContext, OpType, PartialResult, modexp_timeboxed, mul_timeboxed,
};

#[test]
fn test_mul_timeboxed_complete() {
    let a = BigInt::from_u64(10, 10);
    let b = BigInt::from_u64(20, 10);

    let result = mul_timeboxed(&a, &b, 1000).unwrap();

    match result {
        PartialResult::Complete(product) => {
            assert_eq!(product.limbs()[0], 200); // 10 * 20 = 200
        }
        PartialResult::Partial(_) => panic!("Expected complete result"),
    }
}

#[test]
fn test_mul_timeboxed_zero() {
    let a = BigInt::from_u64(0, 10);
    let b = BigInt::from_u64(20, 10);

    let result = mul_timeboxed(&a, &b, 1000).unwrap();

    match result {
        PartialResult::Complete(product) => {
            assert_eq!(product.limbs()[0], 0); // 0 * 20 = 0
        }
        PartialResult::Partial(_) => panic!("Expected complete result"),
    }
}

#[test]
fn test_mul_timeboxed_large() {
    let a = BigInt::from_limbs(&[0x123456789ABCDEF0, 0xFEDCBA9876543210], 4).unwrap();
    let b = BigInt::from_limbs(&[0x1111111111111111, 0x2222222222222222], 4).unwrap();

    let result = mul_timeboxed(&a, &b, 1000).unwrap();

    match result {
        PartialResult::Complete(product) => {
            // Verify the result has the expected number of limbs
            assert!(product.limb_count() <= 4); // Should fit in max_limbs
        }
        PartialResult::Partial(_) => panic!("Expected complete result for reasonable cycle limit"),
    }
}

#[test]
fn test_modexp_timeboxed_small() {
    let modulus = BigInt::from_u64(97, 10); // Prime modulus
    let ctx = MontgomeryContext::new(modulus).unwrap();

    let base = BigInt::from_u64(5, 10);
    let exponent = BigInt::from_u64(3, 10); // 5^3 = 125

    let result = modexp_timeboxed(&ctx, &base, &exponent, 1000).unwrap();

    match result {
        PartialResult::Complete(result) => {
            // Just verify we got a result (exact value depends on Montgomery arithmetic)
            assert!(result.limb_count() >= 1);
        }
        PartialResult::Partial(_) => panic!("Expected complete result"),
    }
}

#[test]
fn test_modexp_timeboxed_exponent_zero() {
    let modulus = BigInt::from_u64(97, 10);
    let ctx = MontgomeryContext::new(modulus).unwrap();

    let base = BigInt::from_u64(5, 10);
    let exponent = BigInt::from_u64(0, 10); // base^0 = 1

    let result = modexp_timeboxed(&ctx, &base, &exponent, 1000).unwrap();

    match result {
        PartialResult::Complete(result) => {
            assert_eq!(result.limbs()[0], 1);
        }
        PartialResult::Partial(_) => panic!("Expected complete result"),
    }
}

#[test]
fn test_modexp_timeboxed_base_zero() {
    let modulus = BigInt::from_u64(97, 10);
    let ctx = MontgomeryContext::new(modulus).unwrap();

    let base = BigInt::from_u64(0, 10);
    let exponent = BigInt::from_u64(5, 10); // 0^5 = 0 (for exponent > 0)

    let result = modexp_timeboxed(&ctx, &base, &exponent, 1000).unwrap();

    match result {
        PartialResult::Complete(result) => {
            assert_eq!(result.limbs()[0], 0);
        }
        PartialResult::Partial(_) => panic!("Expected complete result"),
    }
}

#[test]
fn test_modexp_timeboxed_base_one() {
    let modulus = BigInt::from_u64(97, 10);
    let ctx = MontgomeryContext::new(modulus).unwrap();

    let base = BigInt::from_u64(1, 10);
    let exponent = BigInt::from_u64(10, 10); // 1^10 = 1

    let result = modexp_timeboxed(&ctx, &base, &exponent, 1000).unwrap();

    match result {
        PartialResult::Complete(result) => {
            assert_eq!(result.limbs()[0], 1);
        }
        PartialResult::Partial(_) => panic!("Expected complete result"),
    }
}

#[test]
fn test_modexp_timeboxed_large() {
    let modulus = BigInt::from_limbs(&[0xFFFFFFFFFFFFFF43, 0xFFFFFFFFFFFFFFFF], 4).unwrap();
    let ctx = MontgomeryContext::new(modulus).unwrap();

    let base = BigInt::from_u64(12345, 4);
    let exponent = BigInt::from_u64(1000, 4);

    let result = modexp_timeboxed(&ctx, &base, &exponent, 10000).unwrap();

    match result {
        PartialResult::Complete(_) => {
            // Just verify it completes successfully
        }
        PartialResult::Partial(_) => panic!("Expected complete result for reasonable cycle limit"),
    }
}

#[test]
fn test_resume_function_signature() {
    // Test that the resume function exists and has the expected signature
    // We can't easily test resume functionality without creating valid partial states,
    // so we just verify the function signature is accessible

    // This is a compile-time test - if we can reference the types and function,
    // the API is correctly exposed
    let _op_type: OpType = OpType::Mul;
    let _state: OperationState = OperationState::MulState {
        a: BigInt::from_u64(1, 4),
        b: BigInt::from_u64(2, 4),
        result: vec![0],
        i: 0,
        j: 0,
    };
}
