//! Compliance Checking Module
//!
//! Provides compliance validation for various frameworks including SOC2 and
//! HIPAA.

pub mod frameworks;
pub mod hipaa;
pub mod reports;
pub mod soc2;

use std::collections::HashMap;
use std::sync::Arc;

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

use crate::error::{policy, ControlCenterError, Result};
use crate::policies::{PolicyCategory, PolicyMetadata, PolicyRequestContext, PolicyResult};
use crate::storage::PolicyMetadata as StoragePolicyMetadata;
use crate::storage::{ComplianceCheckResult, PolicyStorage};

/// Compliance framework types
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum ComplianceFramework {
    SOC2,
    HIPAA,
    GDPR,
    ISO27001,
    NIST,
    #[serde(rename = "PCI_DSS")]
    PciDss,
}

/// Compliance check result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceResult {
    pub framework: ComplianceFramework,
    pub overall_compliant: bool,
    pub control_results: Vec<ControlResult>,
    pub violations: Vec<ComplianceViolation>,
    pub recommendations: Vec<ComplianceRecommendation>,
    pub score: f64, // 0.0 to 100.0
    pub timestamp: DateTime<Utc>,
    pub next_review_date: DateTime<Utc>,
}

/// Individual control check result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ControlResult {
    pub control_id: String,
    pub control_name: String,
    pub category: String,
    pub compliant: bool,
    pub evidence: Vec<String>,
    pub gaps: Vec<String>,
    pub risk_rating: RiskRating,
    pub remediation_timeline: Option<String>,
}

/// Compliance violation details
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceViolation {
    pub violation_id: String,
    pub control_id: String,
    pub severity: ViolationSeverity,
    pub description: String,
    pub policy_id: Option<String>,
    pub resource_id: Option<String>,
    pub detected_at: DateTime<Utc>,
    pub remediation_required: bool,
    pub remediation_deadline: Option<DateTime<Utc>>,
}

/// Compliance recommendation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceRecommendation {
    pub recommendation_id: String,
    pub control_id: String,
    pub priority: RecommendationPriority,
    pub title: String,
    pub description: String,
    pub implementation_effort: EffortLevel,
    pub expected_impact: ImpactLevel,
    pub resources_required: Vec<String>,
}

/// Risk rating levels
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum RiskRating {
    Low,
    Medium,
    High,
    Critical,
}

/// Violation severity levels
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ViolationSeverity {
    Informational,
    Low,
    Medium,
    High,
    Critical,
}

/// Recommendation priority levels
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RecommendationPriority {
    Low,
    Medium,
    High,
    Urgent,
}

/// Implementation effort levels
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EffortLevel {
    Minimal,
    Low,
    Medium,
    High,
    Extensive,
}

/// Expected impact levels
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ImpactLevel {
    Low,
    Medium,
    High,
    Transformational,
}

/// Main compliance checker
pub struct ComplianceChecker {
    storage: Arc<dyn PolicyStorage>,
    frameworks: HashMap<ComplianceFramework, Box<dyn ComplianceFrameworkChecker>>,
}

/// Trait for compliance framework checkers
#[async_trait::async_trait]
pub trait ComplianceFrameworkChecker: Send + Sync {
    /// Get framework name
    fn framework_name(&self) -> &str;

    /// Check compliance for the framework
    async fn check_compliance(
        &self,
        policies: &[PolicyMetadata],
        storage: Arc<dyn PolicyStorage>,
    ) -> Result<ComplianceResult>;

    /// Validate policy against framework requirements
    async fn validate_policy(
        &self,
        policy_content: &str,
        metadata: &PolicyMetadata,
    ) -> Result<Vec<ControlResult>>;

    /// Get required controls for this framework
    fn get_required_controls(&self) -> Vec<ComplianceControl>;

    /// Generate compliance report
    async fn generate_report(&self, result: &ComplianceResult) -> Result<String>;
}

/// Compliance control definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceControl {
    pub control_id: String,
    pub control_name: String,
    pub category: String,
    pub description: String,
    pub required: bool,
    pub policy_patterns: Vec<String>, // Regex patterns to match in policies
    pub evidence_requirements: Vec<String>,
    pub test_procedures: Vec<String>,
}

