//! Policy Validation System
//!
//! Validates Cedar policies for syntax, semantics, and compliance.

use std::collections::HashMap;
use std::path::Path;

use cedar_policy::{Policy, PolicyId, PolicySet};
use serde::{Deserialize, Serialize};

use crate::config::PolicyConfig;
use crate::error::{http, policy, ControlCenterError, Result};

/// Policy validation results
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationResult {
    pub valid: bool,
    pub errors: Vec<ValidationError>,
    pub warnings: Vec<ValidationWarning>,
    pub suggestions: Vec<ValidationSuggestion>,
    pub metrics: ValidationMetrics,
}

/// Validation error details
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationError {
    pub error_type: ValidationErrorType,
    pub message: String,
    pub line: Option<usize>,
    pub column: Option<usize>,
    pub policy_id: Option<String>,
}

/// Types of validation errors
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ValidationErrorType {
    SyntaxError,
    SemanticError,
    ComplianceViolation,
    SecurityIssue,
    PerformanceIssue,
}

/// Validation warning details
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationWarning {
    pub warning_type: ValidationWarningType,
    pub message: String,
    pub policy_id: Option<String>,
    pub severity: WarningSeverity,
}

/// Types of validation warnings
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ValidationWarningType {
    DeprecatedSyntax,
    PerformanceImpact,
    SecurityBestPractice,
    MaintenanceIssue,
}

/// Warning severity levels
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum WarningSeverity {
    Low,
    Medium,
    High,
}

/// Validation suggestions for improvement
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationSuggestion {
    pub suggestion_type: SuggestionType,
    pub message: String,
    pub proposed_change: Option<String>,
    pub policy_id: Option<String>,
}

/// Types of validation suggestions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SuggestionType {
    Optimization,
    Security,
    Readability,
    Maintenance,
}

/// Validation metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationMetrics {
    pub total_policies: usize,
    pub valid_policies: usize,
    pub invalid_policies: usize,
    pub total_lines: usize,
    pub complexity_score: f64,
    pub validation_time_ms: u64,
}

/// Policy validator
pub struct PolicyValidator {
    config: PolicyConfig,
    compliance_rules: HashMap<String, Vec<ComplianceRule>>,
    security_patterns: Vec<SecurityPattern>,
}

/// Compliance rule definition
#[derive(Debug, Clone)]
pub struct ComplianceRule {
    pub id: String,
    pub name: String,
    pub description: String,
    pub required: bool,
    pub pattern: String,
    pub compliance_framework: String,
}

/// Security pattern for validation
#[derive(Debug, Clone)]
pub struct SecurityPattern {
    pub id: String,
    pub name: String,
    pub pattern: String,
    pub severity: WarningSeverity,
    pub description: String,
}

impl PolicyValidator {
    /// Create new policy validator
    pub fn new(config: &PolicyConfig) -> Self {
        let mut validator = Self {
            config: config.clone(),
            compliance_rules: HashMap::new(),
            security_patterns: Vec::new(),
        };

        validator.initialize_compliance_rules();
        validator.initialize_security_patterns();

        validator
    }

    /// Initialize compliance rules for various frameworks
    fn initialize_compliance_rules(&mut self) {
        // SOC2 compliance rules
        let soc2_rules = vec![
            ComplianceRule {
                id: "soc2-access-control".to_string(),
                name: "Access Control Requirements".to_string(),
                description: "Policies must implement proper access controls".to_string(),
                required: true,
                pattern: r#"permit.*when.*has.*"role""#.to_string(),
                compliance_framework: "SOC2".to_string(),
            },
            ComplianceRule {
                id: "soc2-audit-logging".to_string(),
                name: "Audit Logging Requirements".to_string(),
                description: "All access decisions must be logged".to_string(),
                required: true,
                pattern: r#"@audit.*true"#.to_string(),
                compliance_framework: "SOC2".to_string(),
            },
        ];

        // HIPAA compliance rules
        let hipaa_rules = vec![
            ComplianceRule {
                id: "hipaa-minimum-necessary".to_string(),
                name: "Minimum Necessary Access".to_string(),
                description: "Access must be limited to minimum necessary".to_string(),
                required: true,
                pattern: r#"permit.*when.*has.*"data_classification".*"phi""#.to_string(),
                compliance_framework: "HIPAA".to_string(),
            },
            ComplianceRule {
                id: "hipaa-authorization-tracking".to_string(),
                name: "Authorization Tracking".to_string(),
                description: "All PHI access must be tracked".to_string(),
                required: true,
                pattern: r#"@track.*"phi_access""#.to_string(),
                compliance_framework: "HIPAA".to_string(),
            },
        ];

        self.compliance_rules.insert("SOC2".to_string(), soc2_rules);
        self.compliance_rules
            .insert("HIPAA".to_string(), hipaa_rules);
    }

