//! Golden snapshot regression test for oracle bit-exact verification
//!
//! Issue #300: Captures the EXACT output of the current codebase before any
//! memory refactoring changes. All subsequent refactoring plans must prove
//! output identity against these golden fixtures.
//!
//! Two modes:
//! - Generate: `cargo nextest run -p opendeviationbar-core --features test-utils,generate-golden test_golden_snapshot_bit_exact`
//! - Verify:   `cargo nextest run -p opendeviationbar-core --features test-utils test_golden_snapshot_bit_exact`

use opendeviationbar_core::interbar::{InterBarConfig, LookbackMode};
use opendeviationbar_core::processor::OpenDeviationBarProcessor;
use opendeviationbar_core::types::OpenDeviationBar;
use serde::{Deserialize, Serialize};

/// Golden bar representation for bit-exact serialization.
/// Uses raw integer values for FixedPoint fields to avoid f64 round-trip issues.
#[derive(Debug, Serialize, Deserialize)]
struct GoldenBar {
    // OHLCV core (raw FixedPoint i64 values)
    open_raw: i64,
    high_raw: i64,
    low_raw: i64,
    close_raw: i64,
    vwap_raw: i64,

    // Volume accumulators (i128 as strings for JSON compatibility)
    volume_raw: String,
    turnover_raw: String,
    buy_volume_raw: String,
    sell_volume_raw: String,
    buy_turnover_raw: String,
    sell_turnover_raw: String,

    // Timestamps
    open_time: i64,
    close_time: i64,

    // Trade tracking
    first_trade_id: i64,
    last_trade_id: i64,
    first_agg_trade_id: i64,
    last_agg_trade_id: i64,
    individual_trade_count: u32,
    agg_record_count: u32,
    buy_trade_count: u32,
    sell_trade_count: u32,

    // Orphan marker (set for the bar from reset_at_ouroboros)
    is_orphan: bool,

    // Intra-bar microstructure features (10 features)
    duration_us: i64,
    ofi: f64,
    vwap_close_deviation: f64,
    price_impact: f64,
    kyle_lambda_proxy: f64,
    trade_intensity: f64,
    volume_per_trade: f64,
    aggression_ratio: f64,
    aggregation_density_f64: f64,
    turnover_imbalance: f64,

    // Inter-bar features (16 features, all Option)
    lookback_trade_count: Option<u32>,
    lookback_ofi: Option<f64>,
    lookback_duration_us: Option<i64>,
    lookback_intensity: Option<f64>,
    lookback_vwap_raw: Option<i64>,
    lookback_vwap_position: Option<f64>,
    lookback_count_imbalance: Option<f64>,
    lookback_kyle_lambda: Option<f64>,
    lookback_burstiness: Option<f64>,
    lookback_volume_skew: Option<f64>,
    lookback_volume_kurt: Option<f64>,
    lookback_price_range: Option<f64>,
    lookback_kaufman_er: Option<f64>,
    lookback_garman_klass_vol: Option<f64>,
    lookback_hurst: Option<f64>,
    lookback_permutation_entropy: Option<f64>,

    // Intra-bar features (22 features, all Option)
    intra_bull_epoch_density: Option<f64>,
    intra_bear_epoch_density: Option<f64>,
    intra_bull_excess_gain: Option<f64>,
    intra_bear_excess_gain: Option<f64>,
    intra_bull_cv: Option<f64>,
    intra_bear_cv: Option<f64>,
    intra_max_drawdown: Option<f64>,
    intra_max_runup: Option<f64>,
    intra_trade_count: Option<u32>,
    intra_ofi: Option<f64>,
    intra_duration_us: Option<i64>,
    intra_intensity: Option<f64>,
    intra_vwap_position: Option<f64>,
    intra_count_imbalance: Option<f64>,
    intra_kyle_lambda: Option<f64>,
    intra_burstiness: Option<f64>,
    intra_volume_skew: Option<f64>,
    intra_volume_kurt: Option<f64>,
    intra_kaufman_er: Option<f64>,
    intra_garman_klass_vol: Option<f64>,
    intra_hurst: Option<f64>,
    intra_permutation_entropy: Option<f64>,
}

