"""
Configuration management for MCP Security Auditor.

Handles loading and parsing of audit configuration files.
"""

from dataclasses import dataclass, field
from typing import Dict, List, Any, Optional
from pathlib import Path
import yaml
import json
import os


@dataclass
class AnalyzerConfig:
    """Configuration for individual analyzers."""
    enabled: bool = True
    custom_rules: List[Dict[str, Any]] = field(default_factory=list)
    options: Dict[str, Any] = field(default_factory=dict)


@dataclass
class DangerousPattern:
    """A dangerous code pattern to detect."""
    pattern: str
    severity: str
    description: str
    cwe_id: Optional[str] = None
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "DangerousPattern":
        return cls(
            pattern=data["pattern"],
            severity=data.get("severity", "medium"),
            description=data.get("description", ""),
            cwe_id=data.get("cwe_id")
        )


@dataclass
class SecretPattern:
    """A pattern for detecting secrets."""
    name: str
    pattern: str
    severity: str = "high"
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "SecretPattern":
        return cls(
            name=data["name"],
            pattern=data["pattern"],
            severity=data.get("severity", "high")
        )


@dataclass
class AuditConfig:
    """Main configuration for the security audit."""
    
    # Severity threshold
    severity_threshold: str = "info"
    
    # Analyzer settings
    analyzers: Dict[str, bool] = field(default_factory=lambda: {
        "static": True,
        "permissions": True,
        "network": True,
        "dependencies": True,
        "injection": True,
        "config": True,
        "secrets": True,
        "readonly": True
    })
    
    # Dangerous patterns for static analysis
    dangerous_patterns: List[DangerousPattern] = field(default_factory=list)
    
    # Allowed domains for network analysis
    allowed_domains: List[str] = field(default_factory=list)
    
    # Blocked domains (known malicious)
    blocked_domains: List[str] = field(default_factory=list)
    
    # Required permissions
    required_permissions: List[str] = field(default_factory=list)
    
    # Forbidden permissions
    forbidden_permissions: List[str] = field(default_factory=list)
    
    # Secret patterns
    secret_patterns: List[SecretPattern] = field(default_factory=list)
    
    # Dependency settings
    check_vulnerabilities: bool = True
    vulnerability_sources: List[str] = field(default_factory=lambda: ["osv", "nvd"])
    
    # CI/CD settings
    ci_fail_on: str = "high"
    ci_output_format: str = "sarif"
    
    # SIEM settings
    siem_format: str = "cef"
    siem_vendor: str = "MCPAuditor"
    siem_product: str = "MCP Security Auditor"
    siem_version: str = "1.0"
    
    # Report settings
    include_remediation: bool = True
    include_code_snippets: bool = True
    max_snippet_lines: int = 10
    
    # File patterns to include/exclude
    include_patterns: List[str] = field(default_factory=lambda: [
        "*.py", "*.ts", "*.js", "*.mjs", "*.cjs",
        "*.json", "*.yaml", "*.yml", "*.toml"
    ])
    exclude_patterns: List[str] = field(default_factory=lambda: [
        "node_modules/**", "__pycache__/**", ".git/**",
        "*.min.js", "*.bundle.js", "dist/**", "build/**"
    ])
    
    @classmethod
    def load(cls, config_path: Optional[str] = None) -> "AuditConfig":
        """Load configuration from file or use defaults."""
        config = cls()
        config._load_default_patterns()
        
        if config_path:
            path = Path(config_path)
            if path.exists():
                config._load_from_file(path)
        else:
            # Try common config file names
            for name in [".mcp-audit.yaml", ".mcp-audit.yml", "mcp-audit.config.yaml"]:
                if Path(name).exists():
                    config._load_from_file(Path(name))
                    break
        
        return config
    
    def _load_default_patterns(self):
        """Load default dangerous patterns and secret patterns."""
        # Default dangerous patterns
        self.dangerous_patterns = [
            # Code execution
            DangerousPattern(
                pattern=r"\beval\s*\(",
                severity="critical",
                description="Dynamic code execution via eval()",
                cwe_id="CWE-95"
            ),
            DangerousPattern(
                pattern=r"\bexec\s*\(",
                severity="critical",
                description="System command execution via exec()",
                cwe_id="CWE-78"
            ),
            DangerousPattern(
                pattern=r"subprocess\.(call|run|Popen)",
                severity="high",
                description="Subprocess execution detected",
                cwe_id="CWE-78"
            ),
            DangerousPattern(
                pattern=r"os\.system\s*\(",
                severity="critical",
                description="OS command execution via os.system()",
                cwe_id="CWE-78"
            ),
            DangerousPattern(
                pattern=r"child_process\.(exec|spawn|execSync)",
                severity="critical",
                description="Child process execution (Node.js)",
                cwe_id="CWE-78"
            ),
            
            # File system access
            DangerousPattern(
                pattern=r"fs\.(readFile|writeFile|unlink|rmdir)",
                severity="medium",
                description="Direct filesystem access (Node.js)",
                cwe_id="CWE-22"
            ),
            DangerousPattern(
                pattern=r"open\s*\([^)]*['\"]w['\"]",
                severity="medium",
                description="File write operation",
                cwe_id="CWE-22"
            ),
            DangerousPattern(
                pattern=r"shutil\.(rmtree|move|copy)",
                severity="medium",
                description="File manipulation via shutil",
                cwe_id="CWE-22"
            ),
            
            # Dangerous imports
            DangerousPattern(
                pattern=r"import\s+pickle|from\s+pickle\s+import",
                severity="high",
                description="Pickle deserialization (potential RCE)",
                cwe_id="CWE-502"
            ),
            DangerousPattern(
                pattern=r"yaml\.load\s*\([^)]*\)",
                severity="high",
                description="Unsafe YAML loading (use safe_load)",
                cwe_id="CWE-502"
            ),
            DangerousPattern(
                pattern=r"yaml\.unsafe_load",
                severity="critical",
                description="Explicitly unsafe YAML loading",
                cwe_id="CWE-502"
            ),
            
            # SQL Injection
            DangerousPattern(
                pattern=r"f['\"].*SELECT.*{.*}",
                severity="high",
                description="Potential SQL injection via f-string",
                cwe_id="CWE-89"
            ),
            DangerousPattern(
                pattern=r"\.format\(.*\).*SELECT|SELECT.*\.format\(",
                severity="high",
                description="Potential SQL injection via format()",
                cwe_id="CWE-89"
            ),
            
            # Sensitive data exposure
            DangerousPattern(
                pattern=r"print\s*\(.*password|password.*print\s*\(",
                severity="medium",
                description="Password potentially logged",
                cwe_id="CWE-532"
            ),
            DangerousPattern(
                pattern=r"console\.(log|debug|info)\s*\(.*password",
                severity="medium",
                description="Password potentially logged (JS)",
                cwe_id="CWE-532"
            ),
            
            # Insecure cryptography
            DangerousPattern(
                pattern=r"hashlib\.md5\s*\(|MD5\s*\(",
                severity="medium",
                description="Weak hash algorithm (MD5)",
                cwe_id="CWE-328"
            ),
            DangerousPattern(
                pattern=r"hashlib\.sha1\s*\(|SHA1\s*\(",
                severity="medium",
                description="Weak hash algorithm (SHA1)",
                cwe_id="CWE-328"
            ),
            
            # Network issues
            DangerousPattern(
                pattern=r"verify\s*=\s*False",
                severity="high",
                description="SSL certificate verification disabled",
                cwe_id="CWE-295"
            ),
            DangerousPattern(
                pattern=r"rejectUnauthorized\s*:\s*false",
                severity="high",
                description="SSL certificate verification disabled (Node.js)",
                cwe_id="CWE-295"
            ),
            
            # Prompt injection risks
            DangerousPattern(
                pattern=r"\.format\([^)]*user_input|f['\"].*{user_input}",
                severity="high",
                description="User input in formatted string (injection risk)",
                cwe_id="CWE-94"
            ),
            DangerousPattern(
                pattern=r"prompt\s*\+\s*user|user.*\+.*prompt",
                severity="medium",
                description="User input concatenated with prompt",
                cwe_id="CWE-94"
            )
        ]
        
        # Default secret patterns
        self.secret_patterns = [
            SecretPattern(
                name="AWS Access Key",
                pattern=r"AKIA[0-9A-Z]{16}",
                severity="critical"
            ),
            SecretPattern(
                name="AWS Secret Key",
                pattern=r"(?i)aws(.{0,20})?(?-i)['\"][0-9a-zA-Z/+]{40}['\"]",
                severity="critical"
            ),
            SecretPattern(
                name="GitHub Token",
                pattern=r"gh[pousr]_[A-Za-z0-9_]{36,}",
                severity="critical"
            ),
            SecretPattern(
                name="Generic API Key",
                pattern=r"(?i)(api[_-]?key|apikey)\s*[=:]\s*['\"][A-Za-z0-9_\-]{16,}['\"]",
                severity="high"
            ),
            SecretPattern(
                name="Generic Secret",
                pattern=r"(?i)(secret|token|password)\s*[=:]\s*['\"][^'\"]{8,}['\"]",
                severity="high"
            ),
            SecretPattern(
                name="Private Key",
                pattern=r"-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----",
                severity="critical"
            ),
            SecretPattern(
                name="Slack Token",
                pattern=r"xox[baprs]-[0-9]{10,13}-[0-9]{10,13}[a-zA-Z0-9-]*",
                severity="critical"
            ),
            SecretPattern(
                name="Stripe API Key",
                pattern=r"sk_live_[0-9a-zA-Z]{24,}",
                severity="critical"
            ),
            SecretPattern(
                name="OpenAI API Key",
                pattern=r"sk-[A-Za-z0-9]{48}",
                severity="critical"
            ),
            SecretPattern(
                name="Anthropic API Key",
                pattern=r"sk-ant-[A-Za-z0-9\-_]{32,}",
                severity="critical"
            ),
            SecretPattern(
                name="Google API Key",
                pattern=r"AIza[0-9A-Za-z_-]{35}",
                severity="high"
            ),
            SecretPattern(
                name="JWT Token",
                pattern=r"eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+",
                severity="high"
            ),
            SecretPattern(
                name="Database URL",
                pattern=r"(?i)(postgres|mysql|mongodb|redis)://[^:]+:[^@]+@",
                severity="critical"
            ),
            SecretPattern(
                name="Basic Auth Header",
                pattern=r"(?i)authorization:\s*basic\s+[A-Za-z0-9+/]+=*",
                severity="high"
            )
        ]
        
        # Default allowed domains (common safe domains)
        self.allowed_domains = [
            "api.anthropic.com",
            "modelcontextprotocol.io",
            "api.openai.com",
            "api.github.com",
            "registry.npmjs.org",
            "pypi.org"
        ]
        
        # Default forbidden permissions
        self.forbidden_permissions = [
            "system.shell",
            "system.root",
            "filesystem.root",
            "network.unrestricted"
        ]
    
    def _load_from_file(self, path: Path):
        """Load configuration from a file."""
        with open(path) as f:
            if path.suffix in [".yaml", ".yml"]:
                data = yaml.safe_load(f)
            else:
                data = json.load(f)
        
        if not data:
            return
        
        # Update basic settings
        self.severity_threshold = data.get("severity_threshold", self.severity_threshold)
        
        # Update analyzers
        if "analyzers" in data:
            for name, enabled in data["analyzers"].items():
                self.analyzers[name] = enabled
        
        # Update rules
        if "rules" in data:
            rules = data["rules"]
            
            if "dangerous_patterns" in rules:
                for pattern_data in rules["dangerous_patterns"]:
                    self.dangerous_patterns.append(DangerousPattern.from_dict(pattern_data))
            
            if "allowed_domains" in rules:
                self.allowed_domains.extend(rules["allowed_domains"])
            
            if "blocked_domains" in rules:
                self.blocked_domains.extend(rules["blocked_domains"])
            
            if "required_permissions" in rules:
                self.required_permissions.extend(rules["required_permissions"])
            
            if "forbidden_permissions" in rules:
                self.forbidden_permissions.extend(rules["forbidden_permissions"])
        
        # Update dependencies settings
        if "dependencies" in data:
            deps = data["dependencies"]
            self.check_vulnerabilities = deps.get("check_vulnerabilities", self.check_vulnerabilities)
            self.vulnerability_sources = deps.get("vulnerability_sources", self.vulnerability_sources)
        
        # Update secrets patterns
        if "secrets" in data and "patterns" in data["secrets"]:
            for pattern_data in data["secrets"]["patterns"]:
                self.secret_patterns.append(SecretPattern.from_dict(pattern_data))
        
        # Update CI settings
        if "ci" in data:
            self.ci_fail_on = data["ci"].get("fail_on", self.ci_fail_on)
            self.ci_output_format = data["ci"].get("output_format", self.ci_output_format)
        
        # Update SIEM settings
        if "siem" in data:
            self.siem_format = data["siem"].get("format", self.siem_format)
            self.siem_vendor = data["siem"].get("vendor", self.siem_vendor)
            self.siem_product = data["siem"].get("product", self.siem_product)
            self.siem_version = data["siem"].get("version", self.siem_version)
        
        # Update report settings
        if "report" in data:
            self.include_remediation = data["report"].get("include_remediation", self.include_remediation)
            self.include_code_snippets = data["report"].get("include_code_snippets", self.include_code_snippets)
            self.max_snippet_lines = data["report"].get("max_snippet_lines", self.max_snippet_lines)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert config to dictionary."""
        return {
            "severity_threshold": self.severity_threshold,
            "analyzers": self.analyzers,
            "dangerous_patterns": [
                {"pattern": p.pattern, "severity": p.severity, "description": p.description}
                for p in self.dangerous_patterns
            ],
            "allowed_domains": self.allowed_domains,
            "blocked_domains": self.blocked_domains,
            "required_permissions": self.required_permissions,
            "forbidden_permissions": self.forbidden_permissions,
            "check_vulnerabilities": self.check_vulnerabilities,
            "vulnerability_sources": self.vulnerability_sources,
            "ci_fail_on": self.ci_fail_on,
            "ci_output_format": self.ci_output_format,
            "siem_format": self.siem_format
        }
