//! # Validation Rules
//!
//! Individual validation rules for different aspects of data quality.
//! Each rule implements the `ValidationRule` trait and can be composed
//! into a comprehensive validation pipeline.

use crate::Indicators;
use crate::OHLCVBar;
use anyhow::Result;

/// Validation error information
#[derive(Debug, Clone)]
pub struct ValidationError {
    /// Error category
    pub category: String,
    /// Error message
    pub message: String,
    /// Bar index where error occurred
    pub bar_index: Option<usize>,
    /// Severity (Error or Warning)
    pub severity: Severity,
}

/// Error severity level
#[derive(Debug, Clone, PartialEq)]
pub enum Severity {
    /// Critical error that indicates data corruption
    Error,
    /// Warning about potential data quality issues
    Warning,
}

impl ValidationError {
    /// Create new error
    pub fn error<C: Into<String>, M: Into<String>>(category: C, message: M) -> Self {
        Self {
            category: category.into(),
            message: message.into(),
            bar_index: None,
            severity: Severity::Error,
        }
    }

    /// Create new warning
    pub fn warning<C: Into<String>, M: Into<String>>(category: C, message: M) -> Self {
        Self {
            category: category.into(),
            message: message.into(),
            bar_index: None,
            severity: Severity::Warning,
        }
    }

    /// Set bar index
    pub const fn at_index(mut self, index: usize) -> Self {
        self.bar_index = Some(index);
        self
    }
}

/// Validation rule trait
///
/// Each rule implements specific validation logic for data quality checks.
pub trait ValidationRule: Send + Sync {
    /// Validate OHLCV bars
    fn validate_bars(&self, bars: &[OHLCVBar]) -> Result<Vec<ValidationError>>;

    /// Validate technical indicators (optional)
    fn validate_indicators(&self, _indicators: &Indicators) -> Result<Vec<ValidationError>> {
        Ok(Vec::new()) // Default: no indicator validation
    }

    /// Rule name for reporting
    fn name(&self) -> &str;
}

// ============================================================================
// Rule 1: OHLCV Integrity Rule
// ============================================================================

/// OHLCV integrity validation
///
/// Checks:
/// - high >= low
/// - high >= open, close
/// - low <= open, close
/// - volume >= 0
#[derive(Debug)]
pub struct IntegrityRule;

impl Default for IntegrityRule {
    fn default() -> Self {
        Self::new()
    }
}

impl IntegrityRule {
    pub const fn new() -> Self {
        Self
    }
}

impl ValidationRule for IntegrityRule {
    fn name(&self) -> &str {
        "OHLCV Integrity"
    }

    fn validate_bars(&self, bars: &[OHLCVBar]) -> Result<Vec<ValidationError>> {
        let mut errors = Vec::new();

        for (i, bar) in bars.iter().enumerate() {
            // Check: high >= low
            if bar.high < bar.low {
                errors.push(
                    ValidationError::error(
                        "integrity",
                        format!("Bar {}: high < low ({:.2} < {:.2})", i, bar.high, bar.low),
                    )
                    .at_index(i),
                );
            }

            // Check: high >= open, close
            if bar.high < bar.open {
                errors.push(
                    ValidationError::error(
                        "integrity",
                        format!("Bar {}: high < open ({:.2} < {:.2})", i, bar.high, bar.open),
                    )
                    .at_index(i),
                );
            }

            if bar.high < bar.close {
                errors.push(
                    ValidationError::error(
                        "integrity",
                        format!(
                            "Bar {}: high < close ({:.2} < {:.2})",
                            i, bar.high, bar.close
                        ),
                    )
                    .at_index(i),
                );
            }

            // Check: low <= open, close
            if bar.low > bar.open {
                errors.push(
                    ValidationError::error(
                        "integrity",
                        format!("Bar {}: low > open ({:.2} > {:.2})", i, bar.low, bar.open),
                    )
                    .at_index(i),
                );
            }

            if bar.low > bar.close {
                errors.push(
                    ValidationError::error(
                        "integrity",
                        format!("Bar {}: low > close ({:.2} > {:.2})", i, bar.low, bar.close),
                    )
                    .at_index(i),
                );
            }

            // Check: volume >= 0
            if bar.volume < 0.0 {
                errors.push(
                    ValidationError::error(
                        "integrity",
                        format!("Bar {}: negative volume ({:.2})", i, bar.volume),
                    )
                    .at_index(i),
                );
            }
        }

        Ok(errors)
    }
}