impl GoldenBar {
    fn from_odb(bar: &OpenDeviationBar, is_orphan: bool) -> Self {
        Self {
            open_raw: bar.open.0,
            high_raw: bar.high.0,
            low_raw: bar.low.0,
            close_raw: bar.close.0,
            vwap_raw: bar.vwap.0,
            volume_raw: bar.volume.to_string(),
            turnover_raw: bar.turnover.to_string(),
            buy_volume_raw: bar.buy_volume.to_string(),
            sell_volume_raw: bar.sell_volume.to_string(),
            buy_turnover_raw: bar.buy_turnover.to_string(),
            sell_turnover_raw: bar.sell_turnover.to_string(),
            open_time: bar.open_time,
            close_time: bar.close_time,
            first_trade_id: bar.first_trade_id,
            last_trade_id: bar.last_trade_id,
            first_agg_trade_id: bar.first_agg_trade_id,
            last_agg_trade_id: bar.last_agg_trade_id,
            individual_trade_count: bar.individual_trade_count,
            agg_record_count: bar.agg_record_count,
            buy_trade_count: bar.buy_trade_count,
            sell_trade_count: bar.sell_trade_count,
            is_orphan,
            duration_us: bar.duration_us,
            ofi: bar.ofi,
            vwap_close_deviation: bar.vwap_close_deviation,
            price_impact: bar.price_impact,
            kyle_lambda_proxy: bar.kyle_lambda_proxy,
            trade_intensity: bar.trade_intensity,
            volume_per_trade: bar.volume_per_trade,
            aggression_ratio: bar.aggression_ratio,
            aggregation_density_f64: bar.aggregation_density_f64,
            turnover_imbalance: bar.turnover_imbalance,
            lookback_trade_count: bar.lookback_trade_count,
            lookback_ofi: bar.lookback_ofi,
            lookback_duration_us: bar.lookback_duration_us,
            lookback_intensity: bar.lookback_intensity,
            lookback_vwap_raw: bar.lookback_vwap_raw,
            lookback_vwap_position: bar.lookback_vwap_position,
            lookback_count_imbalance: bar.lookback_count_imbalance,
            lookback_kyle_lambda: bar.lookback_kyle_lambda,
            lookback_burstiness: bar.lookback_burstiness,
            lookback_volume_skew: bar.lookback_volume_skew,
            lookback_volume_kurt: bar.lookback_volume_kurt,
            lookback_price_range: bar.lookback_price_range,
            lookback_kaufman_er: bar.lookback_kaufman_er,
            lookback_garman_klass_vol: bar.lookback_garman_klass_vol,
            lookback_hurst: bar.lookback_hurst,
            lookback_permutation_entropy: bar.lookback_permutation_entropy,
            intra_bull_epoch_density: bar.intra_bull_epoch_density,
            intra_bear_epoch_density: bar.intra_bear_epoch_density,
            intra_bull_excess_gain: bar.intra_bull_excess_gain,
            intra_bear_excess_gain: bar.intra_bear_excess_gain,
            intra_bull_cv: bar.intra_bull_cv,
            intra_bear_cv: bar.intra_bear_cv,
            intra_max_drawdown: bar.intra_max_drawdown,
            intra_max_runup: bar.intra_max_runup,
            intra_trade_count: bar.intra_trade_count,
            intra_ofi: bar.intra_ofi,
            intra_duration_us: bar.intra_duration_us,
            intra_intensity: bar.intra_intensity,
            intra_vwap_position: bar.intra_vwap_position,
            intra_count_imbalance: bar.intra_count_imbalance,
            intra_kyle_lambda: bar.intra_kyle_lambda,
            intra_burstiness: bar.intra_burstiness,
            intra_volume_skew: bar.intra_volume_skew,
            intra_volume_kurt: bar.intra_volume_kurt,
            intra_kaufman_er: bar.intra_kaufman_er,
            intra_garman_klass_vol: bar.intra_garman_klass_vol,
            intra_hurst: bar.intra_hurst,
            intra_permutation_entropy: bar.intra_permutation_entropy,
        }
    }

