#![allow(
    clippy::assertions_on_constants,
    clippy::assertions_on_result_states,
    clippy::clone_on_copy,
    clippy::decimal_literal_representation,
    clippy::doc_markdown,
    clippy::empty_line_after_doc_comments,
    clippy::field_reassign_with_default,
    clippy::get_unwrap,
    clippy::identity_op,
    clippy::inconsistent_digit_grouping,
    clippy::indexing_slicing,
    clippy::integer_division,
    clippy::len_zero,
    clippy::let_underscore_must_use,
    clippy::manual_div_ceil,
    clippy::manual_let_else,
    clippy::manual_range_contains,
    clippy::modulo_arithmetic,
    clippy::needless_range_loop,
    clippy::non_ascii_literal,
    clippy::redundant_clone,
    clippy::shadow_reuse,
    clippy::shadow_same,
    clippy::shadow_unrelated,
    clippy::single_match_else,
    clippy::str_to_string,
    clippy::string_slice,
    clippy::tests_outside_test_module,
    clippy::too_many_lines,
    clippy::unnecessary_wraps,
    clippy::unseparated_literal_suffix,
    clippy::use_debug,
    clippy::useless_vec,
    clippy::wildcard_enum_match_arm,
    clippy::else_if_without_else,
    clippy::expect_used,
    clippy::missing_const_for_fn,
    clippy::similar_names,
    clippy::type_complexity,
    clippy::collapsible_else_if,
    clippy::doc_lazy_continuation,
    clippy::items_after_test_module,
    clippy::map_clone,
    clippy::multiple_unsafe_ops_per_block,
    clippy::unwrap_or_default,
    clippy::assign_op_pattern,
    clippy::needless_borrow,
    clippy::println_empty_string,
    clippy::unnecessary_cast,
    clippy::used_underscore_binding,
    clippy::create_dir,
    clippy::implicit_saturating_sub,
    clippy::exit,
    clippy::expect_fun_call,
    clippy::too_many_arguments,
    clippy::unnecessary_map_or,
    clippy::unwrap_used,
    dead_code,
    unused_imports,
    unused_variables,
    clippy::cloned_ref_to_slice_refs,
    clippy::neg_multiply,
    clippy::while_let_loop,
    clippy::bool_assert_comparison,
    clippy::excessive_precision,
    clippy::trivially_copy_pass_by_ref,
    clippy::op_ref,
    clippy::redundant_closure,
    clippy::unnecessary_lazy_evaluations,
    clippy::if_then_some_else_none,
    clippy::unnecessary_to_owned,
    clippy::single_component_path_imports,
)]
//! Comprehensive TDD Test Suite for Triple Barrier Labeling
//!
//! This test suite validates the triple barrier method implementation following
//! TDD methodology and MLFinLab research principles.
//!
//! ## Test Coverage
//! 1. **Profit Target Tests**: Upper barrier hit first
//! 2. **Stop Loss Tests**: Lower barrier hit first
//! 3. **Time Horizon Tests**: Expiry without barrier touch
//! 4. **Volatility-Based Barriers**: Dynamic barrier calculation
//! 5. **Edge Cases**: Gaps, extreme moves, simultaneous touches
//! 6. **Label Balance Tests**: Symmetric vs asymmetric barriers
//! 7. **Quality Score Tests**: Label quality metrics
//! 8. **Performance Tests**: <80μs latency target

use ml::labeling::{
    triple_barrier::{BarrierTracker, PricePoint, TripleBarrierEngine},
    types::{BarrierConfig, BarrierResult, BarrierTouchedFirst},
    utils,
};
use tracing::info;

// ============================================================================
// TEST 1: Profit Target Hit First (Upper Barrier)
// ============================================================================

