//! SOC2 Compliance Checker
//!
//! Implements SOC2 Type II compliance validation for security policies.

use std::sync::Arc;

use chrono::Utc;
use regex::Regex;

use super::{
    ComplianceControl, ComplianceFramework, ComplianceFrameworkChecker, ComplianceRecommendation,
    ComplianceResult, ComplianceViolation, ControlResult, EffortLevel, ImpactLevel,
    RecommendationPriority, RiskRating, ViolationSeverity,
};
use crate::error::{auth, ControlCenterError, Result};
use crate::policies::PolicyMetadata;
use crate::storage::PolicyStorage;

/// SOC2 compliance checker implementation
pub struct SOC2Checker {
    controls: Vec<ComplianceControl>,
}

impl Default for SOC2Checker {
    fn default() -> Self {
        Self {
            controls: Self::initialize_soc2_controls(),
        }
    }
}

impl SOC2Checker {
    /// Create new SOC2 checker with predefined controls
    pub fn new() -> Self {
        Self::default()
    }

    /// Initialize SOC2 Type II trust service criteria controls
    fn initialize_soc2_controls() -> Vec<ComplianceControl> {
        vec![
            // Security Trust Service Criteria (CC)
            ComplianceControl {
                control_id: "CC1.1".to_string(),
                control_name: "Control Environment - Commitment to Integrity and Ethics"
                    .to_string(),
                category: "Security".to_string(),
                description: "Management demonstrates commitment to integrity and ethical values"
                    .to_string(),
                required: true,
                policy_patterns: vec![
                    r"ethics|integrity|conduct|code_of_conduct".to_string(),
                    r"@audit.*true".to_string(), // Audit logging requirement
                ],
                evidence_requirements: vec![
                    "Code of conduct policies".to_string(),
                    "Ethics training records".to_string(),
                ],
                test_procedures: vec!["Review ethics policies in access control".to_string()],
            },
            ComplianceControl {
                control_id: "CC1.2".to_string(),
                control_name: "Control Environment - Board Independence and Oversight".to_string(),
                category: "Security".to_string(),
                description: "Board demonstrates independence and oversight of security"
                    .to_string(),
                required: true,
                policy_patterns: vec![
                    r"approval.*board|oversight|governance".to_string(),
                    r"independent.*review".to_string(),
                ],
                evidence_requirements: vec![
                    "Board oversight documentation".to_string(),
                    "Security governance policies".to_string(),
                ],
                test_procedures: vec![
                    "Verify board approval requirements for sensitive access".to_string()
                ],
            },
            ComplianceControl {
                control_id: "CC2.1".to_string(),
                control_name: "Communication and Information - Internal Communication".to_string(),
                category: "Security".to_string(),
                description: "Internal communication supports security objectives".to_string(),
                required: true,
                policy_patterns: vec![
                    r"notification|alert|communicate".to_string(),
                    r"incident.*report".to_string(),
                ],
                evidence_requirements: vec![
                    "Internal communication policies".to_string(),
                    "Security awareness training".to_string(),
                ],
                test_procedures: vec!["Review notification procedures in policies".to_string()],
            },
            ComplianceControl {
                control_id: "CC3.1".to_string(),
                control_name: "Risk Assessment - Risk Identification".to_string(),
                category: "Security".to_string(),
                description: "Organization identifies risks to security objectives".to_string(),
                required: true,
                policy_patterns: vec![
                    r"risk.*assess|risk.*level|criticality".to_string(),
                    r"classification.*sensitive|confidential".to_string(),
                ],
                evidence_requirements: vec![
                    "Risk assessment documentation".to_string(),
                    "Data classification policies".to_string(),
                ],
                test_procedures: vec!["Verify risk-based access controls".to_string()],
            },
            ComplianceControl {
                control_id: "CC4.1".to_string(),
                control_name: "Monitoring Activities - Ongoing and Separate Evaluations"
                    .to_string(),
                category: "Security".to_string(),
                description: "Organization monitors security controls through ongoing evaluations"
                    .to_string(),
                required: true,
                policy_patterns: vec![
                    r"monitor|audit|review|evaluate".to_string(),
                    r"@audit.*true|logging".to_string(),
                ],
                evidence_requirements: vec![
                    "Monitoring procedures".to_string(),
                    "Audit logs and reviews".to_string(),
                ],
                test_procedures: vec!["Verify audit logging in access policies".to_string()],
            },
            ComplianceControl {
                control_id: "CC5.1".to_string(),
                control_name: "Control Activities - Selection and Development".to_string(),
                category: "Security".to_string(),
                description: "Organization develops control activities to mitigate risks"
                    .to_string(),
                required: true,
                policy_patterns: vec![
                    r"permit.*when|forbid.*when".to_string(),
                    r"control|restrict|limit".to_string(),
                ],
                evidence_requirements: vec![
                    "Control activity documentation".to_string(),
                    "Access control policies".to_string(),
                ],
                test_procedures: vec!["Review policy control logic".to_string()],
            },
            // Access Control Requirements
            ComplianceControl {
                control_id: "CC6.1".to_string(),
                control_name: "Logical Access Controls - User Authentication".to_string(),
                category: "Access Control".to_string(),
                description: "Users are authenticated before accessing the system".to_string(),
                required: true,
                policy_patterns: vec![
                    r"authentication|authenticated|login".to_string(),
                    r"principal.*has.*authentication_status".to_string(),
                ],
                evidence_requirements: vec![
                    "Authentication policies".to_string(),
                    "Login procedures".to_string(),
                ],
                test_procedures: vec![
                    "Verify authentication requirements in access policies".to_string()
                ],
            },
            ComplianceControl {
                control_id: "CC6.2".to_string(),
                control_name: "Logical Access Controls - User Authorization".to_string(),
                category: "Access Control".to_string(),
                description: "Users are authorized based on roles and responsibilities".to_string(),
                required: true,
                policy_patterns: vec![
                    r"role.*in|has.*role|authorization".to_string(),
                    r"permit.*principal.*role".to_string(),
                ],
                evidence_requirements: vec![
                    "Role-based access control policies".to_string(),
                    "Authorization matrices".to_string(),
                ],
                test_procedures: vec!["Review role-based access controls".to_string()],
            },
            ComplianceControl {
                control_id: "CC6.3".to_string(),
                control_name: "Logical Access Controls - Multi-Factor Authentication".to_string(),
                category: "Access Control".to_string(),
                description: "Multi-factor authentication is required for sensitive access"
                    .to_string(),
                required: true,
                policy_patterns: vec![
                    r"mfa|multi.?factor|two.?factor".to_string(),
                    r"mfa_enabled.*true".to_string(),
                ],
                evidence_requirements: vec![
                    "MFA implementation policies".to_string(),
                    "MFA usage reports".to_string(),
                ],
                test_procedures: vec!["Verify MFA requirements for sensitive resources".to_string()],
            },
            // Change Management
            ComplianceControl {
                control_id: "CC8.1".to_string(),
                control_name: "Change Management - Authorization and Approval".to_string(),
                category: "Change Management".to_string(),
                description: "Changes are authorized and approved before implementation"
                    .to_string(),
                required: true,
                policy_patterns: vec![
                    r"approval.*required|change.*ticket".to_string(),
                    r"approved_by.*in.*\[.*Admin.*\]".to_string(),
                ],
                evidence_requirements: vec![
                    "Change approval procedures".to_string(),
                    "Change tickets and approvals".to_string(),
                ],
                test_procedures: vec![
                    "Review production deployment approval requirements".to_string()
                ],
            },
        ]
    }