impl ComplianceChecker {
    /// Create new compliance checker
    pub async fn new(storage: Arc<dyn PolicyStorage>) -> Result<Self> {
        let mut checker = Self {
            storage: storage.clone(),
            frameworks: HashMap::new(),
        };

        // Register compliance frameworks
        checker.frameworks.insert(
            ComplianceFramework::SOC2,
            Box::new(soc2::SOC2Checker::new()),
        );

        checker.frameworks.insert(
            ComplianceFramework::HIPAA,
            Box::new(hipaa::HIPAAChecker::new()),
        );

        Ok(checker)
    }

    /// Check compliance for specific framework
    pub async fn check_framework_compliance(
        &self,
        framework: &ComplianceFramework,
    ) -> Result<ComplianceResult> {
        let checker = self.frameworks.get(framework).ok_or_else(|| {
            ControlCenterError::Policy(policy::PolicyError::Compliance(format!(
                "Unsupported compliance framework: {:?}",
                framework
            )))
        })?;

        // Get all policies from storage
        let policies = self.storage.list_policies().await?;

        // Convert storage policies to compliance checker format
        let policies_for_check: Vec<PolicyMetadata> = policies
            .iter()
            .map(|p| PolicyMetadata {
                id: p.id.clone(),
                name: p.name.clone(),
                description: p.description.clone(),
                version: p.version.clone(),
                created_at: p.created_at,
                updated_at: p.updated_at,
                created_by: p.created_by.clone(),
                tags: p.tags.clone(),
                category: parse_category(&p.category),
                priority: p.priority,
                enabled: p.enabled,
            })
            .collect();

        // Run compliance check
        let result = checker
            .check_compliance(policies_for_check.as_slice(), self.storage.clone())
            .await?;

        // Store result
        let check_result = ComplianceCheckResult {
            id: uuid::Uuid::new_v4().to_string(),
            framework: framework.to_string(),
            check_type: "full_assessment".to_string(),
            policy_id: None,
            compliant: result.overall_compliant,
            violations: result
                .violations
                .iter()
                .map(|v| v.description.clone())
                .collect(),
            recommendations: result
                .recommendations
                .iter()
                .map(|r| r.description.clone())
                .collect(),
            timestamp: Utc::now(),
            metadata: serde_json::to_value(&result)?,
        };

        self.storage.store_compliance_check(&check_result).await?;

        Ok(result)
    }

    /// Check compliance for all supported frameworks
    pub async fn check_all_compliance(
        &self,
    ) -> Result<HashMap<ComplianceFramework, ComplianceResult>> {
        let mut results = HashMap::new();

        for framework in self.frameworks.keys() {
            match self.check_framework_compliance(framework).await {
                Ok(result) => {
                    results.insert(framework.clone(), result);
                }
                Err(e) => {
                    tracing::error!("Failed to check compliance for {:?}: {}", framework, e);
                }
            }
        }

        Ok(results)
    }

    /// Validate single policy against framework
    pub async fn validate_policy_compliance(
        &self,
        framework: &ComplianceFramework,
        policy_content: &str,
        metadata: &PolicyMetadata,
    ) -> Result<Vec<ControlResult>> {
        let checker = self.frameworks.get(framework).ok_or_else(|| {
            ControlCenterError::Policy(policy::PolicyError::Compliance(format!(
                "Unsupported compliance framework: {:?}",
                framework
            )))
        })?;

        checker.validate_policy(policy_content, metadata).await
    }

    /// Get compliance summary across all frameworks
    pub async fn get_compliance_summary(&self) -> Result<ComplianceSummary> {
        let all_results = self.check_all_compliance().await?;

        let mut total_controls = 0;
        let mut compliant_controls = 0;
        let mut total_violations = 0;
        let mut critical_violations = 0;

        for result in all_results.values() {
            total_controls += result.control_results.len();
            compliant_controls += result
                .control_results
                .iter()
                .filter(|c| c.compliant)
                .count();
            total_violations += result.violations.len();
            critical_violations += result
                .violations
                .iter()
                .filter(|v| matches!(v.severity, ViolationSeverity::Critical))
                .count();
        }

        let overall_compliance_rate = if total_controls > 0 {
            (compliant_controls as f64 / total_controls as f64) * 100.0
        } else {
            0.0
        };

        Ok(ComplianceSummary {
            overall_compliance_rate,
            framework_results: all_results,
            total_controls,
            compliant_controls,
            total_violations,
            critical_violations,
            last_assessment: Utc::now(),
            next_assessment: Utc::now() + chrono::Duration::days(90), // Quarterly
        })
    }

