//! Security Hardening for APT-OSTree
//! 
//! This module provides comprehensive security features including input validation,
//! privilege escalation protection, secure communication, and security scanning.

use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use tokio::sync::Mutex;
use serde::{Serialize, Deserialize};
use tracing::{warn, error, debug, instrument};
use regex::Regex;
use lazy_static::lazy_static;
use std::os::unix::fs::PermissionsExt;

use crate::error::{AptOstreeError, AptOstreeResult};

/// Security configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityConfig {
    /// Enable input validation
    pub enable_input_validation: bool,
    /// Enable privilege escalation protection
    pub enable_privilege_protection: bool,
    /// Enable secure communication
    pub enable_secure_communication: bool,
    /// Enable security scanning
    pub enable_security_scanning: bool,
    /// Allowed file paths for operations
    pub allowed_paths: Vec<String>,
    /// Blocked file paths
    pub blocked_paths: Vec<String>,
    /// Allowed package sources
    pub allowed_sources: Vec<String>,
    /// Blocked package sources
    pub blocked_sources: Vec<String>,
    /// Maximum file size for operations (bytes)
    pub max_file_size: u64,
    /// Maximum package count per operation
    pub max_package_count: u32,
    /// Security scan timeout (seconds)
    pub security_scan_timeout: u64,
}

impl Default for SecurityConfig {
    fn default() -> Self {
        Self {
            enable_input_validation: true,
            enable_privilege_protection: true,
            enable_secure_communication: true,
            enable_security_scanning: true,
            allowed_paths: vec![
                "/var/lib/apt-ostree".to_string(),
                "/etc/apt-ostree".to_string(),
                "/var/cache/apt-ostree".to_string(),
                "/var/log/apt-ostree".to_string(),
            ],
            blocked_paths: vec![
                "/etc/shadow".to_string(),
                "/etc/passwd".to_string(),
                "/etc/sudoers".to_string(),
                "/root".to_string(),
                "/home".to_string(),
            ],
            allowed_sources: vec![
                "deb.debian.org".to_string(),
                "archive.ubuntu.com".to_string(),
                "security.ubuntu.com".to_string(),
            ],
            blocked_sources: vec![
                "malicious.example.com".to_string(),
            ],
            max_file_size: 1024 * 1024 * 100, // 100MB
            max_package_count: 1000,
            security_scan_timeout: 300, // 5 minutes
        }
    }
}

/// Security validation result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityValidationResult {
    pub is_valid: bool,
    pub warnings: Vec<String>,
    pub errors: Vec<String>,
    pub security_score: u8, // 0-100
}

/// Security scanner for packages and files
#[derive(Debug, Clone)]
pub struct SecurityScanner {
    pub vulnerabilities: Vec<Vulnerability>,
    pub malware_signatures: Vec<String>,
    pub suspicious_patterns: Vec<Regex>,
}

/// Vulnerability information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Vulnerability {
    pub id: String,
    pub severity: VulnerabilitySeverity,
    pub description: String,
    pub cve_id: Option<String>,
    pub affected_packages: Vec<String>,
    pub remediation: String,
}

/// Vulnerability severity levels
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum VulnerabilitySeverity {
    Low,
    Medium,
    High,
    Critical,
}

/// Security manager
pub struct SecurityManager {
    config: SecurityConfig,
    scanner: SecurityScanner,
    validation_cache: Arc<Mutex<HashMap<String, SecurityValidationResult>>>,
}

impl SecurityManager {
    /// Create a new security manager
    pub fn new(config: SecurityConfig) -> Self {
        let scanner = SecurityScanner::new();
        Self {
            config,
            scanner,
            validation_cache: Arc::new(Mutex::new(HashMap::new())),
        }
    }

    /// Validate input parameters
    #[instrument(skip(self))]
    pub async fn validate_input(&self, input: &str, input_type: &str) -> AptOstreeResult<SecurityValidationResult> {
        debug!("Validating input: type={}, value={}", input_type, input);
        
        let mut result = SecurityValidationResult {
            is_valid: true,
            warnings: Vec::new(),
            errors: Vec::new(),
            security_score: 100,
        };

        if !self.config.enable_input_validation {
            return Ok(result);
        }

        // Check for path traversal attempts
        if self.contains_path_traversal(input) {
            result.is_valid = false;
            result.errors.push("Path traversal attempt detected".to_string());
            result.security_score = 0;
        }

        // Check for command injection attempts
        if self.contains_command_injection(input) {
            result.is_valid = false;
            result.errors.push("Command injection attempt detected".to_string());
            result.security_score = 0;
        }

        // Check for SQL injection attempts
        if self.contains_sql_injection(input) {
            result.is_valid = false;
            result.errors.push("SQL injection attempt detected".to_string());
            result.security_score = 0;
        }

        // Check for XSS attempts
        if self.contains_xss(input) {
            result.is_valid = false;
            result.errors.push("XSS attempt detected".to_string());
            result.security_score = 0;
        }

        // Validate file paths
        if input_type == "file_path" {
            if let Err(e) = self.validate_file_path(input) {
                result.is_valid = false;
                result.errors.push(format!("Invalid file path: {}", e));
                result.security_score = 0;
            }
        }

        // Validate package names
        if input_type == "package_name" {
            if let Err(e) = self.validate_package_name(input) {
                result.is_valid = false;
                result.errors.push(format!("Invalid package name: {}", e));
                result.security_score = 0;
            }
        }

        // Cache validation result
        let cache_key = format!("{}:{}", input_type, input);
        {
            let mut cache = self.validation_cache.lock().await;
            cache.insert(cache_key, result.clone());
        }

        if !result.is_valid {
            error!("Input validation failed: {:?}", result);
        }

        Ok(result)
    }