    /// Validate policy content against specific control
    fn validate_policy_against_control(
        &self,
        policy_content: &str,
        control: &ComplianceControl,
    ) -> ControlResult {
        let mut evidence = Vec::new();
        let mut gaps = Vec::new();
        let mut compliant = true;

        // Check each pattern requirement
        for pattern_str in &control.policy_patterns {
            match Regex::new(pattern_str) {
                Ok(pattern) => {
                    if pattern.is_match(policy_content) {
                        evidence.push(format!("Pattern '{}' found in policy", pattern_str));
                    } else {
                        gaps.push(format!("Missing required pattern: '{}'", pattern_str));
                        compliant = false;
                    }
                }
                Err(e) => {
                    gaps.push(format!("Invalid regex pattern '{}': {}", pattern_str, e));
                    compliant = false;
                }
            }
        }

        // Determine risk rating based on control category and compliance
        let risk_rating = if !compliant {
            match control.category.as_str() {
                "Access Control" => RiskRating::High,
                "Security" => RiskRating::Medium,
                "Change Management" => RiskRating::Medium,
                _ => RiskRating::Low,
            }
        } else {
            RiskRating::Low
        };

        ControlResult {
            control_id: control.control_id.clone(),
            control_name: control.control_name.clone(),
            category: control.category.clone(),
            compliant,
            evidence,
            gaps,
            risk_rating,
            remediation_timeline: if !compliant {
                Some("30-60 days".to_string())
            } else {
                None
            },
        }
    }