    /// Assert this golden bar matches an actual bar exactly.
    /// FixedPoint fields: integer-exact. f64 fields: epsilon comparison (NaN == NaN).
    fn assert_matches(&self, other: &GoldenBar, index: usize) {
        // Integer-exact fields
        assert_eq!(
            self.open_raw, other.open_raw,
            "bar[{index}] open_raw mismatch"
        );
        assert_eq!(
            self.high_raw, other.high_raw,
            "bar[{index}] high_raw mismatch"
        );
        assert_eq!(self.low_raw, other.low_raw, "bar[{index}] low_raw mismatch");
        assert_eq!(
            self.close_raw, other.close_raw,
            "bar[{index}] close_raw mismatch"
        );
        assert_eq!(
            self.vwap_raw, other.vwap_raw,
            "bar[{index}] vwap_raw mismatch"
        );
        assert_eq!(
            self.volume_raw, other.volume_raw,
            "bar[{index}] volume_raw mismatch"
        );
        assert_eq!(
            self.turnover_raw, other.turnover_raw,
            "bar[{index}] turnover_raw mismatch"
        );
        assert_eq!(
            self.buy_volume_raw, other.buy_volume_raw,
            "bar[{index}] buy_volume_raw mismatch"
        );
        assert_eq!(
            self.sell_volume_raw, other.sell_volume_raw,
            "bar[{index}] sell_volume_raw mismatch"
        );
        assert_eq!(
            self.buy_turnover_raw, other.buy_turnover_raw,
            "bar[{index}] buy_turnover_raw mismatch"
        );
        assert_eq!(
            self.sell_turnover_raw, other.sell_turnover_raw,
            "bar[{index}] sell_turnover_raw mismatch"
        );
        assert_eq!(
            self.open_time, other.open_time,
            "bar[{index}] open_time mismatch"
        );
        assert_eq!(
            self.close_time, other.close_time,
            "bar[{index}] close_time mismatch"
        );
        assert_eq!(
            self.first_trade_id, other.first_trade_id,
            "bar[{index}] first_trade_id mismatch"
        );
        assert_eq!(
            self.last_trade_id, other.last_trade_id,
            "bar[{index}] last_trade_id mismatch"
        );
        assert_eq!(
            self.first_agg_trade_id, other.first_agg_trade_id,
            "bar[{index}] first_agg_trade_id mismatch"
        );
        assert_eq!(
            self.last_agg_trade_id, other.last_agg_trade_id,
            "bar[{index}] last_agg_trade_id mismatch"
        );
        assert_eq!(
            self.individual_trade_count, other.individual_trade_count,
            "bar[{index}] individual_trade_count mismatch"
        );
        assert_eq!(
            self.agg_record_count, other.agg_record_count,
            "bar[{index}] agg_record_count mismatch"
        );
        assert_eq!(
            self.buy_trade_count, other.buy_trade_count,
            "bar[{index}] buy_trade_count mismatch"
        );
        assert_eq!(
            self.sell_trade_count, other.sell_trade_count,
            "bar[{index}] sell_trade_count mismatch"
        );
        assert_eq!(
            self.is_orphan, other.is_orphan,
            "bar[{index}] is_orphan mismatch"
        );
        assert_eq!(
            self.duration_us, other.duration_us,
            "bar[{index}] duration_us mismatch"
        );

        // f64 microstructure features: NaN-safe epsilon comparison
        assert_f64_eq(self.ofi, other.ofi, index, "ofi");
        assert_f64_eq(
            self.vwap_close_deviation,
            other.vwap_close_deviation,
            index,
            "vwap_close_deviation",
        );
        assert_f64_eq(self.price_impact, other.price_impact, index, "price_impact");
        assert_f64_eq(
            self.kyle_lambda_proxy,
            other.kyle_lambda_proxy,
            index,
            "kyle_lambda_proxy",
        );
        assert_f64_eq(
            self.trade_intensity,
            other.trade_intensity,
            index,
            "trade_intensity",
        );
        assert_f64_eq(
            self.volume_per_trade,
            other.volume_per_trade,
            index,
            "volume_per_trade",
        );
        assert_f64_eq(
            self.aggression_ratio,
            other.aggression_ratio,
            index,
            "aggression_ratio",
        );
        assert_f64_eq(
            self.aggregation_density_f64,
            other.aggregation_density_f64,
            index,
            "aggregation_density_f64",
        );
        assert_f64_eq(
            self.turnover_imbalance,
            other.turnover_imbalance,
            index,
            "turnover_imbalance",
        );

        // Inter-bar optional features
        assert_eq!(
            self.lookback_trade_count, other.lookback_trade_count,
            "bar[{index}] lookback_trade_count mismatch"
        );
        assert_opt_f64_eq(self.lookback_ofi, other.lookback_ofi, index, "lookback_ofi");
        assert_eq!(
            self.lookback_duration_us, other.lookback_duration_us,
            "bar[{index}] lookback_duration_us mismatch"
        );
        assert_opt_f64_eq(
            self.lookback_intensity,
            other.lookback_intensity,
            index,
            "lookback_intensity",
        );
        assert_eq!(
            self.lookback_vwap_raw, other.lookback_vwap_raw,
            "bar[{index}] lookback_vwap_raw mismatch"
        );
        assert_opt_f64_eq(
            self.lookback_vwap_position,
            other.lookback_vwap_position,
            index,
            "lookback_vwap_position",
        );
        assert_opt_f64_eq(
            self.lookback_count_imbalance,
            other.lookback_count_imbalance,
            index,
            "lookback_count_imbalance",
        );
        assert_opt_f64_eq(
            self.lookback_kyle_lambda,
            other.lookback_kyle_lambda,
            index,
            "lookback_kyle_lambda",
        );
        assert_opt_f64_eq(
            self.lookback_burstiness,
            other.lookback_burstiness,
            index,
            "lookback_burstiness",
        );
        assert_opt_f64_eq(
            self.lookback_volume_skew,
            other.lookback_volume_skew,
            index,
            "lookback_volume_skew",
        );
        assert_opt_f64_eq(
            self.lookback_volume_kurt,
            other.lookback_volume_kurt,
            index,
            "lookback_volume_kurt",
        );
        assert_opt_f64_eq(
            self.lookback_price_range,
            other.lookback_price_range,
            index,
            "lookback_price_range",
        );
        assert_opt_f64_eq(
            self.lookback_kaufman_er,
            other.lookback_kaufman_er,
            index,
            "lookback_kaufman_er",
        );
        assert_opt_f64_eq(
            self.lookback_garman_klass_vol,
            other.lookback_garman_klass_vol,
            index,
            "lookback_garman_klass_vol",
        );
        assert_opt_f64_eq(
            self.lookback_hurst,
            other.lookback_hurst,
            index,
            "lookback_hurst",
        );
        assert_opt_f64_eq(
            self.lookback_permutation_entropy,
            other.lookback_permutation_entropy,
            index,
            "lookback_permutation_entropy",
        );

        // Intra-bar optional features
        assert_opt_f64_eq(
            self.intra_bull_epoch_density,
            other.intra_bull_epoch_density,
            index,
            "intra_bull_epoch_density",
        );
        assert_opt_f64_eq(
            self.intra_bear_epoch_density,
            other.intra_bear_epoch_density,
            index,
            "intra_bear_epoch_density",
        );
        assert_opt_f64_eq(
            self.intra_bull_excess_gain,
            other.intra_bull_excess_gain,
            index,
            "intra_bull_excess_gain",
        );
        assert_opt_f64_eq(
            self.intra_bear_excess_gain,
            other.intra_bear_excess_gain,
            index,
            "intra_bear_excess_gain",
        );
        assert_opt_f64_eq(
            self.intra_bull_cv,
            other.intra_bull_cv,
            index,
            "intra_bull_cv",
        );
        assert_opt_f64_eq(
            self.intra_bear_cv,
            other.intra_bear_cv,
            index,
            "intra_bear_cv",
        );
        assert_opt_f64_eq(
            self.intra_max_drawdown,
            other.intra_max_drawdown,
            index,
            "intra_max_drawdown",
        );
        assert_opt_f64_eq(
            self.intra_max_runup,
            other.intra_max_runup,
            index,
            "intra_max_runup",
        );
        assert_eq!(
            self.intra_trade_count, other.intra_trade_count,
            "bar[{index}] intra_trade_count mismatch"
        );
        assert_opt_f64_eq(self.intra_ofi, other.intra_ofi, index, "intra_ofi");
        assert_eq!(
            self.intra_duration_us, other.intra_duration_us,
            "bar[{index}] intra_duration_us mismatch"
        );
        assert_opt_f64_eq(
            self.intra_intensity,
            other.intra_intensity,
            index,
            "intra_intensity",
        );
        assert_opt_f64_eq(
            self.intra_vwap_position,
            other.intra_vwap_position,
            index,
            "intra_vwap_position",
        );
        assert_opt_f64_eq(
            self.intra_count_imbalance,
            other.intra_count_imbalance,
            index,
            "intra_count_imbalance",
        );
        assert_opt_f64_eq(
            self.intra_kyle_lambda,
            other.intra_kyle_lambda,
            index,
            "intra_kyle_lambda",
        );
        assert_opt_f64_eq(
            self.intra_burstiness,
            other.intra_burstiness,
            index,
            "intra_burstiness",
        );
        assert_opt_f64_eq(
            self.intra_volume_skew,
            other.intra_volume_skew,
            index,
            "intra_volume_skew",
        );
        assert_opt_f64_eq(
            self.intra_volume_kurt,
            other.intra_volume_kurt,
            index,
            "intra_volume_kurt",
        );
        assert_opt_f64_eq(
            self.intra_kaufman_er,
            other.intra_kaufman_er,
            index,
            "intra_kaufman_er",
        );
        assert_opt_f64_eq(
            self.intra_garman_klass_vol,
            other.intra_garman_klass_vol,
            index,
            "intra_garman_klass_vol",
        );
        assert_opt_f64_eq(self.intra_hurst, other.intra_hurst, index, "intra_hurst");
        assert_opt_f64_eq(
            self.intra_permutation_entropy,
            other.intra_permutation_entropy,
            index,
            "intra_permutation_entropy",
        );
    }
}

