#![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,
)]
//! Integration test for 54-dimension feature extraction
//!
//! Tests the extract_ml_features() function with real OHLCV data

use chrono::Utc;
use ml::features::extraction::{extract_ml_features, OHLCVBar};
use tracing::info;
use tracing::warn;

#[test]
fn test_extract_256_dim_features() {
    // Create synthetic OHLCV bars (100 bars to exceed warmup period of 50)
    let bars: Vec<OHLCVBar> = (0..100)
        .map(|i| OHLCVBar {
            timestamp: Utc::now() + chrono::Duration::hours(i),
            open: 4500.0 + i as f64 * 0.5,
            high: 4510.0 + i as f64 * 0.5,
            low: 4490.0 + i as f64 * 0.5,
            close: 4505.0 + i as f64 * 0.5,
            volume: 10000.0 + i as f64 * 100.0,
        })
        .collect();

    // Extract features
    let result = extract_ml_features(&bars);
    assert!(
        result.is_ok(),
        "Feature extraction failed: {:?}",
        result.err()
    );

    let features = result.unwrap();

    // Should return features for bars after warmup period (100 - 50 = 50)
    assert_eq!(
        features.len(),
        50,
        "Expected 50 feature vectors (100 bars - 50 warmup), got {}",
        features.len()
    );

    // Each feature vector should be exactly 54 dimensions
    for (i, feature_vec) in features.iter().enumerate() {
        assert_eq!(
            feature_vec.len(),
            54,
            "Feature vector {} has wrong dimension: {}",
            i,
            feature_vec.len()
        );

        // Validate no NaN/Inf values
        for (j, &val) in feature_vec.iter().enumerate() {
            assert!(
                val.is_finite(),
                "Feature vector {} has non-finite value at index {}: {}",
                i,
                j,
                val
            );
        }
    }

    info!(count = features.len(), "Successfully extracted 54-dim feature vectors");
    info!(first_10 = ?&features[0][0..10], "First feature vector sample");
}

#[test]
fn test_feature_dimensions() {
    // Create 60 bars (10 above minimum warmup)
    let bars: Vec<OHLCVBar> = (0..60)
        .map(|i| {
            OHLCVBar {
                timestamp: Utc::now() + chrono::Duration::minutes(i),
                open: 4500.0,
                high: 4510.0,
                low: 4490.0,
                close: 4505.0 + (i as f64 * 0.1).sin() * 5.0, // Add some variation
                volume: 10000.0,
            }
        })
        .collect();

    let features = extract_ml_features(&bars).unwrap();

    // Should have 10 feature vectors (60 - 50 warmup)
    assert_eq!(features.len(), 10);

    // Check output shape (num_bars, 54)
    assert_eq!(features.len(), 10, "Wrong number of bars");
    for feature_vec in &features {
        assert_eq!(feature_vec.len(), 54, "Wrong feature dimension");
    }

    // Validate no NaN/Inf
    for feature_vec in &features {
        for &val in feature_vec.iter() {
            assert!(val.is_finite(), "Found non-finite value: {}", val);
        }
    }

    info!(bars = features.len(), "Feature dimensions validated: bars x 54 features");
}

#[test]
fn test_insufficient_data_error() {
    // Create only 10 bars (below 50 warmup requirement)
    let bars: Vec<OHLCVBar> = (0..10)
        .map(|i| OHLCVBar {
            timestamp: Utc::now() + chrono::Duration::hours(i),
            open: 4500.0,
            high: 4510.0,
            low: 4490.0,
            close: 4505.0,
            volume: 10000.0,
        })
        .collect();

    let result = extract_ml_features(&bars);
    assert!(result.is_err(), "Should fail with insufficient data");

    let error_msg = result.unwrap_err().to_string();
    assert!(
        error_msg.contains("Insufficient data"),
        "Expected 'Insufficient data' error, got: {}",
        error_msg
    );

    info!("Insufficient data error handled correctly");
}

#[test]
fn test_feature_normalization() {
    // Create bars with extreme values to test normalization
    let bars: Vec<OHLCVBar> = (0..100)
        .map(|i| {
            OHLCVBar {
                timestamp: Utc::now() + chrono::Duration::hours(i),
                open: 4500.0 + i as f64 * 10.0, // Large price changes
                high: 4600.0 + i as f64 * 10.0,
                low: 4400.0 + i as f64 * 10.0,
                close: 4500.0 + i as f64 * 10.0,
                volume: 100000.0 + i as f64 * 5000.0, // Large volume changes
            }
        })
        .collect();

    let features = extract_ml_features(&bars).unwrap();

    // Check that features are reasonably normalized
    for (i, feature_vec) in features.iter().enumerate() {
        for (j, &val) in feature_vec.iter().enumerate() {
            // Most features should be in reasonable range (not all, but most)
            // This is a sanity check, not strict validation
            if !(-10.0..=10.0).contains(&val) {
                // Log but don't fail - some features may legitimately be outside this range
                warn!(feature_idx = j, vector_idx = i, value = val, "Feature value outside [-10, 10]");
            }
        }
    }

    info!("Feature normalization validated");
}

#[test]
fn test_feature_consistency() {
    // Test that same input produces same output (deterministic)
    let bars: Vec<OHLCVBar> = (0..100)
        .map(|i| OHLCVBar {
            timestamp: Utc::now() + chrono::Duration::hours(i),
            open: 4500.0,
            high: 4510.0,
            low: 4490.0,
            close: 4505.0,
            volume: 10000.0,
        })
        .collect();

    let features1 = extract_ml_features(&bars).unwrap();
    let features2 = extract_ml_features(&bars).unwrap();

    assert_eq!(features1.len(), features2.len());

    for (vec1, vec2) in features1.iter().zip(features2.iter()) {
        for (&val1, &val2) in vec1.iter().zip(vec2.iter()) {
            assert!(
                (val1 - val2).abs() < 1e-10,
                "Features not consistent: {} vs {}",
                val1,
                val2
            );
        }
    }

    info!("Feature extraction is deterministic");
}