    /// Generate SOC2-specific violations
    fn generate_violations(&self, control_results: &[ControlResult]) -> Vec<ComplianceViolation> {
        let mut violations = Vec::new();

        for result in control_results.iter().filter(|r| !r.compliant) {
            let severity = match result.risk_rating {
                RiskRating::Critical => ViolationSeverity::Critical,
                RiskRating::High => ViolationSeverity::High,
                RiskRating::Medium => ViolationSeverity::Medium,
                RiskRating::Low => ViolationSeverity::Low,
            };

            violations.push(ComplianceViolation {
                violation_id: uuid::Uuid::new_v4().to_string(),
                control_id: result.control_id.clone(),
                severity,
                description: format!(
                    "SOC2 control {} not compliant: {}",
                    result.control_id,
                    result.gaps.join(", ")
                ),
                policy_id: None, // Would be set by caller
                resource_id: None,
                detected_at: Utc::now(),
                remediation_required: matches!(
                    result.risk_rating,
                    RiskRating::High | RiskRating::Critical
                ),
                remediation_deadline: if matches!(
                    result.risk_rating,
                    RiskRating::High | RiskRating::Critical
                ) {
                    Some(Utc::now() + chrono::Duration::days(30))
                } else {
                    Some(Utc::now() + chrono::Duration::days(90))
                },
            });
        }

        violations
    }

    /// Generate SOC2-specific recommendations
    fn generate_recommendations(
        &self,
        control_results: &[ControlResult],
    ) -> Vec<ComplianceRecommendation> {
        let mut recommendations = Vec::new();

        for result in control_results.iter().filter(|r| !r.compliant) {
            let (priority, effort) = match result.risk_rating {
                RiskRating::Critical => (RecommendationPriority::Urgent, EffortLevel::High),
                RiskRating::High => (RecommendationPriority::High, EffortLevel::Medium),
                RiskRating::Medium => (RecommendationPriority::Medium, EffortLevel::Medium),
                RiskRating::Low => (RecommendationPriority::Low, EffortLevel::Low),
            };

            let title = match result.control_id.as_str() {
                "CC6.3" => "Implement Multi-Factor Authentication Requirements",
                "CC6.1" => "Strengthen Authentication Controls",
                "CC6.2" => "Implement Role-Based Access Controls",
                "CC8.1" => "Establish Change Management Approval Process",
                _ => "Address SOC2 Compliance Gap",
            };

            let description = match result.control_id.as_str() {
                "CC6.3" => {
                    "Add MFA requirements for sensitive resource access in security policies. \
                     Ensure all administrative and privileged access requires multi-factor \
                     authentication."
                }
                "CC6.1" => {
                    "Enhance authentication requirements in access policies. Ensure proper user \
                     authentication validation before system access."
                }
                "CC6.2" => {
                    "Implement comprehensive role-based access controls. Define clear role \
                     hierarchies and ensure proper authorization checks."
                }
                "CC8.1" => {
                    "Establish formal change management process with approval workflows. Require \
                     proper authorization for production changes."
                }
                _ => "Review and update policies to address identified compliance gaps.",
            };

            recommendations.push(ComplianceRecommendation {
                recommendation_id: uuid::Uuid::new_v4().to_string(),
                control_id: result.control_id.clone(),
                priority,
                title: title.to_string(),
                description: description.to_string(),
                implementation_effort: effort,
                expected_impact: ImpactLevel::High,
                resources_required: vec![
                    "Security team".to_string(),
                    "Policy review".to_string(),
                    "Technical implementation".to_string(),
                ],
            });
        }

        recommendations
    }

    /// Calculate SOC2 compliance score
    fn calculate_compliance_score(&self, control_results: &[ControlResult]) -> f64 {
        if control_results.is_empty() {
            return 0.0;
        }

        let total_controls = control_results.len() as f64;
        let compliant_controls = control_results.iter().filter(|r| r.compliant).count() as f64;

        // Weight by risk level
        let mut weighted_score = 0.0;
        let mut total_weight = 0.0;

        for result in control_results {
            let weight = match result.risk_rating {
                RiskRating::Critical => 4.0,
                RiskRating::High => 3.0,
                RiskRating::Medium => 2.0,
                RiskRating::Low => 1.0,
            };

            if result.compliant {
                weighted_score += weight;
            }
            total_weight += weight;
        }

        if total_weight > 0.0 {
            (weighted_score / total_weight) * 100.0
        } else {
            (compliant_controls / total_controls) * 100.0
        }
    }
}

#[async_trait::async_trait]
impl ComplianceFrameworkChecker for SOC2Checker {
    fn framework_name(&self) -> &str {
        "SOC2 Type II"
    }