    /// Initialize security patterns for validation
    fn initialize_security_patterns(&mut self) {
        self.security_patterns = vec![
            SecurityPattern {
                id: "overly-permissive".to_string(),
                name: "Overly Permissive Policy".to_string(),
                pattern: r#"permit\s*\(\s*\*\s*,\s*\*\s*,\s*\*\s*\)"#.to_string(),
                severity: WarningSeverity::High,
                description: "Policy allows all principals, actions, and resources".to_string(),
            },
            SecurityPattern {
                id: "missing-time-constraints".to_string(),
                name: "Missing Time Constraints".to_string(),
                pattern: r#"permit.*when.*!.*time"#.to_string(),
                severity: WarningSeverity::Medium,
                description: "Consider adding time-based constraints for security".to_string(),
            },
            SecurityPattern {
                id: "hardcoded-credentials".to_string(),
                name: "Hardcoded Credentials".to_string(),
                pattern: r#"(password|secret|key|token)\s*==\s*"[^"]*""#.to_string(),
                severity: WarningSeverity::High,
                description: "Avoid hardcoding credentials in policies".to_string(),
            },
        ];
    }

    /// Validate policy content
    pub fn validate_policy_content(&self, content: &str) -> Result<ValidationResult> {
        let start_time = std::time::Instant::now();
        let mut result = ValidationResult {
            valid: true,
            errors: Vec::new(),
            warnings: Vec::new(),
            suggestions: Vec::new(),
            metrics: ValidationMetrics {
                total_policies: 1,
                valid_policies: 0,
                invalid_policies: 0,
                total_lines: content.lines().count(),
                complexity_score: 0.0,
                validation_time_ms: 0,
            },
        };

        // Parse policy for syntax validation
        let policy_id = PolicyId::new("validation_check");
        match Policy::parse(Some(policy_id), content) {
            Ok(policy) => {
                result.metrics.valid_policies = 1;

                // Run semantic validation
                self.validate_semantics(&policy, &mut result);

                // Run security validation
                self.validate_security(content, &mut result);

                // Run compliance validation
                self.validate_compliance(content, &mut result);

                // Calculate complexity score
                result.metrics.complexity_score = self.calculate_complexity_score(content);
            }
            Err(parse_error) => {
                result.valid = false;
                result.metrics.invalid_policies = 1;
                result.errors.push(ValidationError {
                    error_type: ValidationErrorType::SyntaxError,
                    message: parse_error.to_string(),
                    line: None, // Cedar doesn't provide line numbers in parse errors
                    column: None,
                    policy_id: Some("validation_check".to_string()),
                });
            }
        }

        result.metrics.validation_time_ms = start_time.elapsed().as_millis() as u64;
        Ok(result)
    }

    /// Validate directory of policies
    pub fn validate_directory(&self, dir: &Path) -> Result<ValidationResult> {
        let start_time = std::time::Instant::now();
        let mut combined_result = ValidationResult {
            valid: true,
            errors: Vec::new(),
            warnings: Vec::new(),
            suggestions: Vec::new(),
            metrics: ValidationMetrics {
                total_policies: 0,
                valid_policies: 0,
                invalid_policies: 0,
                total_lines: 0,
                complexity_score: 0.0,
                validation_time_ms: 0,
            },
        };

        for entry in std::fs::read_dir(dir)? {
            let entry = entry?;
            let path = entry.path();

            if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("cedar") {
                let content = std::fs::read_to_string(&path)?;
                let file_result = self.validate_policy_content(&content)?;

                // Merge results
                combined_result.valid &= file_result.valid;
                combined_result.errors.extend(file_result.errors);
                combined_result.warnings.extend(file_result.warnings);
                combined_result.suggestions.extend(file_result.suggestions);
                combined_result.metrics.total_policies += file_result.metrics.total_policies;
                combined_result.metrics.valid_policies += file_result.metrics.valid_policies;
                combined_result.metrics.invalid_policies += file_result.metrics.invalid_policies;
                combined_result.metrics.total_lines += file_result.metrics.total_lines;
                combined_result.metrics.complexity_score += file_result.metrics.complexity_score;
            }
        }

        // Average complexity score
        if combined_result.metrics.total_policies > 0 {
            combined_result.metrics.complexity_score /=
                combined_result.metrics.total_policies as f64;
        }

        combined_result.metrics.validation_time_ms = start_time.elapsed().as_millis() as u64;
        Ok(combined_result)
    }

