use sci_gaussfilt_rs::{gaussian_filter1d, gaussian_kernel, types::BoundaryMode};
use approx::assert_relative_eq;
use serde::Deserialize;
use std::fs::File;
use std::io::BufReader;

#[derive(Deserialize)]
struct TestData {
    input: Vec<f64>,
    sigma: f64,
    truncate: f64,
    mode: String,
    expected_output: Vec<f64>,
    expected_kernel: Vec<f64>,
}

#[test]
fn test_comprehensive_match() {
    // 1. Load Data
    let file = File::open("test_data.json").expect("Failed to open test_data.json");
    let reader = BufReader::new(file);
    let data: TestData = serde_json::from_reader(reader).expect("Failed to parse JSON");

    // 2. Verify Kernel
    let kernel = gaussian_kernel(data.sigma, data.truncate);
    
    println!("Kernel Length - Rust: {}, SciPy: {}", kernel.len(), data.expected_kernel.len());
    assert_eq!(kernel.len(), data.expected_kernel.len(), "Kernel sizes do not match");

    for (i, (k_rs, k_py)) in kernel.iter().zip(data.expected_kernel.iter()).enumerate() {
        assert_relative_eq!(k_rs, k_py, epsilon = 1e-8, max_relative = 1e-8);
        if (k_rs - k_py).abs() > 1e-8 {
             println!("Kernel mismatch at index {}: Rust {}, SciPy {}", i, k_rs, k_py);
        }
    }

    // 3. Verify Output
    let mode = match data.mode.as_str() {
        "reflect" => BoundaryMode::Reflect,
        "nearest" => BoundaryMode::Nearest,
        "constant" => BoundaryMode::Constant(0.0),
        _ => panic!("Unknown mode in test data"),
    };

    let result = gaussian_filter1d(&data.input, data.sigma, data.truncate, mode).unwrap();

    assert_eq!(result.len(), data.expected_output.len(), "Output sizes do not match");

    let mut max_diff = 0.0;
    for (i, (r, e)) in result.iter().zip(data.expected_output.iter()).enumerate() {
        let diff = (r - e).abs();
        if diff > max_diff {
            max_diff = diff;
        }
        // We use a slightly looser tolerance for the full convolution due to accumulated floating point errors
        if (r - e).abs() > 1e-6 {
             println!("Mismatch at index {}: Rust {}, SciPy {}, Diff {}", i, r, e, diff);
        }
        assert_relative_eq!(r, e, epsilon = 1e-6);
    }
    
    println!("Max difference in output: {}", max_diff);
}