#[test]
fn test_profit_target_hit_first() {
    // GIVEN: A tracker with 1% profit target, 0.5% stop loss
    let config = BarrierConfig::conservative(); // 100bps profit, 50bps stop
    let entry_price = 100.00; // $100.00
    let entry_price_cents = utils::price_to_cents(entry_price);
    let entry_timestamp_ns = 1692000000_000_000_000;

    let mut tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // WHEN: Price moves to $101.05 (above 1% profit target)
    let profit_price = utils::price_to_cents(101.05);
    let profit_timestamp = entry_timestamp_ns + 1_000_000_000; // +1 second
    let price_point = PricePoint::new(profit_price, profit_timestamp);

    // THEN: Label should be BUY (+1) with profit target result
    let result = tracker.update(price_point);
    assert!(result.is_some(), "Should return a label");

    let label = result.unwrap();
    assert_eq!(label.label_value, 1, "Should be BUY label");
    assert!(matches!(label.barrier_result, BarrierResult::ProfitTarget));
    assert!(label.return_bps > 0, "Return should be positive");
    assert!(label.is_profitable());
    assert_eq!(tracker.touched_first, Some(BarrierTouchedFirst::Upper));
}

#[test]
fn test_profit_target_exact_touch() {
    // GIVEN: A tracker with 1% profit target
    let config = BarrierConfig::conservative();
    let entry_price_cents = 10000; // $100.00
    let entry_timestamp_ns = 1692000000_000_000_000;

    let mut tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // WHEN: Price touches exactly the upper barrier ($101.00)
    let upper_barrier = tracker.upper_barrier_cents;
    let price_point = PricePoint::new(upper_barrier, entry_timestamp_ns + 500_000_000);

    // THEN: Should trigger profit target
    let result = tracker.update(price_point);
    assert!(result.is_some());

    let label = result.unwrap();
    assert_eq!(label.label_value, 1);
    assert!(matches!(label.barrier_result, BarrierResult::ProfitTarget));
}

#[test]
fn test_profit_target_gap_up() {
    // GIVEN: A tracker with 1% profit target
    let config = BarrierConfig::conservative();
    let entry_price_cents = 10000;
    let entry_timestamp_ns = 1692000000_000_000_000;

    let mut tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // WHEN: Price gaps up to $102.50 (far above profit target)
    let gap_price = utils::price_to_cents(102.50);
    let price_point = PricePoint::new(gap_price, entry_timestamp_ns + 100_000_000);

    // THEN: Should still trigger profit target (not miss due to gap)
    let result = tracker.update(price_point);
    assert!(result.is_some());

    let label = result.unwrap();
    assert_eq!(label.label_value, 1);
    assert!(label.return_bps > 100, "Return should be > 1%");
}

// ============================================================================
// TEST 2: Stop Loss Hit First (Lower Barrier)
// ============================================================================

#[test]
fn test_stop_loss_hit_first() {
    // GIVEN: A tracker with 0.5% stop loss
    let config = BarrierConfig::conservative();
    let entry_price = 100.00;
    let entry_price_cents = utils::price_to_cents(entry_price);
    let entry_timestamp_ns = 1692000000_000_000_000;

    let mut tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // WHEN: Price drops to $99.40 (below 0.5% stop loss)
    let stop_price = utils::price_to_cents(99.40);
    let stop_timestamp = entry_timestamp_ns + 2_000_000_000; // +2 seconds
    let price_point = PricePoint::new(stop_price, stop_timestamp);

    // THEN: Label should be SELL (-1) with stop loss result
    let result = tracker.update(price_point);
    assert!(result.is_some());

    let label = result.unwrap();
    assert_eq!(label.label_value, -1, "Should be SELL label");
    assert!(matches!(label.barrier_result, BarrierResult::StopLoss));
    assert!(label.return_bps < 0, "Return should be negative");
    assert!(!label.is_profitable());
    assert_eq!(tracker.touched_first, Some(BarrierTouchedFirst::Lower));
}

#[test]
fn test_stop_loss_exact_touch() {
    // GIVEN: A tracker with 0.5% stop loss
    let config = BarrierConfig::conservative();
    let entry_price_cents = 10000;
    let entry_timestamp_ns = 1692000000_000_000_000;

    let mut tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // WHEN: Price touches exactly the lower barrier ($99.50)
    let lower_barrier = tracker.lower_barrier_cents;
    let price_point = PricePoint::new(lower_barrier, entry_timestamp_ns + 1_000_000_000);

    // THEN: Should trigger stop loss
    let result = tracker.update(price_point);
    assert!(result.is_some());

    let label = result.unwrap();
    assert_eq!(label.label_value, -1);
    assert!(matches!(label.barrier_result, BarrierResult::StopLoss));
}

