"""
GuardRails Security Scanner

AI-powered vulnerability detection for modern development workflows.
"""

import os
import json
import time
from pathlib import Path
from typing import List, Dict, Any, Optional
import requests
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.text import Text

console = Console()


class GuardRailsScanner:
    """AI-powered security scanner for code analysis."""
    
    def __init__(self, api_key: Optional[str] = None, base_url: str = "http://localhost:3000"):
        """
        Initialize the GuardRails scanner.
        
        Args:
            api_key: GuardRails API key (optional for local server)
            base_url: Base URL for the GuardRails API server
        """
        self.api_key = api_key or os.getenv("GUARDRAILS_API_KEY")
        self.base_url = base_url.rstrip("/")
        self.session = requests.Session()
        if self.api_key:
            self.session.headers.update({"Authorization": f"Bearer {self.api_key}"})
    
    def scan(self, target: str, **options) -> Dict[str, Any]:
        """
        Scan files or directories for security vulnerabilities.
        
        Args:
            target: File or directory path to scan
            **options: Additional scan options
            
        Returns:
            Dictionary containing scan results
        """
        console.print(f"🔍 GuardRails: Scanning {target}...")
        
        files = self._get_files_to_scan(target, options)
        results = {
            "summary": {
                "totalFiles": len(files),
                "vulnerabilities": 0,
                "critical": 0,
                "high": 0,
                "medium": 0,
                "low": 0
            },
            "findings": [],
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
        }
        
        for file_path in files:
            try:
                file_results = self._scan_file(file_path, options)
                results["findings"].extend(file_results.get("findings", []))
                
                # Update summary
                results["summary"]["vulnerabilities"] += len(file_results.get("findings", []))
                for finding in file_results.get("findings", []):
                    severity = finding.get("severity", "LOW").lower()
                    if severity in results["summary"]:
                        results["summary"][severity] += 1
                        
            except Exception as e:
                console.print(f"[red]Error scanning {file_path}: {e}[/red]")
        
        # Calculate security score
        results["securityScore"] = self._calculate_security_score(results["summary"])
        
        return results
    
    def _scan_file(self, file_path: str, options: Dict[str, Any]) -> Dict[str, Any]:
        """Scan a single file for vulnerabilities."""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                file_content = f.read()
            
            payload = {
                "filePath": file_path,
                "fileContent": file_content,
                "fileName": Path(file_path).name
            }
            
            response = self.session.post(
                f"{self.base_url}/api/scan",
                json=payload,
                timeout=options.get("timeout", 30)
            )
            response.raise_for_status()
            
            return response.json()
            
        except requests.exceptions.ConnectionError:
            raise Exception("GuardRails server not running. Start with: guardrails server start")
        except requests.exceptions.Timeout:
            raise Exception("Scan timeout - try reducing file size or increasing timeout")
        except requests.exceptions.RequestException as e:
            raise Exception(f"API request failed: {e}")
    
    def _get_files_to_scan(self, target: str, options: Dict[str, Any]) -> List[str]:
        """Get list of files to scan based on target and options."""
        target_path = Path(target)
        
        if target_path.is_file():
            return [str(target_path)]
        
        if not target_path.is_dir():
            raise ValueError(f"Target path does not exist: {target}")
        
        extensions = options.get("extensions", [".py", ".js", ".ts", ".jsx", ".tsx", ".java", ".go", ".php", ".rb", ".cs"])
        ignore_patterns = options.get("ignore", ["node_modules", ".git", "dist", "build", "__pycache__", ".pytest_cache"])
        
        files = []
        for ext in extensions:
            pattern = f"**/*{ext}"
            for file_path in target_path.glob(pattern):
                # Check if file should be ignored
                if any(ignore in str(file_path) for ignore in ignore_patterns):
                    continue
                files.append(str(file_path))
        
        return files
    
    def _calculate_security_score(self, summary: Dict[str, int]) -> int:
        """Calculate security score based on vulnerability counts."""
        critical = summary.get("critical", 0)
        high = summary.get("high", 0)
        medium = summary.get("medium", 0)
        low = summary.get("low", 0)
        
        return max(0, 100 - (critical * 30) - (high * 20) - (medium * 10) - (low * 5))
    
    def has_critical_issues(self, results: Dict[str, Any]) -> bool:
        """Check if scan results contain critical security issues."""
        return results["summary"]["critical"] > 0
    
    def format_report(self, results: Dict[str, Any], format_type: str = "text") -> str:
        """
        Format scan results into a readable report.
        
        Args:
            results: Scan results dictionary
            format_type: Output format ("text", "json", "table")
            
        Returns:
            Formatted report string
        """
        if format_type == "json":
            return json.dumps(results, indent=2)
        
        if format_type == "table":
            return self._format_table_report(results)
        
        return self._format_text_report(results)
    
    def _format_text_report(self, results: Dict[str, Any]) -> str:
        """Format results as text report."""
        summary = results["summary"]
        findings = results["findings"]
        
        report = f"""
🛡️  GuardRails Security Report
{'=' * 40}

📊 Security Score: {results['securityScore']}/100
📁 Files Scanned: {summary['totalFiles']}
🚨 Total Issues: {summary['vulnerabilities']}

"""
        
        if summary['vulnerabilities'] > 0:
            report += f"""📋 Issues by Severity:
   🔴 Critical: {summary['critical']}
   🟡 High: {summary['high']}
   🔵 Medium: {summary['medium']}
   🟢 Low: {summary['low']}

🔍 Detailed Findings:

"""
            
            for i, finding in enumerate(findings, 1):
                report += f"""{i}. {finding['type'].replace('_', ' ').upper()}
   📁 File: {finding['file']}:{finding['line']}
   ⚠️  {finding['message']}
"""
                if finding.get('fix'):
                    fix_preview = finding['fix'][:100] + "..." if len(finding['fix']) > 100 else finding['fix']
                    report += f"   💡 Fix: {fix_preview}\n"
                report += "\n"
        else:
            report += "✅ No security issues found!\n"
        
        return report
    
    def _format_table_report(self, results: Dict[str, Any]) -> str:
        """Format results as table report."""
        findings = results["findings"]
        
        if not findings:
            return "✅ No security issues found!"
        
        table = Table(title="🛡️ GuardRails Security Report")
        table.add_column("Severity", style="bold")
        table.add_column("File", style="cyan")
        table.add_column("Issue", style="white")
        
        for finding in findings:
            severity_emoji = {
                "CRITICAL": "🔴",
                "HIGH": "🟡", 
                "MEDIUM": "🔵",
                "LOW": "🟢"
            }.get(finding['severity'], "⚪")
            
            file_info = f"{Path(finding['file']).name}:{finding['line']}"
            issue = finding['message'][:50] + "..." if len(finding['message']) > 50 else finding['message']
            
            table.add_row(
                f"{severity_emoji} {finding['severity']}",
                file_info,
                issue
            )
        
        return str(table)
    
    def check_server_status(self) -> Dict[str, Any]:
        """Check if the GuardRails server is running and accessible."""
        try:
            response = self.session.get(f"{self.base_url}/health", timeout=5)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise Exception(f"Server not accessible: {e}")