    /// Generate compliance report for framework
    pub async fn generate_compliance_report(
        &self,
        framework: &ComplianceFramework,
        format: &str,
    ) -> Result<String> {
        let result = self.check_framework_compliance(framework).await?;

        let checker = self.frameworks.get(framework).ok_or_else(|| {
            ControlCenterError::Policy(policy::PolicyError::Compliance(format!(
                "Unsupported compliance framework: {:?}",
                framework
            )))
        })?;

        match format.to_lowercase().as_str() {
            "json" => Ok(serde_json::to_string_pretty(&result)?),
            "html" | "pdf" => checker.generate_report(&result).await,
            _ => Err(ControlCenterError::Policy(policy::PolicyError::Compliance(
                format!("Unsupported report format: {}", format),
            ))),
        }
    }

    /// Monitor policy changes for compliance impact
    pub async fn assess_policy_change_impact(
        &self,
        old_policy: Option<&str>,
        new_policy: &str,
        metadata: &PolicyMetadata,
    ) -> Result<ComplianceImpactAssessment> {
        let mut impact = ComplianceImpactAssessment {
            policy_id: metadata.id.clone(),
            change_type: if old_policy.is_some() {
                "modification"
            } else {
                "creation"
            }
            .to_string(),
            framework_impacts: HashMap::new(),
            overall_risk_level: RiskRating::Low,
            requires_review: false,
            reviewer_notifications: Vec::new(),
        };

        for (framework, checker) in &self.frameworks {
            let control_results = checker.validate_policy(new_policy, metadata).await?;

            let old_control_results = if let Some(old_content) = old_policy {
                checker.validate_policy(old_content, metadata).await.ok()
            } else {
                None
            };

            let framework_impact =
                self.calculate_framework_impact(&control_results, old_control_results.as_ref());
            impact
                .framework_impacts
                .insert(framework.clone(), framework_impact.clone());

            // Update overall risk level
            if framework_impact.risk_level > impact.overall_risk_level {
                impact.overall_risk_level = framework_impact.risk_level.clone();
            }

            // Determine if review is required
            if framework_impact.compliance_changes > 0
                || matches!(
                    framework_impact.risk_level,
                    RiskRating::High | RiskRating::Critical
                )
            {
                impact.requires_review = true;
            }
        }

        Ok(impact)
    }

    /// Calculate framework-specific impact
    fn calculate_framework_impact(
        &self,
        new_controls: &[ControlResult],
        old_controls: Option<&Vec<ControlResult>>,
    ) -> FrameworkImpactAssessment {
        let (compliance_changes, new_violations, resolved_violations) =
            if let Some(old_results) = old_controls {
                self.count_control_changes(new_controls, old_results)
            } else {
                (0, 0, 0)
            };

        let risk_level = if new_violations > 0 {
            RiskRating::High
        } else if compliance_changes > 0 {
            RiskRating::Medium
        } else {
            RiskRating::Low
        };

        FrameworkImpactAssessment {
            compliance_changes,
            new_violations,
            resolved_violations,
            risk_level,
            affected_controls: new_controls
                .iter()
                .filter(|c| !c.compliant)
                .map(|c| c.control_id.clone())
                .collect(),
        }
    }

    /// Count control compliance changes
    fn count_control_changes(
        &self,
        new_controls: &[ControlResult],
        old_controls: &[ControlResult],
    ) -> (usize, usize, usize) {
        let mut compliance_changes = 0;
        let mut new_violations = 0;
        let mut resolved_violations = 0;

        for new_control in new_controls {
            let old_control = old_controls
                .iter()
                .find(|c| c.control_id == new_control.control_id);

            if let Some(old) = old_control {
                match (old.compliant, new_control.compliant) {
                    (true, false) => {
                        compliance_changes += 1;
                        new_violations += 1;
                    }
                    (false, true) => {
                        compliance_changes += 1;
                        resolved_violations += 1;
                    }
                    _ => {}
                }
            }
        }

        (compliance_changes, new_violations, resolved_violations)
    }
}

