//! # Data Validator
//!
//! Orchestrates multiple validation rules and generates comprehensive reports.

use std::fmt::Write as _;

use super::rules::{Severity, ValidationError, ValidationRule};
use crate::Indicators;
use crate::OHLCVBar;
use anyhow::Result;
use std::sync::atomic::{AtomicUsize, Ordering};

/// Validation result
#[derive(Debug, Clone)]
pub struct ValidationResult {
    /// Validation errors (critical issues)
    pub errors: Vec<ValidationError>,
    /// Validation warnings (potential issues)
    pub warnings: Vec<ValidationError>,
    /// Total bars validated
    pub total_bars: usize,
    /// Validation passed (no errors)
    valid: bool,
}

impl ValidationResult {
    /// Create new validation result
    pub fn new(
        errors: Vec<ValidationError>,
        warnings: Vec<ValidationError>,
        total_bars: usize,
    ) -> Self {
        let valid = errors.is_empty();
        Self {
            errors,
            warnings,
            total_bars,
            valid,
        }
    }

    /// Check if validation passed (no errors)
    pub const fn is_valid(&self) -> bool {
        self.valid
    }

    /// Get error count
    pub fn error_count(&self) -> usize {
        self.errors.len()
    }

    /// Get warning count
    pub fn warning_count(&self) -> usize {
        self.warnings.len()
    }

    /// Get summary of all errors
    pub fn error_summary(&self) -> String {
        let mut summary = String::new();
        for error in &self.errors {
            _ = writeln!(summary, "{}: {}", error.category, error.message);
        }
        summary
    }

    /// Generate formatted validation report
    pub fn generate_report(&self) -> String {
        let mut report = String::new();

        report.push_str("\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\n");
        report.push_str("                 DATA VALIDATION REPORT\n");
        report.push_str("\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\n\n");

        // Overall status
        if self.is_valid() {
            report.push_str("\u{2705} Status: PASS\n");
        } else {
            report.push_str("\u{274c} Status: FAIL\n");
        }

        _ = writeln!(report, "\u{1f4ca} Total bars validated: {}", self.total_bars);
        _ = writeln!(report, "\u{1f534} Errors: {}", self.error_count());
        _ = writeln!(report, "\u{1f7e1} Warnings: {}\n", self.warning_count());

        // Errors section
        if !self.errors.is_empty() {
            report.push_str("\u{1f534} ERRORS:\n");
            report.push_str("\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\n");

            // Group errors by category
            let mut categories = std::collections::HashMap::new();
            for error in &self.errors {
                categories
                    .entry(error.category.clone())
                    .or_insert_with(Vec::new)
                    .push(error);
            }

            for (category, errors) in categories {
                _ = writeln!(report, "\n  {} ({} errors):", category, errors.len());
                for error in errors.iter().take(5) {
                    // Show first 5 errors per category
                    if let Some(idx) = error.bar_index {
                        _ = writeln!(report, "    [Bar {}] {}", idx, error.message);
                    } else {
                        _ = writeln!(report, "    {}", error.message);
                    }
                }
                if errors.len() > 5 {
                    _ = writeln!(report, "    ... and {} more", errors.len() - 5);
                }
            }
            report.push('\n');
        }

        // Warnings section
        if !self.warnings.is_empty() {
            report.push_str("\u{1f7e1} WARNINGS:\n");
            report.push_str("\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\n");

            // Group warnings by category
            let mut categories = std::collections::HashMap::new();
            for warning in &self.warnings {
                categories
                    .entry(warning.category.clone())
                    .or_insert_with(Vec::new)
                    .push(warning);
            }

            for (category, warnings) in categories {
                _ = writeln!(
                    report,
                    "\n  {} ({} warnings):",
                    category,
                    warnings.len()
                );
                for warning in warnings.iter().take(3) {
                    // Show first 3 warnings per category
                    if let Some(idx) = warning.bar_index {
                        _ = writeln!(report, "    [Bar {}] {}", idx, warning.message);
                    } else {
                        _ = writeln!(report, "    {}", warning.message);
                    }
                }
                if warnings.len() > 3 {
                    _ = writeln!(report, "    ... and {} more", warnings.len() - 3);
                }
            }
            report.push('\n');
        }

        report.push_str("\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\u{2550}\n");

        report
    }
}