/// NaN-safe f64 comparison with bit-exact tolerance.
/// Uses 1e-12 relative tolerance to handle deterministic floating-point accumulation
/// differences across 863K trades (f64 mantissa precision is ~15 digits).
fn assert_f64_eq(a: f64, b: f64, index: usize, field: &str) {
    if a.is_nan() && b.is_nan() {
        return; // NaN == NaN for our purposes
    }
    if a == b {
        return; // Exact match (handles 0.0 == 0.0 and inf cases)
    }
    let abs_diff = (a - b).abs();
    let max_abs = a.abs().max(b.abs());
    // Use relative tolerance for large values, absolute for near-zero
    let tol = if max_abs > 1.0 {
        max_abs * 1e-12
    } else {
        1e-12
    };
    assert!(
        abs_diff < tol,
        "bar[{index}] {field} mismatch: {a} != {b} (diff={abs_diff}, tol={tol})",
    );
}

/// NaN-safe Option<f64> comparison
fn assert_opt_f64_eq(a: Option<f64>, b: Option<f64>, index: usize, field: &str) {
    match (a, b) {
        (None, None) => {}
        (Some(va), Some(vb)) => assert_f64_eq(va, vb, index, field),
        _ => panic!("bar[{index}] {field} Some/None mismatch: {a:?} vs {b:?}"),
    }
}