    async fn check_compliance(
        &self,
        policies: &[PolicyMetadata],
        storage: Arc<dyn PolicyStorage>,
    ) -> Result<ComplianceResult> {
        let mut all_control_results = Vec::new();

        // Check each policy against all controls
        for policy_metadata in policies {
            if !policy_metadata.enabled {
                continue;
            }

            match storage.get_policy(&policy_metadata.id).await {
                Ok(policy_content) => {
                    let policy_results = self
                        .validate_policy(&policy_content, policy_metadata)
                        .await?;
                    all_control_results.extend(policy_results);
                }
                Err(e) => {
                    tracing::warn!("Failed to retrieve policy {}: {}", policy_metadata.id, e);
                }
            }
        }

        // Aggregate results by control ID (take best result for each control)
        let mut control_map = std::collections::HashMap::new();
        for result in all_control_results {
            let entry = control_map
                .entry(result.control_id.clone())
                .or_insert_with(|| result.clone());

            // If we find a compliant result for a control, prefer it
            if result.compliant && !entry.compliant {
                *entry = result;
            }
        }

        let control_results: Vec<ControlResult> = control_map.into_values().collect();

        // Generate violations and recommendations
        let violations = self.generate_violations(&control_results);
        let recommendations = self.generate_recommendations(&control_results);

        // Calculate compliance score
        let score = self.calculate_compliance_score(&control_results);
        let overall_compliant = violations
            .iter()
            .filter(|v| {
                matches!(
                    v.severity,
                    ViolationSeverity::Critical | ViolationSeverity::High
                )
            })
            .count()
            == 0;

        Ok(ComplianceResult {
            framework: ComplianceFramework::SOC2,
            overall_compliant,
            control_results,
            violations,
            recommendations,
            score,
            timestamp: Utc::now(),
            next_review_date: Utc::now() + chrono::Duration::days(90), // Quarterly review
        })
    }

    async fn validate_policy(
        &self,
        policy_content: &str,
        _metadata: &PolicyMetadata,
    ) -> Result<Vec<ControlResult>> {
        let mut results = Vec::new();

        for control in &self.controls {
            let result = self.validate_policy_against_control(policy_content, control);
            results.push(result);
        }

        Ok(results)
    }

    fn get_required_controls(&self) -> Vec<ComplianceControl> {
        self.controls.clone()
    }

    async fn generate_report(&self, result: &ComplianceResult) -> Result<String> {
        let mut report = String::new();

        report.push_str("# SOC2 Type II Compliance Report\n\n");
        report.push_str(&format!(
            "**Report Date:** {}\n",
            result.timestamp.format("%Y-%m-%d %H:%M:%S UTC")
        ));
        report.push_str(&format!(
            "**Overall Compliance:** {}\n",
            if result.overall_compliant {
                "✓ COMPLIANT"
            } else {
                "✗ NON-COMPLIANT"
            }
        ));
        report.push_str(&format!("**Compliance Score:** {:.1}%\n\n", result.score));

        // Control Results Summary
        report.push_str("## Control Results Summary\n\n");
        let total_controls = result.control_results.len();
        let compliant_controls = result
            .control_results
            .iter()
            .filter(|c| c.compliant)
            .count();
        report.push_str(&format!("- **Total Controls:** {}\n", total_controls));
        report.push_str(&format!(
            "- **Compliant Controls:** {}\n",
            compliant_controls
        ));
        report.push_str(&format!(
            "- **Non-Compliant Controls:** {}\n\n",
            total_controls - compliant_controls
        ));

        // Violations
        if !result.violations.is_empty() {
            report.push_str("## Violations\n\n");
            for violation in &result.violations {
                report.push_str(&format!(
                    "### {} - {}\n",
                    violation.control_id, violation.severity
                ));
                report.push_str(&format!("**Description:** {}\n", violation.description));
                if let Some(deadline) = violation.remediation_deadline {
                    report.push_str(&format!(
                        "**Remediation Deadline:** {}\n",
                        deadline.format("%Y-%m-%d")
                    ));
                }
                report.push('\n');
            }
        }

        // Recommendations
        if !result.recommendations.is_empty() {
            report.push_str("## Recommendations\n\n");
            for recommendation in &result.recommendations {
                report.push_str(&format!(
                    "### {} ({})\n",
                    recommendation.title, recommendation.priority
                ));
                report.push_str(&format!("**Control:** {}\n", recommendation.control_id));
                report.push_str(&format!(
                    "**Description:** {}\n",
                    recommendation.description
                ));
                report.push_str(&format!(
                    "**Implementation Effort:** {}\n",
                    recommendation.implementation_effort
                ));
                report.push_str(&format!(
                    "**Expected Impact:** {}\n",
                    recommendation.expected_impact
                ));
                report.push('\n');
            }
        }

        report.push_str(&format!(
            "**Next Review Date:** {}\n",
            result.next_review_date.format("%Y-%m-%d")
        ));

        Ok(report)
    }
}