/// Validation metrics for Prometheus
#[derive(Debug, Clone, Default)]
pub struct ValidationMetrics {
    /// Total validations performed
    pub total_validations: usize,
    /// Total bars validated
    pub total_bars_validated: usize,
    /// Total errors detected
    pub total_errors: usize,
    /// Total warnings detected
    pub total_warnings: usize,
    /// Total corrections applied
    pub total_corrections: usize,
}

/// Data validator with composable rules
pub struct DataValidator {
    /// Validation rules
    rules: Vec<Box<dyn ValidationRule>>,
    /// Metrics tracking
    metrics_enabled: bool,
    metrics: ValidationMetrics,
    /// Atomic counters for thread-safe metrics
    validation_counter: AtomicUsize,
    bars_counter: AtomicUsize,
    error_counter: AtomicUsize,
    warning_counter: AtomicUsize,
}

impl std::fmt::Debug for DataValidator {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("DataValidator")
            .field(
                "rules",
                &format_args!("<{} validation rules>", self.rules.len()),
            )
            .field("metrics_enabled", &self.metrics_enabled)
            .field("metrics", &self.metrics)
            .field(
                "validation_counter",
                &self.validation_counter.load(Ordering::Relaxed),
            )
            .field("bars_counter", &self.bars_counter.load(Ordering::Relaxed))
            .field("error_counter", &self.error_counter.load(Ordering::Relaxed))
            .field(
                "warning_counter",
                &self.warning_counter.load(Ordering::Relaxed),
            )
            .finish()
    }
}

impl DataValidator {
    /// Create new validator with no rules
    pub fn new() -> Self {
        Self {
            rules: Vec::new(),
            metrics_enabled: false,
            metrics: ValidationMetrics::default(),
            validation_counter: AtomicUsize::new(0),
            bars_counter: AtomicUsize::new(0),
            error_counter: AtomicUsize::new(0),
            warning_counter: AtomicUsize::new(0),
        }
    }

    /// Add validation rule
    pub fn with_rule(mut self, rule: Box<dyn ValidationRule>) -> Self {
        self.rules.push(rule);
        self
    }

    /// Enable metrics tracking
    pub const fn with_metrics_enabled(mut self, enabled: bool) -> Self {
        self.metrics_enabled = enabled;
        self
    }

    /// Validate OHLCV bars
    pub fn validate(&self, bars: &[OHLCVBar]) -> Result<ValidationResult> {
        let mut all_errors = Vec::new();
        let mut all_warnings = Vec::new();

        // Run all rules
        for rule in &self.rules {
            let rule_errors = rule.validate_bars(bars)?;
            for error in rule_errors {
                match error.severity {
                    Severity::Error => all_errors.push(error),
                    Severity::Warning => all_warnings.push(error),
                }
            }
        }

        // Update metrics
        if self.metrics_enabled {
            self.validation_counter.fetch_add(1, Ordering::Relaxed);
            self.bars_counter.fetch_add(bars.len(), Ordering::Relaxed);
            self.error_counter
                .fetch_add(all_errors.len(), Ordering::Relaxed);
            self.warning_counter
                .fetch_add(all_warnings.len(), Ordering::Relaxed);
        }

        Ok(ValidationResult::new(all_errors, all_warnings, bars.len()))
    }

    /// Validate technical indicators
    pub fn validate_indicators(&self, indicators: &Indicators) -> Result<ValidationResult> {
        let mut all_errors = Vec::new();
        let mut all_warnings = Vec::new();

        // Run all rules that support indicator validation
        for rule in &self.rules {
            let rule_errors = rule.validate_indicators(indicators)?;
            for error in rule_errors {
                match error.severity {
                    Severity::Error => all_errors.push(error),
                    Severity::Warning => all_warnings.push(error),
                }
            }
        }

        // Update metrics
        if self.metrics_enabled {
            self.validation_counter.fetch_add(1, Ordering::Relaxed);
            self.error_counter
                .fetch_add(all_errors.len(), Ordering::Relaxed);
            self.warning_counter
                .fetch_add(all_warnings.len(), Ordering::Relaxed);
        }

        Ok(ValidationResult::new(
            all_errors,
            all_warnings,
            indicators.rsi.len(),
        ))
    }

