//! Tests for examples in README.md to ensure they compile and run correctly

use num::One;
use num_valid::functions::{Abs, ComplexScalarConstructors, Conjugate, Exp, Ln, Reciprocal, Sqrt};
use num_valid::{ComplexNative64StrictFinite, RealNative64StrictFinite, RealScalar};
use try_create::TryNew;

#[test]
fn test_quick_example() -> Result<(), Box<dyn std::error::Error>> {
    // ✅ Valid value - compiles and runs safely
    let x = RealNative64StrictFinite::try_new(4.0)?;
    let sqrt_x = x.sqrt(); // Always safe - no NaN surprises!
    assert_eq!(*sqrt_x.as_ref(), 2.0);

    // ❌ Invalid value - caught immediately
    let bad = RealNative64StrictFinite::try_new(f64::NAN); // Returns Err
    assert!(bad.is_err());

    Ok(())
}

#[test]
fn test_basic_usage() -> Result<(), Box<dyn std::error::Error>> {
    // Create a validated number
    let x = RealNative64StrictFinite::try_new(4.0)?;

    // Panicking version - for known-valid inputs
    let sqrt_x = x.sqrt();
    assert_eq!(*sqrt_x.as_ref(), 2.0);

    // Fallible version - for runtime validation
    let neg = RealNative64StrictFinite::try_new(-4.0)?;
    match neg.try_sqrt() {
        Ok(_result) => panic!("Should have failed"),
        Err(_e) => {} // Expected error for sqrt of negative
    }

    Ok(())
}

#[test]
fn test_generic_functions() -> Result<(), Box<dyn std::error::Error>> {
    // Generic function works with any RealScalar type
    fn pythagorean_identity<T: RealScalar>(angle: T) -> T {
        let sin_x = angle.clone().sin();
        let cos_x = angle.cos();
        (sin_x.clone() * sin_x) + (cos_x.clone() * cos_x)
    }

    // Use with native f64 precision
    let angle = RealNative64StrictFinite::try_from_f64(0.5)?;
    let result = pythagorean_identity(angle);
    let one = RealNative64StrictFinite::one();

    // The result should be very close to 1.0 (sin²x + cos²x = 1)
    let diff = (result - one).abs();
    assert!(*diff.as_ref() < 1e-15);

    Ok(())
}

#[cfg(feature = "rug")]
#[test]
fn test_arbitrary_precision() -> Result<(), Box<dyn std::error::Error>> {
    use num_valid::RealRugStrictFinite;

    fn pythagorean_identity<T: RealScalar>(angle: T) -> T {
        let sin_x = angle.clone().sin();
        let cos_x = angle.cos();
        (sin_x.clone() * sin_x) + (cos_x.clone() * cos_x)
    }

    // Use with 200-bit precision (≈60 decimal digits)
    type HighPrecision = RealRugStrictFinite<200>;

    let angle = HighPrecision::try_from_f64(0.5)?;
    let result = pythagorean_identity(angle);
    let one = HighPrecision::one();

    // More accurate than f64!
    let diff = (result - one).abs();
    let threshold = HighPrecision::try_from_f64(1e-50)?;
    assert!(*diff.as_ref() < *threshold.as_ref());

    Ok(())
}

#[test]
fn test_complex_numbers() {
    // Create complex number: 3 + 4i
    let re = RealNative64StrictFinite::from_f64(3.0);
    let im = RealNative64StrictFinite::from_f64(4.0);
    let z = ComplexNative64StrictFinite::new_complex(re, im);

    // Complex operations
    let magnitude = z.abs(); // |3 + 4i| = 5.0
    assert_eq!(*magnitude.as_ref(), 5.0);

    let _conjugate = z.conjugate(); // 3 - 4i
    let _reciprocal = z.reciprocal(); // 1/z

    // Complex math functions
    let _exp_z = z.exp();
    let _log_z = z.ln();
    let _sqrt_z = z.sqrt();
}

#[test]
fn test_zero_copy_conversions() {
    use bytemuck::checked::try_from_bytes;

    // From bytes to validated type
    let bytes = 42.0_f64.to_ne_bytes();
    let validated: &RealNative64StrictFinite = try_from_bytes(&bytes).unwrap();
    assert_eq!(*validated.as_ref(), 42.0);

    // Invalid values automatically rejected
    let nan_bytes = f64::NAN.to_ne_bytes();
    assert!(try_from_bytes::<RealNative64StrictFinite>(&nan_bytes).is_err());
}
