//! Edge case tests for gap detection (Issue #257, Phase 4f).

use std::sync::Arc;
use tokio::sync::mpsc;

use opendeviationbar_streaming::gap::{GapEvent, TradeIdGapDetector};

#[test]
fn test_single_trade_gap() {
    // Gap of exactly 1 missing trade (gap_size == 1)
    let (tx, mut rx) = mpsc::channel::<GapEvent>(16);
    let mut detector = TradeIdGapDetector::new(Arc::from("BTCUSDT"), Some(tx));

    detector.seed(100);
    let result = detector.check(102); // Trade 101 missing

    assert!(result.is_some());
    let (last_id, gap_size) = result.unwrap();
    assert_eq!(last_id, 100);
    assert_eq!(gap_size, 1);

    let event = rx.try_recv().unwrap();
    assert_eq!(event.gap_size, 1);
    assert_eq!(event.expected_tid, 101);
    assert_eq!(event.actual_tid, 102);
}

#[test]
fn test_overlap_not_detected_as_gap() {
    // When incoming trade ID is <= last known, no gap should be detected
    let (tx, mut rx) = mpsc::channel::<GapEvent>(16);
    let mut detector = TradeIdGapDetector::new(Arc::from("BTCUSDT"), Some(tx));

    detector.seed(100);
    // Duplicate or out-of-order trade
    assert!(detector.check(100).is_none());
    assert!(detector.check(99).is_none());

    assert!(rx.try_recv().is_err(), "no events for overlaps");
}

#[test]
fn test_multi_symbol_concurrent_gaps() {
    // Multiple symbols can have gaps simultaneously — shared channel
    let (tx, mut rx) = mpsc::channel::<GapEvent>(32);

    let symbols = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT"];
    let mut detectors: Vec<_> = symbols
        .iter()
        .map(|s| TradeIdGapDetector::new(Arc::from(*s), Some(tx.clone())))
        .collect();

    // Seed all
    for (i, d) in detectors.iter_mut().enumerate() {
        d.seed((i as i64 + 1) * 1000);
    }

    // Create gaps in all 4 simultaneously
    for (i, d) in detectors.iter_mut().enumerate() {
        let gap_target = (i as i64 + 1) * 1000 + 50;
        assert!(d.check(gap_target).is_some());
    }

    // Should have 4 events, one per symbol
    let mut received_symbols = Vec::new();
    while let Ok(event) = rx.try_recv() {
        received_symbols.push(event.symbol.to_string());
    }
    assert_eq!(received_symbols.len(), 4);
    for s in symbols {
        assert!(
            received_symbols.contains(&s.to_string()),
            "missing event for {s}"
        );
    }
}

#[test]
fn test_large_gap_size_calculation() {
    // Verify correct gap size for large gaps (>100K)
    let mut detector = TradeIdGapDetector::new(Arc::from("BTCUSDT"), None);

    detector.seed(1_000_000);
    let result = detector.check(1_200_000);

    assert!(result.is_some());
    let (_, gap_size) = result.unwrap();
    assert_eq!(gap_size, 199_999); // 1_200_000 - 1_000_000 - 1
}

#[test]
fn test_detector_unseeded_first_trade() {
    // First trade without seed should not trigger gap
    let (tx, mut rx) = mpsc::channel::<GapEvent>(16);
    let mut detector = TradeIdGapDetector::new(Arc::from("BTCUSDT"), Some(tx));

    // No seed — first trade just establishes baseline
    assert!(detector.check(5000).is_none());

    // No event emitted
    assert!(rx.try_recv().is_err());

    // Next contiguous trade also no gap
    detector.update_last_tid(5000);
    assert!(detector.check(5001).is_none());
    assert!(rx.try_recv().is_err());
}
