use super::{SyncAnalysisOptions, SyncAnalysisReport};

#[test]
fn default_options_match_live_probe_expectations() {
    let options = SyncAnalysisOptions::default();
    assert_eq!(options.audio_window_ms, 5);
    assert!((options.max_pair_gap_s - 0.5).abs() < f64::EPSILON);
    assert!((options.pulse_period_s - 1.0).abs() < f64::EPSILON);
    assert!((options.pulse_width_s - 0.12).abs() < f64::EPSILON);
    assert_eq!(options.marker_tick_period, 5);
    assert!(options.event_width_codes.is_empty());
}

#[test]
/// Keeps `calibration_recommendation_requires_enough_pairs` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn calibration_recommendation_requires_enough_pairs() {
    let report = SyncAnalysisReport {
        video_event_count: 4,
        audio_event_count: 4,
        paired_event_count: 4,
        coded_events: false,
        activity_start_delta_ms: 0.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 0.0,
        first_skew_ms: 20.0,
        last_skew_ms: 20.0,
        mean_skew_ms: 20.0,
        median_skew_ms: 20.0,
        max_abs_skew_ms: 20.0,
        drift_ms: 0.0,
        skews_ms: vec![20.0; 4],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let recommendation = report.calibration_recommendation();
    assert!(!recommendation.ready);
    assert_eq!(recommendation.recommended_audio_offset_adjust_us, 0);
    assert!(
        recommendation
            .note
            .contains("need at least 13 paired pulses")
    );
}