#[test]
fn test_stop_loss_gap_down() {
    // GIVEN: A tracker
    let config = BarrierConfig::conservative();
    let entry_price_cents = 10000;
    let entry_timestamp_ns = 1692000000_000_000_000;

    let mut tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // WHEN: Price gaps down to $97.00 (far below stop loss)
    let gap_price = utils::price_to_cents(97.00);
    let price_point = PricePoint::new(gap_price, entry_timestamp_ns + 50_000_000);

    // THEN: Should still trigger stop loss
    let result = tracker.update(price_point);
    assert!(result.is_some());

    let label = result.unwrap();
    assert_eq!(label.label_value, -1);
    assert!(label.return_bps < -50, "Return should be < -0.5%");
}

// ============================================================================
// TEST 3: Time Horizon Expiry (No Barrier Touch)
// ============================================================================

#[test]
fn test_time_expiry_no_barrier_touch() {
    // GIVEN: A tracker with 1-hour time horizon
    let config = BarrierConfig::conservative(); // 3600s = 1 hour
    let entry_price_cents = 10000;
    let entry_timestamp_ns = 1692000000_000_000_000;

    let mut tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // WHEN: Price stays at $100.30 (within barriers) until expiry
    let neutral_price = utils::price_to_cents(100.30);
    let expiry_timestamp = entry_timestamp_ns + 3700_000_000_000; // 1 hour + 100s
    let price_point = PricePoint::new(neutral_price, expiry_timestamp);

    // THEN: Label should be HOLD (0) or BUY (1) depending on return sign
    let result = tracker.update(price_point);
    assert!(result.is_some());

    let label = result.unwrap();
    assert!(matches!(label.barrier_result, BarrierResult::TimeExpiry));
    // Since price is above entry (100.30 > 100.00), label should be BUY (1)
    assert_eq!(label.label_value, 1, "Positive return at expiry → BUY");
    assert!(label.return_bps > 0);
}

#[test]
fn test_time_expiry_negative_return() {
    // GIVEN: A tracker
    let config = BarrierConfig::conservative();
    let entry_price_cents = 10000;
    let entry_timestamp_ns = 1692000000_000_000_000;

    let mut tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // WHEN: Price is at $99.70 (negative but within stop loss) at expiry
    let negative_price = utils::price_to_cents(99.70);
    let expiry_timestamp = entry_timestamp_ns + 3700_000_000_000;
    let price_point = PricePoint::new(negative_price, expiry_timestamp);

    // THEN: Label should be SELL (-1) due to negative return
    let result = tracker.update(price_point);
    assert!(result.is_some());

    let label = result.unwrap();
    assert!(matches!(label.barrier_result, BarrierResult::TimeExpiry));
    assert_eq!(label.label_value, -1, "Negative return at expiry → SELL");
    assert!(label.return_bps < 0);
}

#[test]
fn test_time_expiry_exactly_zero_return() {
    // GIVEN: A tracker
    let config = BarrierConfig::conservative();
    let entry_price_cents = 10000;
    let entry_timestamp_ns = 1692000000_000_000_000;

    let mut tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // WHEN: Price returns exactly to entry price at expiry
    let same_price = 10000;
    let expiry_timestamp = entry_timestamp_ns + 3700_000_000_000;
    let price_point = PricePoint::new(same_price, expiry_timestamp);

    // THEN: Label should be HOLD (0) due to zero return
    let result = tracker.update(price_point);
    assert!(result.is_some());

    let label = result.unwrap();
    assert!(matches!(label.barrier_result, BarrierResult::TimeExpiry));
    assert_eq!(label.label_value, 0, "Zero return at expiry → HOLD");
    assert_eq!(label.return_bps, 0);
}

// ============================================================================
// TEST 4: Barrier Calculation (Volatility-Based)
// ============================================================================

#[test]
fn test_barrier_calculation_conservative() {
    // GIVEN: Conservative config (1% profit, 0.5% stop)
    let config = BarrierConfig::conservative();
    let entry_price_cents = 10000; // $100.00
    let entry_timestamp_ns = 1692000000_000_000_000;

    // WHEN: Creating tracker
    let tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // THEN: Barriers should match expected values
    assert_eq!(
        tracker.upper_barrier_cents, 10100,
        "Upper barrier should be +1% = $101.00"
    );
    assert_eq!(
        tracker.lower_barrier_cents, 9950,
        "Lower barrier should be -0.5% = $99.50"
    );
    assert_eq!(
        tracker.expiry_timestamp_ns,
        entry_timestamp_ns + 3600_000_000_000,
        "Expiry should be 1 hour later"
    );
}