/// Helper function to parse category string to PolicyCategory enum
fn parse_category(category: &str) -> PolicyCategory {
    match category {
        "Authentication" => PolicyCategory::Authentication,
        "Authorization" => PolicyCategory::Authorization,
        "Infrastructure" => PolicyCategory::Infrastructure,
        "Workflow" => PolicyCategory::Workflow,
        "Compliance" => PolicyCategory::Compliance,
        "Security" => PolicyCategory::Security,
        "Anomaly" => PolicyCategory::Anomaly,
        _ => PolicyCategory::Security,
    }
}

/// Compliance summary across all frameworks
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceSummary {
    pub overall_compliance_rate: f64,
    pub framework_results: HashMap<ComplianceFramework, ComplianceResult>,
    pub total_controls: usize,
    pub compliant_controls: usize,
    pub total_violations: usize,
    pub critical_violations: usize,
    pub last_assessment: DateTime<Utc>,
    pub next_assessment: DateTime<Utc>,
}

/// Policy change compliance impact assessment
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceImpactAssessment {
    pub policy_id: String,
    pub change_type: String,
    pub framework_impacts: HashMap<ComplianceFramework, FrameworkImpactAssessment>,
    pub overall_risk_level: RiskRating,
    pub requires_review: bool,
    pub reviewer_notifications: Vec<String>,
}

/// Framework-specific impact assessment
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrameworkImpactAssessment {
    pub compliance_changes: usize,
    pub new_violations: usize,
    pub resolved_violations: usize,
    pub risk_level: RiskRating,
    pub affected_controls: Vec<String>,
}

// Helper implementations
impl std::fmt::Display for ComplianceFramework {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ComplianceFramework::SOC2 => write!(f, "SOC2"),
            ComplianceFramework::HIPAA => write!(f, "HIPAA"),
            ComplianceFramework::GDPR => write!(f, "GDPR"),
            ComplianceFramework::ISO27001 => write!(f, "ISO27001"),
            ComplianceFramework::NIST => write!(f, "NIST"),
            ComplianceFramework::PciDss => write!(f, "PCI_DSS"),
        }
    }
}

impl PartialOrd for RiskRating {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        let self_val = match self {
            RiskRating::Low => 1,
            RiskRating::Medium => 2,
            RiskRating::High => 3,
            RiskRating::Critical => 4,
        };
        let other_val = match other {
            RiskRating::Low => 1,
            RiskRating::Medium => 2,
            RiskRating::High => 3,
            RiskRating::Critical => 4,
        };
        self_val.partial_cmp(&other_val)
    }
}

impl std::fmt::Display for ViolationSeverity {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ViolationSeverity::Informational => write!(f, "Informational"),
            ViolationSeverity::Low => write!(f, "Low"),
            ViolationSeverity::Medium => write!(f, "Medium"),
            ViolationSeverity::High => write!(f, "High"),
            ViolationSeverity::Critical => write!(f, "Critical"),
        }
    }
}

impl std::fmt::Display for RecommendationPriority {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            RecommendationPriority::Low => write!(f, "Low"),
            RecommendationPriority::Medium => write!(f, "Medium"),
            RecommendationPriority::High => write!(f, "High"),
            RecommendationPriority::Urgent => write!(f, "Urgent"),
        }
    }
}

impl std::fmt::Display for EffortLevel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            EffortLevel::Minimal => write!(f, "Minimal"),
            EffortLevel::Low => write!(f, "Low"),
            EffortLevel::Medium => write!(f, "Medium"),
            EffortLevel::High => write!(f, "High"),
            EffortLevel::Extensive => write!(f, "Extensive"),
        }
    }
}

impl std::fmt::Display for ImpactLevel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ImpactLevel::Low => write!(f, "Low"),
            ImpactLevel::Medium => write!(f, "Medium"),
            ImpactLevel::High => write!(f, "High"),
            ImpactLevel::Transformational => write!(f, "Transformational"),
        }
    }
}