    /// Validate file path security
    pub fn validate_file_path(&self, path: &str) -> AptOstreeResult<()> {
        let path_buf = PathBuf::from(path);
        
        // Check for absolute path
        if path_buf.is_absolute() {
            // Check if path is in blocked paths
            for blocked_path in &self.config.blocked_paths {
                if path.starts_with(blocked_path) {
                    return Err(AptOstreeError::Security(
                        format!("Access to blocked path: {}", blocked_path)
                    ));
                }
            }
            
            // Check if path is in allowed paths
            let mut allowed = false;
            for allowed_path in &self.config.allowed_paths {
                if path.starts_with(allowed_path) {
                    allowed = true;
                    break;
                }
            }
            
            if !allowed {
                return Err(AptOstreeError::Security(
                    format!("Access to unauthorized path: {}", path)
                ));
            }
        }

        // Check for path traversal
        if path.contains("..") || path.contains("//") {
            return Err(AptOstreeError::Security(
                "Path traversal attempt detected".to_string()
            ));
        }

        Ok(())
    }

    /// Validate package name security
    pub fn validate_package_name(&self, package_name: &str) -> AptOstreeResult<()> {
        lazy_static! {
            static ref PACKAGE_NAME_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9][a-zA-Z0-9+.-]*$").unwrap();
        }

        if !PACKAGE_NAME_REGEX.is_match(package_name) {
            return Err(AptOstreeError::Security(
                format!("Invalid package name format: {}", package_name)
            ));
        }

        // Check for suspicious patterns
        let suspicious_patterns = [
            "..", "//", "\\", "|", "&", ";", "`", "$(", "eval", "exec",
        ];

        for pattern in &suspicious_patterns {
            if package_name.contains(pattern) {
                return Err(AptOstreeError::Security(
                    format!("Suspicious pattern in package name: {}", pattern)
                ));
            }
        }