#[test]
fn test_barrier_calculation_asymmetric() {
    // GIVEN: Asymmetric config (2% profit, 1% stop)
    let config = BarrierConfig {
        profit_target_bps: 200,
        stop_loss_bps: 100,
        max_holding_period_ns: 1800_000_000_000, // 30 minutes
        min_return_threshold_bps: 10,
        use_sample_weights: true,
        volatility_lookback_periods: Some(20),
    };
    let entry_price_cents = 50000; // $500.00
    let entry_timestamp_ns = 1692000000_000_000_000;

    // WHEN: Creating tracker
    let tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // THEN: Barriers should match expected asymmetric values
    assert_eq!(
        tracker.upper_barrier_cents, 51000,
        "Upper barrier should be +2% = $510.00"
    );
    assert_eq!(
        tracker.lower_barrier_cents, 49500,
        "Lower barrier should be -1% = $495.00"
    );
}

#[test]
fn test_barrier_calculation_edge_case_low_price() {
    // GIVEN: Low price stock ($0.50)
    let config = BarrierConfig::conservative();
    let entry_price_cents = 50; // $0.50
    let entry_timestamp_ns = 1692000000_000_000_000;

    // WHEN: Creating tracker
    let tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // THEN: Barriers should still be calculated correctly
    // Upper: $0.50 * 1.01 = $0.505 (rounded to 50 cents due to integer math)
    // Lower: $0.50 * 0.995 = $0.4975 (rounded to 49 cents)
    assert!(
        tracker.upper_barrier_cents >= entry_price_cents,
        "Upper barrier should be >= entry"
    );
    assert!(
        tracker.lower_barrier_cents <= entry_price_cents,
        "Lower barrier should be <= entry"
    );
}

// ============================================================================
// TEST 5: Edge Cases
// ============================================================================

#[test]
fn test_multiple_updates_same_tracker() {
    // GIVEN: A tracker
    let config = BarrierConfig::conservative();
    let mut tracker = BarrierTracker::new(10000, 1692000000_000_000_000, config);

    // WHEN: Multiple price updates within barriers
    let price1 = PricePoint::new(10020, 1692000000_000_000_000 + 100_000_000);
    let price2 = PricePoint::new(10040, 1692000000_000_000_000 + 200_000_000);
    let price3 = PricePoint::new(10060, 1692000000_000_000_000 + 300_000_000);

    // THEN: Should return None until barrier is touched
    assert!(tracker.update(price1).is_none());
    assert!(tracker.update(price2).is_none());
    assert!(tracker.update(price3).is_none());
    assert!(!tracker.is_closed());

    // AND WHEN: Final price hits profit target
    let price_final = PricePoint::new(10150, 1692000000_000_000_000 + 400_000_000);
    let result = tracker.update(price_final);

    // THEN: Should return label
    assert!(result.is_some());
    assert!(tracker.is_closed());
}

#[test]
fn test_tracker_closed_after_barrier_touch() {
    // GIVEN: A tracker that hit profit target
    let config = BarrierConfig::conservative();
    let mut tracker = BarrierTracker::new(10000, 1692000000_000_000_000, config);

    let profit_price = PricePoint::new(10150, 1692000000_000_000_000 + 1_000_000_000);
    let _label = tracker.update(profit_price);

    assert!(tracker.is_closed());

    // WHEN: Trying to update again
    let new_price = PricePoint::new(10200, 1692000000_000_000_000 + 2_000_000_000);
    let result = tracker.update(new_price);

    // THEN: Should return None (tracker is closed)
    assert!(result.is_none());
}

#[test]
fn test_extreme_volatility_scenario() {
    // GIVEN: A tracker with tight barriers
    let config = BarrierConfig {
        profit_target_bps: 10,                 // 0.1%
        stop_loss_bps: 10,                     // 0.1%
        max_holding_period_ns: 60_000_000_000, // 1 minute
        min_return_threshold_bps: 1,
        use_sample_weights: true,
        volatility_lookback_periods: Some(5),
    };
    let entry_price_cents = 10000;
    let entry_timestamp_ns = 1692000000_000_000_000;

    let mut tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // WHEN: Price moves very quickly to profit target
    let fast_profit = PricePoint::new(10011, entry_timestamp_ns + 1_000_000); // 1ms later
    let result = tracker.update(fast_profit);

    // THEN: Should still capture the profit
    assert!(result.is_some());
    let label = result.unwrap();
    assert_eq!(label.label_value, 1);
}