/// Load the full BTCUSDT 2024-01-01 fixture (headerless, 8-column Binance Vision format)
fn load_full_day_fixture() -> Vec<opendeviationbar_core::types::AggTrade> {
    let manifest_dir = env!("CARGO_MANIFEST_DIR");
    let workspace_root = std::path::Path::new(manifest_dir)
        .parent()
        .unwrap()
        .parent()
        .unwrap();
    let path = workspace_root
        .join("tests")
        .join("fixtures")
        .join("BTCUSDT-aggTrades-2024-01-01.csv");

    let file = std::fs::File::open(&path)
        .unwrap_or_else(|e| panic!("Failed to open fixture {}: {}", path.display(), e));

    let mut reader = csv::ReaderBuilder::new()
        .has_headers(false)
        .buffer_capacity(2 * 1024 * 1024)
        .from_reader(file);

    let mut trades = Vec::with_capacity(864_000);

    for (line, result) in reader.deserialize().enumerate() {
        let record: HeaderlessRecord =
            result.unwrap_or_else(|e| panic!("CSV parse error at line {}: {}", line + 1, e));
        trades.push(record.into_agg_trade());
    }

    eprintln!("Loaded {} trades from {}", trades.len(), path.display());
    trades
}

/// Headerless CSV record matching Binance Vision 8-column format
#[derive(serde::Deserialize)]
struct HeaderlessRecord {
    agg_trade_id: i64,
    price: String,
    quantity: String,
    first_trade_id: i64,
    last_trade_id: i64,
    timestamp: i64,
    is_buyer_maker: String,
    _is_best_match: String,
}

impl HeaderlessRecord {
    fn into_agg_trade(self) -> opendeviationbar_core::types::AggTrade {
        use opendeviationbar_core::fixed_point::FixedPoint;
        opendeviationbar_core::types::AggTrade {
            agg_trade_id: self.agg_trade_id,
            price: FixedPoint::from_str(&self.price).unwrap(),
            volume: FixedPoint::from_str(&self.quantity).unwrap(),
            first_trade_id: self.first_trade_id,
            last_trade_id: self.last_trade_id,
            timestamp: self.timestamp,
            is_buyer_maker: self.is_buyer_maker == "True",
            is_best_match: None,
        }
    }
}

fn golden_fixture_path() -> std::path::PathBuf {
    let manifest_dir = env!("CARGO_MANIFEST_DIR");
    std::path::Path::new(manifest_dir)
        .join("tests")
        .join("fixtures")
        .join("golden_bars_btcusdt_250_2024-01-01.json")
}