        Ok(())
    }

    /// Check for path traversal attempts
    fn contains_path_traversal(&self, input: &str) -> bool {
        let traversal_patterns = [
            "..", "//", "\\", "~", "..\\", "../", "..\\",
        ];

        for pattern in &traversal_patterns {
            if input.contains(pattern) {
                return true;
            }
        }

        false
    }

    /// Check for command injection attempts
    fn contains_command_injection(&self, input: &str) -> bool {
        let injection_patterns = [
            "|", "&", ";", "`", "$(", "eval", "exec", "system", "popen",
            "shell_exec", "passthru", "proc_open", "pcntl_exec",
        ];

        for pattern in &injection_patterns {
            if input.contains(pattern) {
                return true;
            }
        }

        false
    }

    /// Check for SQL injection attempts
    fn contains_sql_injection(&self, input: &str) -> bool {
        let sql_patterns = [
            "SELECT", "INSERT", "UPDATE", "DELETE", "DROP", "CREATE",
            "UNION", "OR", "AND", "WHERE", "FROM", "JOIN",
        ];

        for pattern in &sql_patterns {
            if input.to_uppercase().contains(pattern) {
                return true;
            }
        }

        false
    }

    /// Check for XSS attempts
    fn contains_xss(&self, input: &str) -> bool {
        let xss_patterns = [
            "<script", "javascript:", "onload=", "onerror=", "onclick=",
            "onmouseover=", "onfocus=", "onblur=", "onchange=",
        ];

        for pattern in &xss_patterns {
            if input.to_lowercase().contains(pattern) {
                return true;
            }
        }

        false
    }

    /// Protect against privilege escalation
    #[instrument(skip(self))]
    pub async fn protect_privilege_escalation(&self) -> AptOstreeResult<()> {
        if !self.config.enable_privilege_protection {
            return Ok(());
        }

        debug!("Checking privilege escalation protection");

        // Check if running as root
        if unsafe { libc::geteuid() == 0 } {
            // Verify we're not in a privileged context that could be exploited
            if self.is_in_dangerous_context() {
                return Err(AptOstreeError::Security(
                    "Running in potentially dangerous privileged context".to_string()
                ));
            }
        }

        // Check for setuid binaries
        if self.has_setuid_binaries() {
            warn!("Setuid binaries detected - potential security risk");
        }

        // Check for world-writable directories
        if self.has_world_writable_dirs() {
            warn!("World-writable directories detected - potential security risk");
        }

        Ok(())
    }

    /// Check if running in dangerous context
    fn is_in_dangerous_context(&self) -> bool {
        // Check environment variables
        let dangerous_vars = [
            "LD_PRELOAD", "LD_LIBRARY_PATH", "PYTHONPATH", "PERL5LIB",
        ];

        for var in &dangerous_vars {
            if std::env::var(var).is_ok() {
                return true;
            }
        }

        // Check if running in container
        if self.is_container_environment() {
            return true;
        }

        false
    }

    /// Check for setuid binaries
    fn has_setuid_binaries(&self) -> bool {
        let setuid_paths = [
            "/usr/bin/sudo", "/usr/bin/su", "/usr/bin/passwd",
            "/usr/bin/chsh", "/usr/bin/chfn", "/usr/bin/gpasswd",
        ];

        for path in &setuid_paths {
            if Path::new(path).exists() {
                if let Ok(metadata) = std::fs::metadata(path) {
                    let mode = metadata.permissions().mode();
                    if (mode & 0o4000) != 0 {
                        return true;
                    }
                }
            }
        }

        false
    }

    /// Check for world-writable directories
    fn has_world_writable_dirs(&self) -> bool {
        let world_writable_paths = [
            "/tmp", "/var/tmp", "/dev/shm",
        ];

        for path in &world_writable_paths {
            if let Ok(metadata) = std::fs::metadata(path) {
                let mode = metadata.permissions().mode();
                if (mode & 0o0002) != 0 {
                    return true;
                }
            }
        }

        false
    }

    /// Check if running in container environment
    fn is_container_environment(&self) -> bool {
        let container_indicators = [
            "/.dockerenv",
            "/proc/1/cgroup",
            "/proc/self/cgroup",
        ];

        for indicator in &container_indicators {
            if Path::new(indicator).exists() {
                return true;
            }
        }

        // Check cgroup for container indicators
        if let Ok(content) = std::fs::read_to_string("/proc/self/cgroup") {
            if content.contains("docker") || content.contains("lxc") || content.contains("systemd") {
                return true;
            }
        }

        false
    }

    /// Scan package for security vulnerabilities
    #[instrument(skip(self))]
    pub async fn scan_package(&self, package_name: &str, package_path: &Path) -> AptOstreeResult<Vec<Vulnerability>> {
        if !self.config.enable_security_scanning {
            return Ok(Vec::new());
        }

        debug!("Scanning package for vulnerabilities: {}", package_name);

        let mut vulnerabilities = Vec::new();

        // Check file size
        if let Ok(metadata) = std::fs::metadata(package_path) {
            if metadata.len() > self.config.max_file_size {
                vulnerabilities.push(Vulnerability {
                    id: "FILE_SIZE_EXCEEDED".to_string(),
                    severity: VulnerabilitySeverity::Medium,
                    description: format!("Package file size exceeds limit: {} bytes", metadata.len()),
                    cve_id: None,
                    affected_packages: vec![package_name.to_string()],
                    remediation: "Reduce package size or increase limit".to_string(),
                });
            }
        }

        // Check for known vulnerabilities (placeholder for real vulnerability database)
        if let Some(vuln) = self.check_known_vulnerabilities(package_name).await {
            vulnerabilities.push(vuln);
        }

        // Check for malware signatures
        if let Some(vuln) = self.scan_for_malware(package_path).await {
            vulnerabilities.push(vuln);
        }

        // Check for suspicious patterns
        if let Some(vuln) = self.scan_for_suspicious_patterns(package_path).await {
            vulnerabilities.push(vuln);
        }

        if !vulnerabilities.is_empty() {
            warn!("Security vulnerabilities found in package {}: {:?}", package_name, vulnerabilities);
        }

        Ok(vulnerabilities)
    }

    /// Check for known vulnerabilities
    async fn check_known_vulnerabilities(&self, package_name: &str) -> Option<Vulnerability> {
        // This would integrate with a real vulnerability database
        // For now, return None as placeholder
        None
    }

    /// Scan for malware signatures
    async fn scan_for_malware(&self, package_path: &Path) -> Option<Vulnerability> {
        // This would integrate with malware scanning tools
        // For now, return None as placeholder
        None
    }

    /// Scan for suspicious patterns
    async fn scan_for_suspicious_patterns(&self, package_path: &Path) -> Option<Vulnerability> {
        // This would scan file contents for suspicious patterns
        // For now, return None as placeholder
        None
    }

    /// Validate secure communication
    #[instrument(skip(self))]
    pub async fn validate_secure_communication(&self, endpoint: &str) -> AptOstreeResult<()> {
        if !self.config.enable_secure_communication {
            return Ok(());
        }

        debug!("Validating secure communication to: {}", endpoint);

        // Check for HTTPS
        if !endpoint.starts_with("https://") {
            return Err(AptOstreeError::Security(
                "Non-HTTPS communication not allowed".to_string()
            ));
        }

        // Check for allowed sources
        let mut allowed = false;
        for allowed_source in &self.config.allowed_sources {
            if endpoint.contains(allowed_source) {
                allowed = true;
                break;
            }
        }

        if !allowed {
            return Err(AptOstreeError::Security(
                format!("Communication to unauthorized endpoint: {}", endpoint)
            ));
        }

        // Check for blocked sources
        for blocked_source in &self.config.blocked_sources {
            if endpoint.contains(blocked_source) {
                return Err(AptOstreeError::Security(
                    format!("Communication to blocked endpoint: {}", blocked_source)
                ));
            }
        }

        Ok(())
    }

    /// Get security report
    pub async fn get_security_report(&self) -> AptOstreeResult<String> {
        let mut report = String::new();
        report.push_str("=== APT-OSTree Security Report ===\n\n");

        // System security status
        report.push_str("System Security Status:\n");
        report.push_str(&format!("- Running as root: {}\n", unsafe { libc::geteuid() == 0 }));
        report.push_str(&format!("- Container environment: {}\n", self.is_container_environment()));
        report.push_str(&format!("- Setuid binaries detected: {}\n", self.has_setuid_binaries()));
        report.push_str(&format!("- World-writable directories: {}\n", self.has_world_writable_dirs()));

        // Configuration status
        report.push_str("\nSecurity Configuration:\n");
        report.push_str(&format!("- Input validation: {}\n", self.config.enable_input_validation));
        report.push_str(&format!("- Privilege protection: {}\n", self.config.enable_privilege_protection));
        report.push_str(&format!("- Secure communication: {}\n", self.config.enable_secure_communication));
        report.push_str(&format!("- Security scanning: {}\n", self.config.enable_security_scanning));

        // Validation cache statistics
        {
            let cache = self.validation_cache.lock().await;
            report.push_str(&format!("\nValidation Cache:\n"));
            report.push_str(&format!("- Cached validations: {}\n", cache.len()));
        }

        Ok(report)
    }
}