#[test]
fn test_price_oscillation_around_entry() {
    // GIVEN: A tracker
    let config = BarrierConfig::conservative();
    let mut tracker = BarrierTracker::new(10000, 1692000000_000_000_000, config);

    // WHEN: Price oscillates but stays within barriers
    let prices = vec![
        PricePoint::new(10030, 1692000000_000_000_000 + 100_000_000),
        PricePoint::new(9970, 1692000000_000_000_000 + 200_000_000),
        PricePoint::new(10020, 1692000000_000_000_000 + 300_000_000),
        PricePoint::new(9980, 1692000000_000_000_000 + 400_000_000),
    ];

    // THEN: No labels should be generated
    for price in prices {
        let result = tracker.update(price);
        assert!(result.is_none());
    }
}

// ============================================================================
// TEST 6: Label Balance (Symmetric vs Asymmetric Barriers)
// ============================================================================

#[test]
fn test_symmetric_barriers_balance() {
    // GIVEN: Symmetric barriers (equal profit and stop)
    let config = BarrierConfig {
        profit_target_bps: 100,
        stop_loss_bps: 100,
        max_holding_period_ns: 3600_000_000_000,
        min_return_threshold_bps: 10,
        use_sample_weights: true,
        volatility_lookback_periods: Some(20),
    };

    // WHEN: Creating tracker
    let entry_price_cents = 10000;
    let entry_timestamp_ns = 1692000000_000_000_000;
    let tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // THEN: Upper and lower barriers should be equidistant from entry
    let upper_distance = tracker.upper_barrier_cents - entry_price_cents;
    let lower_distance = entry_price_cents - tracker.lower_barrier_cents;

    assert_eq!(
        upper_distance, lower_distance,
        "Symmetric barriers should have equal distance"
    );
}

#[test]
fn test_asymmetric_barriers_reduce_false_positives() {
    // GIVEN: Asymmetric barriers (profit > stop)
    let config = BarrierConfig {
        profit_target_bps: 200, // 2x the stop loss
        stop_loss_bps: 100,
        max_holding_period_ns: 3600_000_000_000,
        min_return_threshold_bps: 10,
        use_sample_weights: true,
        volatility_lookback_periods: Some(20),
    };

    let entry_price_cents = 10000;
    let entry_timestamp_ns = 1692000000_000_000_000;
    let tracker = BarrierTracker::new(entry_price_cents, entry_timestamp_ns, config);

    // THEN: Profit barrier should be farther from entry than stop loss
    let upper_distance = tracker.upper_barrier_cents - entry_price_cents;
    let lower_distance = entry_price_cents - tracker.lower_barrier_cents;

    assert!(
        upper_distance > lower_distance,
        "Asymmetric barriers: profit target further than stop loss"
    );
    assert_eq!(upper_distance, lower_distance * 2);
}

// ============================================================================
// TEST 7: Quality Score Validation
// ============================================================================

#[test]
fn test_quality_score_profit_target() {
    // GIVEN: A tracker that hits profit target
    let config = BarrierConfig::conservative();
    let mut tracker = BarrierTracker::new(10000, 1692000000_000_000_000, config);

    // WHEN: Hitting profit target
    let profit_price = PricePoint::new(10150, 1692000000_000_000_000 + 1_000_000_000);
    let result = tracker.update(profit_price);

    // THEN: Quality score should be high (0.9)
    assert!(result.is_some());
    let label = result.unwrap();
    assert!(
        label.quality_score >= 0.85,
        "Profit targets should have high quality score"
    );
}

