"""
Multi-Language Code Verifier: Security Analysis for Code Snippets.

Supports: Python, JavaScript/TypeScript, Java, Go, SQL
Provides static analysis and security verification for code generated by LLMs.
"""

from typing import Dict, Any, List, Set, Optional
from dataclasses import dataclass, field
import re


@dataclass
class SecurityIssue:
    """A detected security issue."""
    severity: str  # "CRITICAL", "WARNING", "INFO"
    issue_type: str
    pattern: str
    description: str
    line_number: Optional[int] = None
    recommendation: Optional[str] = None


@dataclass
class VerificationResult:
    """Result of code verification."""
    is_safe: bool
    status: str  # "SAFE", "REVIEW", "BLOCKED"
    language: str
    issues: List[SecurityIssue] = field(default_factory=list)
    critical_count: int = 0
    warning_count: int = 0
    info_count: int = 0


class CodeVerifier:
    """
    Multi-Language Code Security Verifier.
    
    Engine 6 of QWED's verification architecture.
    
    Supported Languages:
    - Python: eval, exec, pickle, weak crypto, subprocess
    - JavaScript/TypeScript: eval, innerHTML, XSS, prototype pollution
    - Java: SQL injection, deserialization, SSRF
    - Go: command injection, path traversal
    - SQL: injection patterns (delegated to SQLVerifier)

    Attributes:
        supported_languages (List[str]): List of supported programming languages.
    """
    
    # =========================================================================
    # Python Patterns
    # =========================================================================
    
    PYTHON_CRITICAL = {
        "eval": "Remote code execution via eval()",
        "exec": "Remote code execution via exec()",
        "compile": "Dynamic code compilation",
        "__import__": "Dynamic module import",
        "pickle.loads": "Insecure deserialization",
        "pickle.load": "Insecure deserialization",
        "yaml.unsafe_load": "Insecure YAML parsing",
        "yaml.load": "Potential insecure YAML (use safe_load)",
        "getattr": "Dynamic attribute access",
        "setattr": "Dynamic attribute modification",
    }
    
    PYTHON_WARNING = {
        "open": "File operation (verify path source)",
        "os.system": "Shell command execution",
        "os.popen": "Shell command execution",
        "subprocess.call": "Subprocess execution",
        "subprocess.Popen": "Subprocess execution",
        "subprocess.run": "Subprocess execution",
        "shutil.rmtree": "Recursive directory deletion",
        "requests.get": "HTTP request (verify URL source)",
        "requests.post": "HTTP request (verify URL source)",
        "socket.socket": "Raw socket access",
    }
    
    PYTHON_WEAK_CRYPTO = {
        "hashlib.md5": "MD5 is cryptographically broken",
        "hashlib.sha1": "SHA1 is cryptographically weak",
        "random.random": "Not cryptographically secure (use secrets)",
        "random.randint": "Not cryptographically secure (use secrets)",
    }
    
    PYTHON_DANGEROUS_ATTRS = {
        "__class__", "__base__", "__subclasses__", "__globals__",
        "__builtins__", "__import__", "__code__", "__dict__"
    }
    
    # =========================================================================
    # JavaScript/TypeScript Patterns
    # =========================================================================
    
    JS_CRITICAL = {
        "eval(": "Remote code execution via eval()",
        "Function(": "Dynamic function creation",
        "setTimeout(": "Dynamic code execution (if string arg)",
        "setInterval(": "Dynamic code execution (if string arg)",
        ".innerHTML": "XSS vulnerability (use textContent)",
        ".outerHTML": "XSS vulnerability",
        "document.write": "XSS vulnerability",
        "document.writeln": "XSS vulnerability",
    }
    
    JS_WARNING = {
        "dangerouslySetInnerHTML": "React XSS risk (intentional bypass)",
        "child_process.exec": "Command injection risk",
        "child_process.spawn": "Command execution (verify args)",
        "fs.readFile": "File read (verify path source)",
        "fs.writeFile": "File write (verify path source)",
        "fs.unlink": "File deletion (verify path source)",
        "require(": "Dynamic module loading (if variable)",
        "import(": "Dynamic import (if variable)",
    }
    
    JS_PROTOTYPE_POLLUTION = {
        "__proto__": "Prototype pollution vector",
        "constructor": "Prototype pollution via constructor",
        "Object.assign": "Potential prototype pollution (validate source)",
    }
    
    JS_SECRETS = {
        r"api[_-]?key\s*[=:]\s*['\"][^'\"]+['\"]": "Hardcoded API key",
        r"secret\s*[=:]\s*['\"][^'\"]+['\"]": "Hardcoded secret",
        r"password\s*[=:]\s*['\"][^'\"]+['\"]": "Hardcoded password",
        r"token\s*[=:]\s*['\"][^'\"]+['\"]": "Hardcoded token",
        r"aws_access_key": "AWS access key",
        r"aws_secret": "AWS secret key",
    }
    
    # =========================================================================
    # Java Patterns
    # =========================================================================
    
    JAVA_CRITICAL = {
        "Runtime.getRuntime().exec": "Command injection",
        "ProcessBuilder": "Command execution",
        "ObjectInputStream": "Insecure deserialization",
        "XMLDecoder": "XML deserialization attack",
        "ScriptEngine": "Script execution",
        "java.lang.reflect": "Reflection (verify necessity)",
    }
    
    JAVA_SQL_INJECTION = {
        r'\+\s*["\'][^"\']*["\']': "String concatenation in SQL",
        r'Statement\s+\w+\s*=': "Statement (use PreparedStatement)",
        r'createStatement\s*\(': "Raw statement (use PreparedStatement)",
        r'executeQuery\s*\([^"]*\+': "SQL injection via concatenation",
    }
    
    JAVA_WARNING = {
        "new URL(": "SSRF risk (validate URL source)",
        "HttpURLConnection": "HTTP request (validate URL)",
        "SSLContext": "Custom SSL (verify configuration)",
        "TrustManager": "Custom trust (potential MITM)",
        "X509Certificate": "Certificate handling",
        "File(": "File operation (validate path)",
        "FileInputStream": "File read (validate path)",
        "FileOutputStream": "File write (validate path)",
    }
    
    # =========================================================================
    # Go Patterns
    # =========================================================================
    
    GO_CRITICAL = {
        "os/exec": "Command execution",
        "exec.Command": "Command injection risk",
        "syscall.Exec": "System call execution",
        "unsafe.Pointer": "Unsafe memory operation",
    }
    
    GO_WARNING = {
        "os.Open": "File operation (validate path)",
        "os.Create": "File creation (validate path)",
        "os.Remove": "File deletion (validate path)",
        "ioutil.ReadFile": "File read (validate path)",
        "ioutil.WriteFile": "File write (validate path)",
        "http.Get": "HTTP request (validate URL)",
        "http.Post": "HTTP request (validate URL)",
        "filepath.Join": "Path joining (check for traversal)",
    }
    
    GO_SQL_INJECTION = {
        r'fmt\.Sprintf\s*\([^)]*\%': "String formatting in SQL (use params)",
        r'\+\s*"[^"]*"': "String concatenation (potential SQL injection)",
    }
    
    # =========================================================================
    # Cross-Language Secret Patterns
    # =========================================================================
    
    SECRET_PATTERNS = {
        r'AKIA[0-9A-Z]{16}': "AWS Access Key ID",
        r'[a-zA-Z0-9+/]{40}': "Potential AWS Secret Key",
        r'ghp_[a-zA-Z0-9]{36}': "GitHub Personal Access Token",
        r'gho_[a-zA-Z0-9]{36}': "GitHub OAuth Token",
        r'sk-[a-zA-Z0-9]{48}': "OpenAI API Key",
        r'xox[baprs]-[0-9a-zA-Z-]+': "Slack Token",
        r'-----BEGIN (RSA |EC )?PRIVATE KEY-----': "Private Key",
        r'eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+': "JWT Token",
    }
    
    # =========================================================================
    # Password Context Indicators
    # =========================================================================
    
    PASSWORD_INDICATORS = {
        "password", "passwd", "pwd", "pass",
        "credential", "cred", "auth",
        "secret", "token", "key"
    }
    
    def __init__(self):
        """
        Initialize the multi-language code verifier.

        Example:
            >>> verifier = CodeVerifier()
        """
        self.supported_languages = ["python", "javascript", "typescript", "java", "go", "sql"]
        self._taint_analyzer = None  # Lazy-loaded
    
    @property
    def taint_analyzer(self):
        """Lazy-load taint analyzer for Python deep analysis."""
        if self._taint_analyzer is None:
            from .taint_analyzer import TaintAnalyzer
            self._taint_analyzer = TaintAnalyzer()
        return self._taint_analyzer
    
    def verify_code(self, code: str, language: str = "python") -> Dict[str, Any]:
        """
        Verify code for security vulnerabilities.
        
        Args:
            code: The code snippet to verify.
            language: Programming language (default: "python").
            
        Returns:
            Dict with verification results including safety status and issues list.

        Example:
            >>> result = verifier.verify_code("eval('rm -rf /')", language="python")
            >>> print(result["is_safe"])
            False
        """
        language = language.lower()
        
        # Map typescript to javascript
        if language == "typescript" or language == "ts":
            language = "javascript"
        if language == "js":
            language = "javascript"
        if language == "py":
            language = "python"
        
        if language not in self.supported_languages:
            return {
                "is_safe": False,
                "status": "ERROR",
                "error": f"Language '{language}' not supported. Supported: {self.supported_languages}",
                "issues": [],
                "language": language
            }
        
        issues = []
        
        # Language-specific checks
        if language == "python":
            issues.extend(self._check_python(code))
        elif language == "javascript":
            issues.extend(self._check_javascript(code))
        elif language == "java":
            issues.extend(self._check_java(code))
        elif language == "go":
            issues.extend(self._check_go(code))
        elif language == "sql":
            issues.extend(self._check_sql(code))
        
        # Cross-language secret detection
        issues.extend(self._check_secrets(code))
        
        # Calculate counts
        critical_count = sum(1 for i in issues if i.severity == "CRITICAL")
        warning_count = sum(1 for i in issues if i.severity == "WARNING")
        info_count = sum(1 for i in issues if i.severity == "INFO")
        
        # Determine status
        if critical_count > 0:
            status = "BLOCKED"
            is_safe = False
        elif warning_count > 0:
            status = "REVIEW"
            is_safe = True
        else:
            status = "SAFE"
            is_safe = True
        
        return {
            "is_safe": is_safe,
            "status": status,
            "language": language,
            "issues": [
                {
                    "severity": i.severity,
                    "type": i.issue_type,
                    "pattern": i.pattern,
                    "description": i.description,
                    "line_number": i.line_number,
                    "recommendation": i.recommendation
                }
                for i in issues
            ],
            "critical_count": critical_count,
            "warning_count": warning_count,
            "info_count": info_count
        }
    
    # =========================================================================
    # Python Checks
    # =========================================================================
    
    def _check_python(self, code: str) -> List[SecurityIssue]:
        """Check Python code for security issues."""
        issues = []
        lines = code.split('\n')
        
        for line_num, line in enumerate(lines, 1):
            # Critical functions
            for pattern, desc in self.PYTHON_CRITICAL.items():
                if pattern in line:
                    issues.append(SecurityIssue(
                        severity="CRITICAL",
                        issue_type="dangerous_function",
                        pattern=pattern,
                        description=desc,
                        line_number=line_num,
                        recommendation="Remove or sandbox this operation"
                    ))
            
            # Warning functions
            for pattern, desc in self.PYTHON_WARNING.items():
                if pattern in line:
                    issues.append(SecurityIssue(
                        severity="WARNING",
                        issue_type="context_dependent",
                        pattern=pattern,
                        description=desc,
                        line_number=line_num,
                        recommendation="Verify input source is trusted"
                    ))
            
            # Dangerous attributes
            for attr in self.PYTHON_DANGEROUS_ATTRS:
                if attr in line:
                    issues.append(SecurityIssue(
                        severity="CRITICAL",
                        issue_type="dangerous_attribute",
                        pattern=attr,
                        description=f"Dangerous attribute access: {attr}",
                        line_number=line_num
                    ))
        
        # Weak crypto in password context
        code_lower = code.lower()
        has_password = any(p in code_lower for p in self.PASSWORD_INDICATORS)
        if has_password:
            for pattern, desc in self.PYTHON_WEAK_CRYPTO.items():
                if pattern in code:
                    issues.append(SecurityIssue(
                        severity="CRITICAL",
                        issue_type="weak_crypto_with_password",
                        pattern=pattern,
                        description=f"{desc} - used in password context",
                        recommendation="Use bcrypt, argon2, or scrypt for passwords"
                    ))
        
        return issues
    
    def verify_python_deep(self, code: str) -> Dict[str, Any]:
        """
        Deep Python verification combining pattern matching with AST taint analysis.
        
        This method performs two types of analysis:
        1. Pattern-based detection (fast, catches known dangerous patterns)
        2. Taint analysis (AST-based, tracks data flow from sources to sinks)
        
        Args:
            code: Python source code to analyze.
            
        Returns:
            Dict with combined verification results.
            
        Example:
            >>> result = verifier.verify_python_deep("x = input(); eval(x)")
            >>> print(result["taint_vulnerabilities"])
        """
        # Standard pattern-based check
        pattern_result = self.verify_code(code, "python")
        
        # AST-based taint analysis
        taint_result = self.taint_analyzer.analyze(code)
        
        # Combine results
        is_safe = pattern_result["is_safe"] and taint_result["is_safe"]
        
        return {
            "is_safe": is_safe,
            "status": "SAFE" if is_safe else "VULNERABLE",
            "language": "python",
            "analysis_methods": ["pattern_matching", "taint_analysis"],
            # Pattern-based results
            "pattern_issues": pattern_result["issues"],
            "pattern_critical": pattern_result["critical_count"],
            "pattern_warnings": pattern_result["warning_count"],
            # Taint analysis results  
            "taint_vulnerabilities": taint_result["vulnerabilities"],
            "tainted_variables": taint_result["tainted_variables"],
            "data_flow_sources": taint_result["sources_found"],
            "data_flow_sinks": taint_result["sinks_found"],
            # Summary
            "summary": {
                "pattern_issues_found": len(pattern_result["issues"]),
                "taint_vulnerabilities_found": len(taint_result["vulnerabilities"]),
                "tainted_variable_count": len(taint_result["tainted_variables"]),
                "is_safe": is_safe
            }
        }
    
    # =========================================================================
    # JavaScript/TypeScript Checks
    # =========================================================================
    
    def _check_javascript(self, code: str) -> List[SecurityIssue]:
        """Check JavaScript/TypeScript code for security issues."""
        issues = []
        lines = code.split('\n')
        
        for line_num, line in enumerate(lines, 1):
            # Critical patterns (XSS, eval)
            for pattern, desc in self.JS_CRITICAL.items():
                if pattern in line:
                    issues.append(SecurityIssue(
                        severity="CRITICAL",
                        issue_type="xss_or_rce",
                        pattern=pattern,
                        description=desc,
                        line_number=line_num,
                        recommendation="Use textContent or sanitize input"
                    ))
            
            # Warning patterns
            for pattern, desc in self.JS_WARNING.items():
                if pattern in line:
                    issues.append(SecurityIssue(
                        severity="WARNING",
                        issue_type="context_dependent",
                        pattern=pattern,
                        description=desc,
                        line_number=line_num
                    ))
            
            # Prototype pollution
            for pattern, desc in self.JS_PROTOTYPE_POLLUTION.items():
                if pattern in line:
                    issues.append(SecurityIssue(
                        severity="CRITICAL",
                        issue_type="prototype_pollution",
                        pattern=pattern,
                        description=desc,
                        line_number=line_num,
                        recommendation="Validate object keys against allowlist"
                    ))
        
        # Hardcoded secrets
        for pattern, desc in self.JS_SECRETS.items():
            matches = re.finditer(pattern, code, re.IGNORECASE)
            for match in matches:
                line_num = code[:match.start()].count('\n') + 1
                issues.append(SecurityIssue(
                    severity="CRITICAL",
                    issue_type="hardcoded_secret",
                    pattern=match.group(0)[:30] + "...",
                    description=desc,
                    line_number=line_num,
                    recommendation="Use environment variables"
                ))
        
        return issues
    
    # =========================================================================
    # Java Checks
    # =========================================================================
    
    def _check_java(self, code: str) -> List[SecurityIssue]:
        """Check Java code for security issues."""
        issues = []
        lines = code.split('\n')
        
        for line_num, line in enumerate(lines, 1):
            # Critical patterns
            for pattern, desc in self.JAVA_CRITICAL.items():
                if pattern in line:
                    issues.append(SecurityIssue(
                        severity="CRITICAL",
                        issue_type="dangerous_operation",
                        pattern=pattern,
                        description=desc,
                        line_number=line_num
                    ))
            
            # Warning patterns
            for pattern, desc in self.JAVA_WARNING.items():
                if pattern in line:
                    issues.append(SecurityIssue(
                        severity="WARNING",
                        issue_type="context_dependent",
                        pattern=pattern,
                        description=desc,
                        line_number=line_num
                    ))
        
        # SQL injection patterns (regex)
        for pattern, desc in self.JAVA_SQL_INJECTION.items():
            matches = re.finditer(pattern, code)
            for match in matches:
                line_num = code[:match.start()].count('\n') + 1
                issues.append(SecurityIssue(
                    severity="CRITICAL",
                    issue_type="sql_injection",
                    pattern=match.group(0)[:50],
                    description=desc,
                    line_number=line_num,
                    recommendation="Use PreparedStatement with parameters"
                ))
        
        return issues
    
    # =========================================================================
    # Go Checks
    # =========================================================================
    
    def _check_go(self, code: str) -> List[SecurityIssue]:
        """Check Go code for security issues."""
        issues = []
        lines = code.split('\n')
        
        for line_num, line in enumerate(lines, 1):
            # Critical patterns
            for pattern, desc in self.GO_CRITICAL.items():
                if pattern in line:
                    issues.append(SecurityIssue(
                        severity="CRITICAL",
                        issue_type="dangerous_operation",
                        pattern=pattern,
                        description=desc,
                        line_number=line_num
                    ))
            
            # Warning patterns
            for pattern, desc in self.GO_WARNING.items():
                if pattern in line:
                    issues.append(SecurityIssue(
                        severity="WARNING",
                        issue_type="context_dependent",
                        pattern=pattern,
                        description=desc,
                        line_number=line_num
                    ))
        
        # SQL injection patterns
        for pattern, desc in self.GO_SQL_INJECTION.items():
            matches = re.finditer(pattern, code)
            for match in matches:
                line_num = code[:match.start()].count('\n') + 1
                issues.append(SecurityIssue(
                    severity="CRITICAL",
                    issue_type="sql_injection",
                    pattern=match.group(0)[:50],
                    description=desc,
                    line_number=line_num,
                    recommendation="Use parameterized queries"
                ))
        
        return issues
    
    # =========================================================================
    # SQL Checks
    # =========================================================================
    
    def _check_sql(self, code: str) -> List[SecurityIssue]:
        """Check SQL for common injection patterns."""
        issues = []
        
        # Tautology patterns (1=1, 'a'='a')
        if re.search(r"1\s*=\s*1|'[^']+'\s*=\s*'[^']+'", code):
            issues.append(SecurityIssue(
                severity="CRITICAL",
                issue_type="sql_injection_tautology",
                pattern="1=1 or equivalent",
                description="Tautology detected - likely injection attempt"
            ))
        
        # Union injection
        if re.search(r"\bUNION\s+(ALL\s+)?SELECT\b", code, re.IGNORECASE):
            issues.append(SecurityIssue(
                severity="CRITICAL",
                issue_type="union_injection",
                pattern="UNION SELECT",
                description="UNION-based injection pattern"
            ))
        
        # Comment injection
        if "--" in code or "/*" in code:
            issues.append(SecurityIssue(
                severity="WARNING",
                issue_type="comment_injection",
                pattern="-- or /*",
                description="SQL comment (may indicate injection)"
            ))
        
        # Semicolon stacking
        if ";" in code and code.count(";") > 1:
            issues.append(SecurityIssue(
                severity="WARNING",
                issue_type="stacked_queries",
                pattern=";",
                description="Multiple statements (may indicate injection)"
            ))
        
        return issues
    
    # =========================================================================
    # Secret Detection (Cross-Language)
    # =========================================================================
    
    def _check_secrets(self, code: str) -> List[SecurityIssue]:
        """Check for hardcoded secrets across all languages."""
        issues = []
        
        for pattern, desc in self.SECRET_PATTERNS.items():
            matches = re.finditer(pattern, code)
            for match in matches:
                line_num = code[:match.start()].count('\n') + 1
                # Mask the secret
                secret = match.group(0)
                masked = secret[:8] + "..." + secret[-4:] if len(secret) > 12 else secret[:4] + "..."
                
                issues.append(SecurityIssue(
                    severity="CRITICAL",
                    issue_type="hardcoded_secret",
                    pattern=masked,
                    description=desc,
                    line_number=line_num,
                    recommendation="Use environment variables or secret management"
                ))
        
        return issues
    
    # =========================================================================
    # Batch Verification
    # =========================================================================
    
    def verify_batch(self, snippets: List[Dict[str, str]]) -> Dict[str, Any]:
        """
        Verify multiple code snippets.
        
        Args:
            snippets: List of dicts with {"code": str, "language": str}.
            
        Returns:
            Dict with batch verification results and summary stats.

        Example:
            >>> batch = [{"code": "print(1)", "language": "python"}, {"code": "alert(1)", "language": "js"}]
            >>> result = verifier.verify_batch(batch)
        """
        results = []
        
        for snippet in snippets:
            result = self.verify_code(
                snippet.get("code", ""),
                snippet.get("language", "python")
            )
            results.append({
                "language": snippet.get("language", "python"),
                **result
            })
        
        # Summary
        return {
            "results": results,
            "summary": {
                "total": len(snippets),
                "safe": sum(1 for r in results if r["status"] == "SAFE"),
                "review": sum(1 for r in results if r["status"] == "REVIEW"),
                "blocked": sum(1 for r in results if r["status"] == "BLOCKED"),
                "total_critical": sum(r["critical_count"] for r in results),
                "total_warnings": sum(r["warning_count"] for r in results)
            }
        }
