//! End-to-end tests building synthetic baseline + drifted current datasets
//! and verifying each detector flags drift above its default threshold.

use ndarray::{Array1, Array2};
use ndarray_rand::rand_distr::StandardNormal;
use ndarray_rand::RandomExt;

use ragdrift_core::detectors::{
    ConfidenceDriftDetector, DataDriftDetector, EmbeddingDriftDetector, QueryDriftDetector,
    ResponseDriftDetector,
};
use ragdrift_core::types::DriftDimension;

#[test]
fn embedding_detector_flags_severe_shift() {
    let baseline = Array2::<f32>::random((256, 32), StandardNormal);
    let mut current = Array2::<f32>::random((256, 32), StandardNormal);
    current.mapv_inplace(|v| v + 4.0);
    let detector = EmbeddingDriftDetector::default();
    let s = detector.detect(&baseline.view(), &current.view()).unwrap();
    assert_eq!(s.dimension, DriftDimension::Embedding);
    assert!(s.exceeded);
}

#[test]
fn data_detector_flags_severe_feature_shift() {
    let baseline = Array2::from_shape_fn((500, 5), |(i, j)| ((i * (j + 1)) as f64) * 0.01);
    let mut current = baseline.clone();
    for i in 0..current.nrows() {
        current[[i, 2]] += 1000.0;
    }
    let detector = DataDriftDetector::default();
    let s = detector.detect(&baseline.view(), &current.view()).unwrap();
    assert_eq!(s.dimension, DriftDimension::Data);
    assert!(s.exceeded);
}

#[test]
fn response_detector_flags_length_shift() {
    let baseline = Array1::from((1..=200).map(|x| x as f64).collect::<Vec<_>>());
    let current = Array1::from((1..=200).map(|x| (x as f64) + 1000.0).collect::<Vec<_>>());
    let detector = ResponseDriftDetector::default();
    let s = detector.detect(&baseline.view(), &current.view()).unwrap();
    assert_eq!(s.dimension, DriftDimension::Response);
    assert!(s.exceeded);
}

#[test]
fn confidence_detector_flags_calibration_collapse() {
    // Baseline: high-confidence accurate. Current: high-confidence wrong.
    let baseline_scores = Array1::from(vec![0.95; 200]);
    let baseline_labels = Array1::from(vec![1.0; 200]);
    let current_scores = Array1::from(vec![0.95; 200]);
    let current_labels = Array1::from(vec![0.0; 200]);
    let detector = ConfidenceDriftDetector::default();
    let s = detector
        .detect_with_calibration(
            &baseline_scores.view(),
            &baseline_labels.view(),
            &current_scores.view(),
            &current_labels.view(),
        )
        .unwrap();
    assert_eq!(s.dimension, DriftDimension::Confidence);
    assert!(s.exceeded);
}

#[test]
fn query_detector_flags_topic_shift() {
    let baseline = Array2::<f32>::random((256, 16), StandardNormal);
    let mut current = Array2::<f32>::random((256, 16), StandardNormal);
    current.mapv_inplace(|v| v + 6.0);
    let detector = QueryDriftDetector::default();
    let s = detector.detect(&baseline.view(), &current.view()).unwrap();
    assert_eq!(s.dimension, DriftDimension::Query);
    assert!(s.exceeded);
}

#[test]
fn no_drift_under_h0() {
    let baseline = Array2::<f32>::random((128, 16), StandardNormal);
    let detector = EmbeddingDriftDetector::default();
    let s = detector.detect(&baseline.view(), &baseline.view()).unwrap();
    assert!(!s.exceeded);
    assert!(s.score.abs() < 1e-6);
}