#[test]
fn test_quality_score_stop_loss() {
    // GIVEN: A tracker that hits stop loss
    let config = BarrierConfig::conservative();
    let mut tracker = BarrierTracker::new(10000, 1692000000_000_000_000, config);

    // WHEN: Hitting stop loss
    let stop_price = PricePoint::new(9940, 1692000000_000_000_000 + 1_000_000_000);
    let result = tracker.update(stop_price);

    // THEN: Quality score should be moderate (0.8)
    assert!(result.is_some());
    let label = result.unwrap();
    assert!(
        label.quality_score >= 0.75,
        "Stop losses should have moderate quality score"
    );
}

#[test]
fn test_quality_score_time_expiry() {
    // GIVEN: A tracker that expires
    let config = BarrierConfig::conservative();
    let mut tracker = BarrierTracker::new(10000, 1692000000_000_000_000, config);

    // WHEN: Time expiry with neutral price
    let neutral_price = PricePoint::new(10020, 1692000000_000_000_000 + 3700_000_000_000);
    let result = tracker.update(neutral_price);

    // THEN: Quality score should be low (0.5)
    assert!(result.is_some());
    let label = result.unwrap();
    assert!(
        label.quality_score <= 0.6,
        "Time expiry should have lower quality score"
    );
}

// ============================================================================
// TEST 8: Engine Multi-Tracker Tests
// ============================================================================

#[test]
fn test_engine_start_tracking() {
    // GIVEN: An empty engine
    let mut engine = TripleBarrierEngine::new(1000);
    assert_eq!(engine.active_count(), 0);

    // WHEN: Starting new tracker
    let config = BarrierConfig::conservative();
    let result = engine.start_tracking(config, 10000, 1692000000_000_000_000);

    // THEN: Should return tracker ID and increment count
    assert!(result.is_ok());
    assert_eq!(engine.active_count(), 1);
}

#[test]
fn test_engine_max_active_trackers() {
    // GIVEN: An engine with max 2 trackers
    let mut engine = TripleBarrierEngine::new(2);

    // WHEN: Starting 2 trackers (should succeed)
    let config = BarrierConfig::conservative();
    let r1 = engine.start_tracking(config.clone(), 10000, 1692000000_000_000_000);
    let r2 = engine.start_tracking(config.clone(), 10100, 1692000000_000_000_000);

    assert!(r1.is_ok());
    assert!(r2.is_ok());
    assert_eq!(engine.active_count(), 2);

    // WHEN: Starting 3rd tracker (should fail)
    let r3 = engine.start_tracking(config, 10200, 1692000000_000_000_000);

    // THEN: Should return error
    assert!(r3.is_err());
    assert_eq!(engine.active_count(), 2);
}

#[test]
fn test_engine_update_all() {
    // GIVEN: Engine with multiple trackers at different entry prices
    let mut engine = TripleBarrierEngine::new(100);
    let config = BarrierConfig::conservative();

    // Start 5 trackers with varying entry prices
    for i in 0..5 {
        let entry_price = 10000 + i * 100;
        let _ = engine.start_tracking(config.clone(), entry_price, 1692000000_000_000_000);
    }

    assert_eq!(engine.active_count(), 5);

    // WHEN: Price moves to a level that hits some profit targets
    let price_point = PricePoint::new(10200, 1692000000_000_000_000 + 1_000_000_000);
    let labels = engine.update_all(price_point);

    // THEN: Some trackers should close (those with profit targets hit)
    assert!(labels.len() > 0, "Should generate some labels");
    assert!(engine.active_count() < 5, "Some trackers should be closed");
    assert_eq!(engine.completed_count() as usize, labels.len());
}

#[test]
fn test_engine_expire_old_trackers() {
    // GIVEN: Engine with trackers at entry time T0
    let mut engine = TripleBarrierEngine::new(100);
    let config = BarrierConfig::conservative(); // 1 hour max holding
    let entry_timestamp = 1692000000_000_000_000;

    // Start 3 trackers
    for i in 0..3 {
        let entry_price = 10000 + i * 100;
        let _ = engine.start_tracking(config.clone(), entry_price, entry_timestamp);
    }

    // WHEN: Forcing expiry at T0 + 2 hours (past 1-hour limit)
    let expiry_timestamp = entry_timestamp + 7200_000_000_000;
    let expired_labels = engine.expire_old_trackers(expiry_timestamp);

    // THEN: All 3 trackers should be expired
    assert_eq!(expired_labels.len(), 3, "All trackers should expire");
    assert_eq!(engine.active_count(), 0, "No active trackers left");

    // All labels should be time expiry
    for label in &expired_labels {
        assert!(matches!(label.barrier_result, BarrierResult::TimeExpiry));
    }
}