#[test]
/// Keeps `calibration_recommendation_rejects_unstable_drift` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn calibration_recommendation_rejects_unstable_drift() {
    let report = SyncAnalysisReport {
        video_event_count: 12,
        audio_event_count: 12,
        paired_event_count: 14,
        coded_events: false,
        activity_start_delta_ms: 0.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 0.0,
        first_skew_ms: 10.0,
        last_skew_ms: 70.0,
        mean_skew_ms: 40.0,
        median_skew_ms: 35.0,
        max_abs_skew_ms: 70.0,
        drift_ms: 60.0,
        skews_ms: vec![10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let recommendation = report.calibration_recommendation();
    assert!(!recommendation.ready);
    assert_eq!(recommendation.recommended_audio_offset_adjust_us, 0);
    assert!(recommendation.note.contains("drift 60.0 ms exceeds"));
}

#[test]
/// Keeps `calibration_recommendation_maps_median_skew_to_audio_and_video_offsets` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn calibration_recommendation_maps_median_skew_to_audio_and_video_offsets() {
    let report = SyncAnalysisReport {
        video_event_count: 14,
        audio_event_count: 14,
        paired_event_count: 14,
        coded_events: false,
        activity_start_delta_ms: 0.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 0.0,
        first_skew_ms: 28.0,
        last_skew_ms: 32.0,
        mean_skew_ms: 30.0,
        median_skew_ms: 30.0,
        max_abs_skew_ms: 32.0,
        drift_ms: 4.0,
        skews_ms: vec![28.0, 30.0, 32.0],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let recommendation = report.calibration_recommendation();
    assert!(recommendation.ready);
    assert_eq!(recommendation.recommended_audio_offset_adjust_us, -30_000);
    assert_eq!(recommendation.recommended_video_offset_adjust_us, 30_000);
    assert!(recommendation.note.contains("move median skew"));
}

#[test]
/// Keeps `calibration_recommendation_accepts_large_stable_measured_offsets` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn calibration_recommendation_accepts_large_stable_measured_offsets() {
    let report = SyncAnalysisReport {
        video_event_count: 16,
        audio_event_count: 16,
        paired_event_count: 16,
        coded_events: false,
        activity_start_delta_ms: -766.4,
        raw_first_video_activity_s: 7.491,
        raw_first_audio_activity_s: 6.725,
        first_skew_ms: -766.4,
        last_skew_ms: -769.2,
        mean_skew_ms: -756.0,
        median_skew_ms: -766.4,
        max_abs_skew_ms: 775.7,
        drift_ms: -2.8,
        skews_ms: vec![-766.4; 16],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let recommendation = report.calibration_recommendation();
    assert!(recommendation.ready);
    assert_eq!(recommendation.recommended_audio_offset_adjust_us, 766_400);
    assert!(recommendation.note.contains("move median skew"));
}

#[test]
/// Keeps `calibration_recommendation_uses_pairs_when_raw_activity_disagrees` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn calibration_recommendation_uses_pairs_when_raw_activity_disagrees() {
    let report = SyncAnalysisReport {
        video_event_count: 16,
        audio_event_count: 16,
        paired_event_count: 16,
        coded_events: false,
        activity_start_delta_ms: 6_735.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 6.735,
        first_skew_ms: -90.0,
        last_skew_ms: -100.0,
        mean_skew_ms: -95.0,
        median_skew_ms: -99.0,
        max_abs_skew_ms: 120.0,
        drift_ms: -10.0,
        skews_ms: vec![-99.0; 16],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let recommendation = report.calibration_recommendation();
    assert!(recommendation.ready);
    assert_eq!(recommendation.recommended_audio_offset_adjust_us, 99_000);
    assert!(recommendation.note.contains("paired pulses disagree"));
}

#[test]
/// Keeps `calibration_recommendation_uses_coded_pairs_when_raw_activity_disagrees` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn calibration_recommendation_uses_coded_pairs_when_raw_activity_disagrees() {
    let report = SyncAnalysisReport {
        video_event_count: 14,
        audio_event_count: 14,
        paired_event_count: 13,
        coded_events: true,
        activity_start_delta_ms: -3_620.7,
        raw_first_video_activity_s: 9.361,
        raw_first_audio_activity_s: 5.740,
        first_skew_ms: -270.0,
        last_skew_ms: -230.0,
        mean_skew_ms: -205.0,
        median_skew_ms: -188.4,
        max_abs_skew_ms: 273.8,
        drift_ms: 40.0,
        skews_ms: vec![-270.0, -240.0, -188.4, -175.0, -130.0],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let recommendation = report.calibration_recommendation();
    assert!(recommendation.ready);
    assert_eq!(recommendation.recommended_audio_offset_adjust_us, 188_400);
    assert!(recommendation.note.contains("paired pulses disagree"));
}

#[test]
/// Keeps `calibration_recommendation_rejects_raw_activity_that_disagrees_too_much` explicit because calibration must not bake in a delay from aliased cadence evidence.
/// Inputs are a stable paired report with non-coded raw activity far away from the median; output is a not-ready recommendation.
fn calibration_recommendation_rejects_raw_activity_that_disagrees_too_much() {
    let report = SyncAnalysisReport {
        video_event_count: 16,
        audio_event_count: 16,
        paired_event_count: 16,
        coded_events: false,
        activity_start_delta_ms: 2_000.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 2.0,
        first_skew_ms: 10.0,
        last_skew_ms: 12.0,
        mean_skew_ms: 11.0,
        median_skew_ms: 11.0,
        max_abs_skew_ms: 12.0,
        drift_ms: 2.0,
        skews_ms: vec![10.0, 11.0, 12.0],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let recommendation = report.calibration_recommendation();
    assert!(!recommendation.ready);
    assert!(recommendation.note.contains("calibration-safe band"));
}

#[test]
/// Keeps `calibration_recommendation_reports_when_skew_is_already_settled` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn calibration_recommendation_reports_when_skew_is_already_settled() {
    let report = SyncAnalysisReport {
        video_event_count: 14,
        audio_event_count: 14,
        paired_event_count: 14,
        coded_events: false,
        activity_start_delta_ms: 0.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 0.0,
        first_skew_ms: 3.0,
        last_skew_ms: 4.0,
        mean_skew_ms: 3.5,
        median_skew_ms: 4.0,
        max_abs_skew_ms: 4.0,
        drift_ms: 1.0,
        skews_ms: vec![3.0, 4.0],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let recommendation = report.calibration_recommendation();
    assert!(recommendation.ready);
    assert_eq!(recommendation.recommended_audio_offset_adjust_us, -4_000);
    assert!(recommendation.note.contains("already within the settled"));
}

#[test]
/// Keeps `verdict_passes_preferred_skew_band` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn verdict_passes_preferred_skew_band() {
    let report = SyncAnalysisReport {
        video_event_count: 5,
        audio_event_count: 5,
        paired_event_count: 5,
        coded_events: false,
        activity_start_delta_ms: 0.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 0.0,
        first_skew_ms: 10.0,
        last_skew_ms: 20.0,
        mean_skew_ms: 15.0,
        median_skew_ms: 15.0,
        max_abs_skew_ms: 20.0,
        drift_ms: 10.0,
        skews_ms: vec![10.0, 12.0, 15.0, 18.0, 20.0],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let verdict = report.verdict();
    assert!(verdict.passed);
    assert_eq!(verdict.status, "preferred");
}

#[test]
/// Keeps `verdict_requires_enough_pairs_before_trusting_skew` explicit because a few lucky pulse matches can hide a broken client transport train.
/// Inputs are fewer than the minimum paired events; output is an insufficient-data verdict.
fn verdict_requires_enough_pairs_before_trusting_skew() {
    let report = SyncAnalysisReport {
        video_event_count: 2,
        audio_event_count: 2,
        paired_event_count: 2,
        coded_events: true,
        activity_start_delta_ms: 0.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 0.0,
        first_skew_ms: 1.0,
        last_skew_ms: 2.0,
        mean_skew_ms: 1.5,
        median_skew_ms: 1.5,
        max_abs_skew_ms: 2.0,
        drift_ms: 1.0,
        skews_ms: vec![1.0, 2.0],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let verdict = report.verdict();
    assert!(!verdict.passed);
    assert_eq!(verdict.status, "insufficient_data");
}

#[test]
/// Keeps `verdict_passes_acceptable_skew_band` explicit because acceptable-but-not-preferred sync is still useful evidence while tuning transport.
/// Inputs are paired skews below the acceptable band; output is a passing acceptable verdict.
fn verdict_passes_acceptable_skew_band() {
    let report = SyncAnalysisReport {
        video_event_count: 5,
        audio_event_count: 5,
        paired_event_count: 5,
        coded_events: false,
        activity_start_delta_ms: 0.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 0.0,
        first_skew_ms: 45.0,
        last_skew_ms: 70.0,
        mean_skew_ms: 55.0,
        median_skew_ms: 55.0,
        max_abs_skew_ms: 70.0,
        drift_ms: 25.0,
        skews_ms: vec![45.0, 50.0, 55.0, 60.0, 70.0],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let verdict = report.verdict();
    assert!(verdict.passed);
    assert_eq!(verdict.status, "acceptable");
}

#[test]
/// Keeps `verdict_reports_gross_failure_between_acceptable_and_catastrophic` explicit because transport tuning needs a middle failure class before declaring total loss.
/// Inputs are paired skews beyond the acceptable band but below catastrophic; output is a gross-failure verdict.
fn verdict_reports_gross_failure_between_acceptable_and_catastrophic() {
    let report = SyncAnalysisReport {
        video_event_count: 5,
        audio_event_count: 5,
        paired_event_count: 5,
        coded_events: false,
        activity_start_delta_ms: 0.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 0.0,
        first_skew_ms: 90.0,
        last_skew_ms: 120.0,
        mean_skew_ms: 105.0,
        median_skew_ms: 105.0,
        max_abs_skew_ms: 120.0,
        drift_ms: 30.0,
        skews_ms: vec![90.0, 100.0, 105.0, 110.0, 120.0],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let verdict = report.verdict();
    assert!(!verdict.passed);
    assert_eq!(verdict.status, "gross_failure");
    assert!(verdict.reason.contains("acceptable band"));
}

#[test]
/// Keeps `verdict_flags_catastrophic_desync` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn verdict_flags_catastrophic_desync() {
    let report = SyncAnalysisReport {
        video_event_count: 5,
        audio_event_count: 5,
        paired_event_count: 5,
        coded_events: false,
        activity_start_delta_ms: 0.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 0.0,
        first_skew_ms: 8_000.0,
        last_skew_ms: 8_000.0,
        mean_skew_ms: 8_000.0,
        median_skew_ms: 8_000.0,
        max_abs_skew_ms: 8_000.0,
        drift_ms: 0.0,
        skews_ms: vec![8_000.0; 5],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let verdict = report.verdict();
    assert!(!verdict.passed);
    assert_eq!(verdict.status, "catastrophic_failure");
}

#[test]
/// Keeps `verdict_flags_catastrophic_activity_start_delta` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn verdict_flags_catastrophic_activity_start_delta() {
    let report = SyncAnalysisReport {
        video_event_count: 20,
        audio_event_count: 20,
        paired_event_count: 20,
        coded_events: false,
        activity_start_delta_ms: 20_000.0,
        raw_first_video_activity_s: 0.0,
        raw_first_audio_activity_s: 0.0,
        first_skew_ms: 900.0,
        last_skew_ms: 900.0,
        mean_skew_ms: 19_900.0,
        median_skew_ms: 19_900.0,
        max_abs_skew_ms: 900.0,
        drift_ms: 0.0,
        skews_ms: vec![900.0; 20],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let verdict = report.verdict();
    assert!(!verdict.passed);
    assert_eq!(verdict.status, "catastrophic_failure");
    assert!(verdict.reason.contains("activity starts"));
}

#[test]
/// Keeps `verdict_ignores_uncorroborated_raw_activity_for_coded_runs` explicit because it sits on sync-probe analysis, where small timestamp or pairing mistakes can hide real A/V skew.
/// Inputs are the typed parameters; output is the return value or side effect.
fn verdict_ignores_uncorroborated_raw_activity_for_coded_runs() {
    let report = SyncAnalysisReport {
        video_event_count: 20,
        audio_event_count: 20,
        paired_event_count: 20,
        coded_events: true,
        activity_start_delta_ms: -3_620.7,
        raw_first_video_activity_s: 9.361,
        raw_first_audio_activity_s: 5.740,
        first_skew_ms: -20.0,
        last_skew_ms: -18.0,
        mean_skew_ms: -19.0,
        median_skew_ms: -19.0,
        max_abs_skew_ms: 20.0,
        drift_ms: 2.0,
        skews_ms: vec![-20.0; 20],
        video_onsets_s: vec![],
        audio_onsets_s: vec![],
        paired_events: vec![],
    };

    let verdict = report.verdict();
    assert!(verdict.passed);
    assert_eq!(verdict.status, "preferred");
    assert!(verdict.reason.contains("raw activity start delta"));
}