// ============================================================================
// Rule 2: Price Continuity Rule
// ============================================================================

/// Price continuity validation
///
/// Detects price spikes (large percentage changes between consecutive bars).
/// Default threshold: 20% change
#[derive(Debug)]
pub struct ContinuityRule {
    /// Maximum allowed percentage change (0.20 = 20%)
    threshold: f64,
}

impl ContinuityRule {
    pub const fn new(threshold: f64) -> Self {
        Self { threshold }
    }
}

impl ValidationRule for ContinuityRule {
    fn name(&self) -> &str {
        "Price Continuity"
    }

    fn validate_bars(&self, bars: &[OHLCVBar]) -> Result<Vec<ValidationError>> {
        let mut errors = Vec::new();

        for i in 1..bars.len() {
            let prev_close = bars[i - 1].close;
            let curr_open = bars[i].open;

            // Calculate percentage change
            let pct_change = ((curr_open - prev_close) / prev_close).abs();

            if pct_change > self.threshold {
                errors.push(
                    ValidationError::error(
                        "continuity",
                        format!(
                            "Bar {}: price spike of {:.1}% (threshold: {:.1}%)",
                            i,
                            pct_change * 100.0,
                            self.threshold * 100.0
                        ),
                    )
                    .at_index(i),
                );
            }
        }

        Ok(errors)
    }
}

// ============================================================================
// Rule 3: Indicator Validation Rule
// ============================================================================

/// Technical indicator validation
///
/// Checks:
/// - RSI in range [0, 100]
/// - No NaN or Infinite values
/// - Bollinger bands properly ordered (upper > middle > lower)
#[derive(Debug)]
pub struct IndicatorRule;

impl Default for IndicatorRule {
    fn default() -> Self {
        Self::new()
    }
}

impl IndicatorRule {
    pub const fn new() -> Self {
        Self
    }
}

impl ValidationRule for IndicatorRule {
    fn name(&self) -> &str {
        "Technical Indicators"
    }

    fn validate_bars(&self, _bars: &[OHLCVBar]) -> Result<Vec<ValidationError>> {
        // This rule validates indicators, not bars
        Ok(Vec::new())
    }

    fn validate_indicators(&self, indicators: &Indicators) -> Result<Vec<ValidationError>> {
        let mut errors = Vec::new();

        // Validate RSI range (0-100)
        for (i, &rsi) in indicators.rsi.iter().enumerate() {
            if rsi.is_nan() {
                errors.push(
                    ValidationError::error("indicator", format!("RSI at index {}: NaN value", i))
                        .at_index(i),
                );
            } else if rsi.is_infinite() {
                errors.push(
                    ValidationError::error(
                        "indicator",
                        format!("RSI at index {}: infinite value", i),
                    )
                    .at_index(i),
                );
            } else if !(0.0..=100.0).contains(&rsi) {
                errors.push(
                    ValidationError::error(
                        "indicator",
                        format!("RSI at index {}: out of range [{:.2}]", i, rsi),
                    )
                    .at_index(i),
                );
            } else {
                // RSI value is valid
            }
        }

        // Validate MACD for NaN/Inf
        for (i, &macd) in indicators.macd.iter().enumerate() {
            if macd.is_nan() || macd.is_infinite() {
                errors.push(
                    ValidationError::error(
                        "indicator",
                        format!("MACD at index {}: invalid value", i),
                    )
                    .at_index(i),
                );
            }
        }

        // Validate Bollinger Bands ordering
        for i in 0..indicators.bb_upper.len() {
            let upper = indicators.bb_upper[i];
            let middle = indicators.bb_middle[i];
            let lower = indicators.bb_lower[i];

            if upper.is_nan() || middle.is_nan() || lower.is_nan() {
                errors.push(
                    ValidationError::error(
                        "indicator",
                        format!("Bollinger Bands at index {}: NaN value", i),
                    )
                    .at_index(i),
                );
            } else if !(upper >= middle && middle >= lower) {
                errors.push(
                    ValidationError::warning(
                        "indicator",
                        format!(
                            "Bollinger Bands at index {}: improper ordering (upper: {:.2}, middle: {:.2}, lower: {:.2})",
                            i, upper, middle, lower
                        ),
                    )
                    .at_index(i),
                );
            } else {
                // Bollinger Bands properly ordered
            }
        }

        // Validate ATR for negative values
        for (i, &atr) in indicators.atr.iter().enumerate() {
            if atr.is_nan() || atr.is_infinite() {
                errors.push(
                    ValidationError::error(
                        "indicator",
                        format!("ATR at index {}: invalid value", i),
                    )
                    .at_index(i),
                );
            } else if atr < 0.0 {
                errors.push(
                    ValidationError::error(
                        "indicator",
                        format!("ATR at index {}: negative value ({:.2})", i, atr),
                    )
                    .at_index(i),
                );
            } else {
                // ATR value is valid
            }
        }

        Ok(errors)
    }
}