#[test]
fn test_engine_drain_completed_labels() {
    // GIVEN: Engine with completed labels
    let mut engine = TripleBarrierEngine::new(100);
    let config = BarrierConfig::conservative();

    // Start and complete a tracker
    let _ = engine.start_tracking(config, 10000, 1692000000_000_000_000);
    let price_point = PricePoint::new(10150, 1692000000_000_000_000 + 1_000_000_000);
    let _labels = engine.update_all(price_point);

    // WHEN: Draining completed labels
    let drained = engine.drain_completed_labels();

    // THEN: Should return labels and clear internal buffer
    assert_eq!(drained.len(), 1);

    // Draining again should return empty
    let drained_again = engine.drain_completed_labels();
    assert_eq!(drained_again.len(), 0);
}

#[test]
fn test_engine_get_tracker() {
    // GIVEN: Engine with a tracker
    let mut engine = TripleBarrierEngine::new(100);
    let config = BarrierConfig::conservative();
    let tracker_id = engine
        .start_tracking(config, 10000, 1692000000_000_000_000)
        .unwrap();

    // WHEN: Getting tracker by ID
    let tracker = engine.get_tracker(&tracker_id);

    // THEN: Should return the tracker
    assert!(tracker.is_some());
    let tracker = tracker.unwrap();
    assert_eq!(tracker.entry_price_cents, 10000);
    assert!(!tracker.is_closed());
}

#[test]
fn test_engine_clear() {
    // GIVEN: Engine with multiple active trackers
    let mut engine = TripleBarrierEngine::new(100);
    let config = BarrierConfig::conservative();

    for i in 0..5 {
        let _ = engine.start_tracking(config.clone(), 10000 + i * 100, 1692000000_000_000_000);
    }

    // WHEN: Clearing the engine
    engine.clear();

    // THEN: All trackers and stats should be reset
    assert_eq!(engine.active_count(), 0);
    assert_eq!(engine.completed_count(), 0);
}

// ============================================================================
// TEST 9: Performance Tests (<80μs latency target)
// ============================================================================

#[test]
fn test_latency_single_update() {
    use std::time::Instant;

    // GIVEN: A tracker
    let config = BarrierConfig::conservative();
    let mut tracker = BarrierTracker::new(10000, 1692000000_000_000_000, config);

    // WHEN: Updating with a price point
    let price_point = PricePoint::new(10150, 1692000000_000_000_000 + 1_000_000_000);
    let start = Instant::now();
    let result = tracker.update(price_point);
    let elapsed_us = start.elapsed().as_micros();

    // THEN: Should complete in <80μs
    assert!(result.is_some());
    assert!(
        elapsed_us < 80,
        "Single update should be <80μs, got {}μs",
        elapsed_us
    );
}

#[test]
fn test_latency_engine_update_all() {
    use std::time::Instant;

    // GIVEN: Engine with 100 active trackers
    let mut engine = TripleBarrierEngine::new(1000);
    let config = BarrierConfig::conservative();

    for i in 0..100 {
        let _ = engine.start_tracking(
            config.clone(),
            10000 + (i % 20) * 10,
            1692000000_000_000_000,
        );
    }

    // WHEN: Updating all trackers
    let price_point = PricePoint::new(10150, 1692000000_000_000_000 + 1_000_000_000);
    let start = Instant::now();
    let _labels = engine.update_all(price_point);
    let elapsed_us = start.elapsed().as_micros();

    // THEN: Should complete in reasonable time (<10ms for 100 trackers)
    assert!(
        elapsed_us < 10_000,
        "Update 100 trackers should be <10ms, got {}μs",
        elapsed_us
    );
}