    /// Validate policy semantics
    fn validate_semantics(&self, _policy: &Policy, result: &mut ValidationResult) {
        // Implementation would check for semantic issues
        // For now, just add placeholder validations

        // Check for common semantic issues
        result.warnings.push(ValidationWarning {
            warning_type: ValidationWarningType::MaintenanceIssue,
            message: "Consider adding comments for complex conditions".to_string(),
            policy_id: Some("semantic_check".to_string()),
            severity: WarningSeverity::Low,
        });
    }

    /// Validate security patterns
    fn validate_security(&self, content: &str, result: &mut ValidationResult) {
        use regex::Regex;

        for pattern in &self.security_patterns {
            if let Ok(regex) = Regex::new(&pattern.pattern) {
                if regex.is_match(content) {
                    result.warnings.push(ValidationWarning {
                        warning_type: ValidationWarningType::SecurityBestPractice,
                        message: format!("{}: {}", pattern.name, pattern.description),
                        policy_id: Some(pattern.id.clone()),
                        severity: pattern.severity.clone(),
                    });
                }
            }
        }
    }

    /// Validate compliance requirements
    fn validate_compliance(&self, content: &str, result: &mut ValidationResult) {
        use regex::Regex;

        // Check enabled compliance frameworks
        for (framework, rules) in &self.compliance_rules {
            for rule in rules {
                let Ok(regex) = Regex::new(&rule.pattern) else {
                    continue;
                };

                if rule.required && !regex.is_match(content) {
                    result.errors.push(ValidationError {
                        error_type: ValidationErrorType::ComplianceViolation,
                        message: format!("{} violation: {}", framework, rule.description),
                        line: None,
                        column: None,
                        policy_id: Some(rule.id.clone()),
                    });
                    result.valid = false;
                }
            }
        }
    }

    /// Calculate complexity score for policy
    fn calculate_complexity_score(&self, content: &str) -> f64 {
        let lines = content.lines().count() as f64;
        let conditions = content.matches("when").count() as f64;
        let operators = content.matches("&&").count() + content.matches("||").count();
        let nested_levels = self.count_nested_levels(content) as f64;

        // Simple complexity calculation
        (lines * 0.1) + (conditions * 0.5) + (operators as f64 * 0.3) + (nested_levels * 0.8)
    }

    /// Count nested levels in policy content
    fn count_nested_levels(&self, content: &str) -> usize {
        let mut max_depth = 0usize;
        let mut current_depth = 0usize;

        for char in content.chars() {
            match char {
                '(' | '{' => {
                    current_depth += 1;
                    max_depth = max_depth.max(current_depth);
                }
                ')' | '}' => {
                    current_depth = current_depth.saturating_sub(1);
                }
                _ => {}
            }
        }

        max_depth
    }

    /// Generate validation report
    pub fn generate_report(&self, result: &ValidationResult, format: &str) -> Result<String> {
        match format.to_lowercase().as_str() {
            "json" => Ok(serde_json::to_string_pretty(result)?),
            "text" => self.generate_text_report(result),
            "html" => self.generate_html_report(result),
            _ => Err(ControlCenterError::Http(http::HttpError::Validation(
                format!("Unsupported report format: {}", format),
            ))),
        }
    }