// ============================================================================
// Rule 4: Timestamp Validation Rule
// ============================================================================

/// Timestamp alignment validation
///
/// Checks:
/// - Timestamps are properly ordered
/// - No large gaps in time series
#[derive(Debug)]
pub struct TimestampRule {
    /// Expected interval between bars in seconds
    expected_interval_secs: i64,
}

impl TimestampRule {
    pub const fn new(expected_interval_secs: i64) -> Self {
        Self {
            expected_interval_secs,
        }
    }
}

impl ValidationRule for TimestampRule {
    fn name(&self) -> &str {
        "Timestamp Alignment"
    }

    fn validate_bars(&self, bars: &[OHLCVBar]) -> Result<Vec<ValidationError>> {
        let mut errors = Vec::new();

        for i in 1..bars.len() {
            let prev_ts = bars[i - 1].timestamp;
            let curr_ts = bars[i].timestamp;

            // Check: timestamps are ordered
            if curr_ts <= prev_ts {
                errors.push(
                    ValidationError::error(
                        "timestamp",
                        format!("Bar {}: timestamp not ordered (current <= previous)", i),
                    )
                    .at_index(i),
                );
            }

            // Check: no large gaps
            let gap_secs = (curr_ts - prev_ts).num_seconds();
            let max_gap = self.expected_interval_secs * 3; // Allow up to 3x expected interval

            if gap_secs > max_gap {
                errors.push(
                    ValidationError::error(
                        "timestamp",
                        format!(
                            "Bar {}: large gap of {}s (expected: {}s)",
                            i, gap_secs, self.expected_interval_secs
                        ),
                    )
                    .at_index(i),
                );
            }
        }

        Ok(errors)
    }
}

// ============================================================================
// Rule 5: Data Completeness Rule
// ============================================================================

/// Data completeness validation
///
/// Checks for missing bars in the time series based on expected interval.
#[derive(Debug)]
pub struct CompletenessRule {
    /// Expected interval between bars in seconds
    expected_interval_secs: i64,
    /// Minimum completeness ratio (0.0 - 1.0)
    min_completeness_ratio: f64,
}

impl CompletenessRule {
    pub const fn new(expected_interval_secs: i64, min_completeness_ratio: f64) -> Self {
        Self {
            expected_interval_secs,
            min_completeness_ratio,
        }
    }
}

impl ValidationRule for CompletenessRule {
    fn name(&self) -> &str {
        "Data Completeness"
    }

    fn validate_bars(&self, bars: &[OHLCVBar]) -> Result<Vec<ValidationError>> {
        let mut errors = Vec::new();

        if bars.len() < 2 {
            return Ok(errors);
        }

        // Calculate expected number of bars (safe: checked bars.len() >= 2)
        let start_ts = match bars.first() {
            Some(b) => b.timestamp,
            None => return Ok(errors),
        };
        let end_ts = match bars.last() {
            Some(b) => b.timestamp,
            None => return Ok(errors),
        };
        let total_duration_secs = (end_ts - start_ts).num_seconds();
        let expected_bars = (total_duration_secs / self.expected_interval_secs) as usize + 1;
        let actual_bars = bars.len();

        // Calculate completeness ratio
        let completeness_ratio = actual_bars as f64 / expected_bars as f64;

        if completeness_ratio < self.min_completeness_ratio {
            errors.push(ValidationError::error(
                "completeness",
                format!(
                    "Data completeness: {:.1}% (expected: \u{2265}{:.1}%) - {}/{} bars present",
                    completeness_ratio * 100.0,
                    self.min_completeness_ratio * 100.0,
                    actual_bars,
                    expected_bars
                ),
            ));
        } else if completeness_ratio < 1.0 {
            errors.push(ValidationError::warning(
                "completeness",
                format!(
                    "Data completeness: {:.1}% - {}/{} bars present",
                    completeness_ratio * 100.0,
                    actual_bars,
                    expected_bars
                ),
            ));
        } else {
            // Full completeness, no issues
        }

        Ok(errors)
    }
}