impl SecurityScanner {
    /// Create a new security scanner
    pub fn new() -> Self {
        let suspicious_patterns = vec![
            Regex::new(r"\.\./").unwrap(),
            Regex::new(r"\.\.\\").unwrap(),
            Regex::new(r"[|&;`$]").unwrap(),
            Regex::new(r"eval\s*\(").unwrap(),
            Regex::new(r"exec\s*\(").unwrap(),
        ];
        
        Self {
            vulnerabilities: Vec::new(),
            malware_signatures: Vec::new(),
            suspicious_patterns,
        }
    }
}

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

    #[tokio::test]
    async fn test_input_validation() {
        let config = SecurityConfig::default();
        let security_manager = SecurityManager::new(config);

        // Test valid input
        let result = security_manager.validate_input("valid-package-name", "package_name").await.unwrap();
        assert!(result.is_valid);

        // Test path traversal
        let result = security_manager.validate_input("../../../etc/passwd", "file_path").await.unwrap();
        assert!(!result.is_valid);

        // Test command injection
        let result = security_manager.validate_input("package; rm -rf /", "package_name").await.unwrap();
        assert!(!result.is_valid);
    }

    #[tokio::test]
    async fn test_file_path_validation() {
        let config = SecurityConfig::default();
        let security_manager = SecurityManager::new(config);

        // Test allowed path
        assert!(security_manager.validate_file_path("/var/lib/apt-ostree/test").is_ok());

        // Test blocked path
        assert!(security_manager.validate_file_path("/etc/shadow").is_err());

        // Test path traversal
        assert!(security_manager.validate_file_path("../../../etc/passwd").is_err());
    }

    #[tokio::test]
    async fn test_package_name_validation() {
        let config = SecurityConfig::default();
        let security_manager = SecurityManager::new(config);

        // Test valid package name
        assert!(security_manager.validate_package_name("valid-package").is_ok());

        // Test invalid package name
        assert!(security_manager.validate_package_name("package; rm -rf /").is_err());
    }
} 