#[test]
fn test_throughput_batch_processing() {
    use std::time::Instant;

    // GIVEN: Engine with many trackers
    let mut engine = TripleBarrierEngine::new(10_000);
    let config = BarrierConfig::conservative();

    // Start 1000 trackers
    for i in 0..1000 {
        let _ = engine.start_tracking(
            config.clone(),
            10000 + (i % 50) * 10,
            1692000000_000_000_000 + i * 1_000_000,
        );
    }

    // WHEN: Processing 100 price updates
    let start = Instant::now();
    let mut total_labels = 0;

    for i in 0..100 {
        let price = 10000 + (i % 300);
        let timestamp = 1692000000_000_000_000 + i * 10_000_000;
        let price_point = PricePoint::new(price, timestamp);
        let labels = engine.update_all(price_point);
        total_labels += labels.len();
    }

    let elapsed_ms = start.elapsed().as_millis();
    let throughput = (total_labels as f64 / elapsed_ms as f64) * 1000.0;

    // THEN: Should achieve >10K labels/second
    info!(
        total_labels,
        elapsed_ms,
        labels_per_sec = format_args!("{:.0}", throughput),
        "Processed labels"
    );
    assert!(
        throughput > 10_000.0,
        "Should achieve >10K labels/sec, got {:.0}",
        throughput
    );
}

// ============================================================================
// TEST 10: Integration Tests (Real-World Scenarios)
// ============================================================================

#[test]
fn test_realistic_trading_scenario() {
    // GIVEN: A realistic trading scenario with ES futures
    let config = BarrierConfig {
        profit_target_bps: 50,                  // 0.5% profit target (realistic for ES)
        stop_loss_bps: 25,                      // 0.25% stop loss (2:1 risk-reward)
        max_holding_period_ns: 900_000_000_000, // 15 minutes
        min_return_threshold_bps: 5,
        use_sample_weights: true,
        volatility_lookback_periods: Some(20),
    };

    let mut engine = TripleBarrierEngine::new(1000);
    let entry_price = 475000; // ES at $4,750.00
    let entry_timestamp = 1692000000_000_000_000;

    // WHEN: Starting position
    let tracker_id = engine
        .start_tracking(config, entry_price, entry_timestamp)
        .unwrap();

    // Simulate price movement over 5 minutes (profit scenario)
    let price_updates = vec![
        (475100, entry_timestamp + 60_000_000_000), // +1 min: $4,751
        (475200, entry_timestamp + 120_000_000_000), // +2 min: $4,752
        (475300, entry_timestamp + 180_000_000_000), // +3 min: $4,753
        (475400, entry_timestamp + 240_000_000_000), // +4 min: $4,754
        (477500, entry_timestamp + 300_000_000_000), // +5 min: $4,775 (hit profit)
    ];

    let mut final_label = None;
    for (price, timestamp) in price_updates {
        let price_point = PricePoint::new(price, timestamp);
        if let Some(label) = engine.update_tracker(tracker_id, price_point) {
            final_label = Some(label);
            break;
        }
    }

    // THEN: Should hit profit target
    assert!(final_label.is_some());
    let label = final_label.unwrap();
    assert_eq!(label.label_value, 1);
    assert!(matches!(label.barrier_result, BarrierResult::ProfitTarget));
    assert!(label.return_bps >= 50); // At least 0.5% return
}

#[test]
fn test_config_validation() {
    // GIVEN: Invalid config (stop loss >= profit target)
    let invalid_config = BarrierConfig {
        profit_target_bps: 50,
        stop_loss_bps: 100, // Greater than profit target
        max_holding_period_ns: 3600_000_000_000,
        min_return_threshold_bps: 10,
        use_sample_weights: true,
        volatility_lookback_periods: Some(20),
    };

    // WHEN: Validating config
    let result = invalid_config.validate();

    // THEN: Should return error
    assert!(result.is_err());
}

#[test]
fn test_return_calculation_accuracy() {
    // GIVEN: A tracker
    let config = BarrierConfig::conservative();
    let entry_price_cents = 10000; // $100.00
    let mut tracker = BarrierTracker::new(entry_price_cents, 1692000000_000_000_000, config);

    // WHEN: Price moves to $102.50 (exactly +2.5%)
    let profit_price = 10250;
    let price_point = PricePoint::new(profit_price, 1692000000_000_000_000 + 1_000_000_000);
    let result = tracker.update(price_point);

    // THEN: Return should be exactly 250 bps (2.5%)
    assert!(result.is_some());
    let label = result.unwrap();
    assert_eq!(label.return_bps, 250, "Return should be exactly 2.5%");
    assert!(
        (label.return_as_ratio() - 0.025).abs() < 1e-10,
        "Return ratio should be 0.025"
    );
}