/// Process all trades and return golden bars (completed + orphan from reset_at_ouroboros)
fn produce_golden_bars() -> Vec<GoldenBar> {
    let trades = load_full_day_fixture();

    let inter_bar_config = InterBarConfig {
        lookback_mode: LookbackMode::FixedCount(500),
        compute_tier2: true,
        compute_tier3: true,
        compute_hurst: None,
        compute_permutation_entropy: None,
    };

    let mut processor = OpenDeviationBarProcessor::new(250)
        .unwrap()
        .with_inter_bar_config(inter_bar_config)
        .with_intra_bar_features();

    let completed_bars = processor.process_agg_trade_records(&trades).unwrap();
    eprintln!("Produced {} completed bars", completed_bars.len());

    let mut golden_bars: Vec<GoldenBar> = completed_bars
        .iter()
        .map(|bar| GoldenBar::from_odb(bar, false))
        .collect();

    // Emit orphan bar via reset_at_ouroboros (simulating day boundary)
    if let Some(orphan) = processor.reset_at_ouroboros() {
        eprintln!(
            "Orphan bar emitted (trade IDs {}-{})",
            orphan.first_agg_trade_id, orphan.last_agg_trade_id
        );
        golden_bars.push(GoldenBar::from_odb(&orphan, true));
    }

    eprintln!(
        "Total golden bars (including orphan): {}",
        golden_bars.len()
    );
    golden_bars
}

#[test]
#[cfg_attr(not(feature = "test-utils"), ignore = "requires test-utils feature")]
fn test_golden_snapshot_bit_exact() {
    let fixture_path = golden_fixture_path();

    #[cfg(feature = "generate-golden")]
    {
        eprintln!("=== GENERATE MODE: Creating golden fixture ===");
        let golden_bars = produce_golden_bars();

        let json =
            serde_json::to_string_pretty(&golden_bars).expect("Failed to serialize golden bars");
        std::fs::write(&fixture_path, &json).unwrap_or_else(|e| {
            panic!("Failed to write fixture {}: {}", fixture_path.display(), e)
        });
        eprintln!(
            "Written {} bars to {}",
            golden_bars.len(),
            fixture_path.display()
        );

        // Sanity checks on generated fixture
        assert!(!golden_bars.is_empty(), "Must produce at least one bar");
        let orphan_count = golden_bars.iter().filter(|b| b.is_orphan).count();
        assert_eq!(
            orphan_count, 1,
            "Must have exactly one orphan bar (from reset_at_ouroboros)"
        );

        // Verify inter-bar features are present (after first few bars)
        let has_inter_bar = golden_bars.iter().any(|b| b.lookback_trade_count.is_some());
        assert!(
            has_inter_bar,
            "Must have inter-bar features on at least some bars"
        );

        // Verify intra-bar features are present
        let has_intra_bar = golden_bars.iter().any(|b| b.intra_trade_count.is_some());
        assert!(
            has_intra_bar,
            "Must have intra-bar features on at least some bars"
        );

        eprintln!("=== Golden fixture generated successfully ===");
    }

    #[cfg(not(feature = "generate-golden"))]
    {
        eprintln!("=== VERIFY MODE: Checking against golden fixture ===");

        let json = std::fs::read_to_string(&fixture_path).unwrap_or_else(|e| {
            panic!(
                "Golden fixture not found at {}: {}. Run with --features generate-golden first.",
                fixture_path.display(),
                e
            )
        });

        let golden_bars: Vec<GoldenBar> =
            serde_json::from_str(&json).expect("Failed to deserialize golden fixture");

        let actual_bars = produce_golden_bars();

        assert_eq!(
            golden_bars.len(),
            actual_bars.len(),
            "Bar count mismatch: golden={} actual={}",
            golden_bars.len(),
            actual_bars.len()
        );

        for (i, (golden, actual)) in golden_bars.iter().zip(actual_bars.iter()).enumerate() {
            golden.assert_matches(actual, i);
        }

        // Verify orphan bar presence
        let orphan_count = golden_bars.iter().filter(|b| b.is_orphan).count();
        assert_eq!(orphan_count, 1, "Must have exactly one orphan bar");

        // Verify inter-bar features
        let has_inter_bar = golden_bars.iter().any(|b| b.lookback_trade_count.is_some());
        assert!(has_inter_bar, "Must have inter-bar features");

        eprintln!(
            "=== Verification PASSED: {} bars match exactly ===",
            golden_bars.len()
        );
    }
}