    /// Get validation metrics
    pub fn get_metrics(&self) -> ValidationMetrics {
        ValidationMetrics {
            total_validations: self.validation_counter.load(Ordering::Relaxed),
            total_bars_validated: self.bars_counter.load(Ordering::Relaxed),
            total_errors: self.error_counter.load(Ordering::Relaxed),
            total_warnings: self.warning_counter.load(Ordering::Relaxed),
            total_corrections: 0, // Updated by corrector
        }
    }

    /// Reset metrics
    pub fn reset_metrics(&self) {
        self.validation_counter.store(0, Ordering::Relaxed);
        self.bars_counter.store(0, Ordering::Relaxed);
        self.error_counter.store(0, Ordering::Relaxed);
        self.warning_counter.store(0, Ordering::Relaxed);
    }
}

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

/// Validation report for external consumption
#[derive(Debug, Clone)]
pub struct ValidationReport {
    /// Overall status
    pub status: ValidationStatus,
    /// Total bars validated
    pub total_bars: usize,
    /// Error count
    pub error_count: usize,
    /// Warning count
    pub warning_count: usize,
    /// Detailed errors
    pub errors: Vec<String>,
    /// Detailed warnings
    pub warnings: Vec<String>,
}

/// Validation status
#[derive(Debug, Clone, PartialEq)]
pub enum ValidationStatus {
    /// All checks passed
    Pass,
    /// Some checks failed
    Fail,
    /// Warnings present but no errors
    PassWithWarnings,
}

impl From<ValidationResult> for ValidationReport {
    fn from(result: ValidationResult) -> Self {
        let status = if result.is_valid() {
            if result.warnings.is_empty() {
                ValidationStatus::Pass
            } else {
                ValidationStatus::PassWithWarnings
            }
        } else {
            ValidationStatus::Fail
        };

        let errors: Vec<String> = result
            .errors
            .iter()
            .map(|e| format!("{}: {}", e.category, e.message))
            .collect();

        let warnings: Vec<String> = result
            .warnings
            .iter()
            .map(|w| format!("{}: {}", w.category, w.message))
            .collect();

        Self {
            status,
            total_bars: result.total_bars,
            error_count: result.error_count(),
            warning_count: result.warning_count(),
            errors,
            warnings,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::rules::IntegrityRule;

    #[test]
    fn test_validator_creation() {
        let validator = DataValidator::new();
        assert_eq!(validator.rules.len(), 0);
    }

    #[test]
    fn test_validator_with_rules() {
        let validator = DataValidator::new().with_rule(Box::new(IntegrityRule::new()));
        assert_eq!(validator.rules.len(), 1);
    }

    #[test]
    fn test_validation_result_valid() {
        let result = ValidationResult::new(Vec::new(), Vec::new(), 100);
        assert!(result.is_valid());
        assert_eq!(result.error_count(), 0);
        assert_eq!(result.warning_count(), 0);
    }

    #[test]
    fn test_validation_result_invalid() {
        let errors = vec![ValidationError::error("test", "test error")];
        let result = ValidationResult::new(errors, Vec::new(), 100);
        assert!(!result.is_valid());
        assert_eq!(result.error_count(), 1);
    }

    #[test]
    fn test_report_generation() {
        let errors = vec![ValidationError::error("integrity", "test error").at_index(5)];
        let warnings = vec![ValidationError::warning("continuity", "test warning").at_index(10)];
        let result = ValidationResult::new(errors, warnings, 100);

        let report = result.generate_report();
        assert!(report.contains("FAIL"));
        assert!(report.contains("integrity"));
        assert!(report.contains("continuity"));
        assert!(report.contains("Bar 5"));
        assert!(report.contains("Bar 10"));
    }
}