    /// Generate text report
    fn generate_text_report(&self, result: &ValidationResult) -> Result<String> {
        let mut report = String::new();

        report.push_str("Policy Validation Report\n");
        report.push_str("========================\n\n");
        report.push_str(&format!(
            "Status: {}\n",
            if result.valid { "VALID" } else { "INVALID" }
        ));
        report.push_str(&format!(
            "Total Policies: {}\n",
            result.metrics.total_policies
        ));
        report.push_str(&format!(
            "Valid Policies: {}\n",
            result.metrics.valid_policies
        ));
        report.push_str(&format!(
            "Invalid Policies: {}\n",
            result.metrics.invalid_policies
        ));
        report.push_str(&format!(
            "Complexity Score: {:.2}\n",
            result.metrics.complexity_score
        ));
        report.push_str(&format!(
            "Validation Time: {}ms\n\n",
            result.metrics.validation_time_ms
        ));

        if !result.errors.is_empty() {
            report.push_str("ERRORS:\n");
            for error in &result.errors {
                report.push_str(&format!("  - {}: {}\n", error.error_type, error.message));
            }
            report.push('\n');
        }

        if !result.warnings.is_empty() {
            report.push_str("WARNINGS:\n");
            for warning in &result.warnings {
                report.push_str(&format!(
                    "  - [{}] {}: {}\n",
                    warning.severity, warning.warning_type, warning.message
                ));
            }
            report.push('\n');
        }

        if !result.suggestions.is_empty() {
            report.push_str("SUGGESTIONS:\n");
            for suggestion in &result.suggestions {
                report.push_str(&format!(
                    "  - {}: {}\n",
                    suggestion.suggestion_type, suggestion.message
                ));
            }
        }

        Ok(report)
    }

    /// Generate HTML report
    fn generate_html_report(&self, result: &ValidationResult) -> Result<String> {
        let status_color = if result.valid { "green" } else { "red" };
        let status_text = if result.valid { "VALID" } else { "INVALID" };

        let html = format!(
            r#"
            <html>
            <head>
                <title>Policy Validation Report</title>
                <style>
                    body {{ font-family: Arial, sans-serif; margin: 20px; }}
                    .header {{ background-color: #f5f5f5; padding: 15px; border-radius: 5px; }}
                    .status {{ color: {}; font-weight: bold; }}
                    .section {{ margin: 20px 0; }}
                    .error {{ color: red; }}
                    .warning {{ color: orange; }}
                    .suggestion {{ color: blue; }}
                </style>
            </head>
            <body>
                <div class="header">
                    <h1>Policy Validation Report</h1>
                    <p><strong>Status:</strong> <span class="status">{}</span></p>
                    <p><strong>Total Policies:</strong> {}</p>
                    <p><strong>Valid Policies:</strong> {}</p>
                    <p><strong>Invalid Policies:</strong> {}</p>
                    <p><strong>Complexity Score:</strong> {:.2}</p>
                    <p><strong>Validation Time:</strong> {}ms</p>
                </div>
                <!-- Additional HTML content would be generated here -->
            </body>
            </html>
            "#,
            status_color,
            status_text,
            result.metrics.total_policies,
            result.metrics.valid_policies,
            result.metrics.invalid_policies,
            result.metrics.complexity_score,
            result.metrics.validation_time_ms
        );

        Ok(html)
    }
}

// Implement Display for enum types
impl std::fmt::Display for ValidationErrorType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ValidationErrorType::SyntaxError => write!(f, "Syntax Error"),
            ValidationErrorType::SemanticError => write!(f, "Semantic Error"),
            ValidationErrorType::ComplianceViolation => write!(f, "Compliance Violation"),
            ValidationErrorType::SecurityIssue => write!(f, "Security Issue"),
            ValidationErrorType::PerformanceIssue => write!(f, "Performance Issue"),
        }
    }
}

impl std::fmt::Display for ValidationWarningType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ValidationWarningType::DeprecatedSyntax => write!(f, "Deprecated Syntax"),
            ValidationWarningType::PerformanceImpact => write!(f, "Performance Impact"),
            ValidationWarningType::SecurityBestPractice => write!(f, "Security Best Practice"),
            ValidationWarningType::MaintenanceIssue => write!(f, "Maintenance Issue"),
        }
    }
}

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

impl std::fmt::Display for SuggestionType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SuggestionType::Optimization => write!(f, "Optimization"),
            SuggestionType::Security => write!(f, "Security"),
            SuggestionType::Readability => write!(f, "Readability"),
            SuggestionType::Maintenance => write!(f, "Maintenance"),
        }
    }
}
