"""
Report Generator for MCP Security Auditor.

Generates reports in various formats: Text, JSON, HTML, SARIF, Markdown, SIEM.
"""

import json
from datetime import datetime
from typing import Optional
from .models import ScanResult, Finding


class ReportGenerator:
    """Generate security reports in various formats."""
    
    def __init__(self, results: ScanResult):
        self.results = results
    
    def to_text(self, use_color: bool = True) -> str:
        """Generate colored text report for terminal."""
        lines = []
        
        # Colors
        if use_color:
            RESET = "\033[0m"
            BOLD = "\033[1m"
            RED = "\033[91m"
            YELLOW = "\033[93m"
            GREEN = "\033[92m"
            BLUE = "\033[94m"
            CYAN = "\033[96m"
            MAGENTA = "\033[95m"
        else:
            RESET = BOLD = RED = YELLOW = GREEN = BLUE = CYAN = MAGENTA = ""
        
        # Header
        lines.append(f"\n{BOLD}{'='*70}{RESET}")
        lines.append(f"{BOLD}MCP Security Audit Report{RESET}")
        lines.append(f"{'='*70}\n")
        
        # Summary
        lines.append(f"{BOLD}Target:{RESET} {self.results.target_path}")
        lines.append(f"{BOLD}Scan ID:{RESET} {self.results.scan_id}")
        lines.append(f"{BOLD}Date:{RESET} {self.results.scan_date}")
        lines.append(f"{BOLD}Duration:{RESET} {self.results.scan_duration_seconds}s")
        lines.append(f"{BOLD}Scanner Version:{RESET} {self.results.scanner_version}")
        
        # Server Info
        info = self.results.server_info
        lines.append(f"\n{BOLD}Server Info:{RESET}")
        lines.append(f"  Name: {info.name}")
        lines.append(f"  Language: {info.language}")
        lines.append(f"  Framework: {info.framework or 'Unknown'}")
        lines.append(f"  Tools: {len(info.tools)}")
        lines.append(f"  Dependencies: {len(info.dependencies)}")
        
        # Risk Score
        score = self.results.risk_score
        if score >= 70:
            score_color = RED
            risk_level = "CRITICAL"
        elif score >= 50:
            score_color = YELLOW
            risk_level = "HIGH"
        elif score >= 30:
            score_color = YELLOW
            risk_level = "MEDIUM"
        elif score >= 10:
            score_color = CYAN
            risk_level = "LOW"
        else:
            score_color = GREEN
            risk_level = "MINIMAL"
        
        lines.append(f"\n{BOLD}Risk Score:{RESET} {score_color}{score}/100 ({risk_level}){RESET}")
        
        # Findings Summary
        lines.append(f"\n{BOLD}Findings Summary:{RESET}")
        sev_colors = {
            "critical": RED,
            "high": RED,
            "medium": YELLOW,
            "low": CYAN,
            "info": BLUE
        }
        
        for sev in ["critical", "high", "medium", "low", "info"]:
            count = self.results.findings_by_severity.get(sev, 0)
            color = sev_colors.get(sev, "")
            lines.append(f"  {color}{sev.capitalize():10}{RESET}: {count}")
        
        # Detailed Findings
        if self.results.findings:
            lines.append(f"\n{BOLD}{'='*70}{RESET}")
            lines.append(f"{BOLD}Detailed Findings{RESET}")
            lines.append(f"{'='*70}\n")
            
            for i, finding in enumerate(self.results.findings, 1):
                sev_color = sev_colors.get(finding.severity, "")
                
                lines.append(f"{BOLD}[{i}] {finding.title}{RESET}")
                lines.append(f"    Severity: {sev_color}{finding.severity.upper()}{RESET}")
                lines.append(f"    Category: {finding.category}")
                lines.append(f"    Analyzer: {finding.analyzer}")
                
                if finding.cwe_id:
                    lines.append(f"    CWE: {finding.cwe_id}")
                
                lines.append(f"    Description: {finding.description}")
                
                if finding.location:
                    loc = finding.location
                    lines.append(f"    Location: {loc.file}:{loc.line_start}")
                    if loc.snippet:
                        lines.append(f"    Code:")
                        for snippet_line in loc.snippet.split('\n')[:5]:
                            lines.append(f"      {MAGENTA}{snippet_line}{RESET}")
                
                if finding.remediation:
                    lines.append(f"    {GREEN}Remediation:{RESET} {finding.remediation.description}")
                
                lines.append("")
        else:
            lines.append(f"\n{GREEN}No security findings detected!{RESET}\n")
        
        # Footer
        lines.append(f"{'='*70}")
        lines.append(f"Analyzers run: {', '.join(self.results.analyzers_run)}")
        lines.append(f"{'='*70}\n")
        
        return '\n'.join(lines)
    
    def to_json(self, indent: int = 2) -> str:
        """Generate JSON report."""
        return self.results.to_json(indent=indent)
    
    def to_sarif(self) -> str:
        """Generate SARIF format for CI/CD integration."""
        sarif = {
            "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
            "version": "2.1.0",
            "runs": [{
                "tool": {
                    "driver": {
                        "name": "MCP Security Auditor",
                        "version": self.results.scanner_version,
                        "informationUri": "https://github.com/mcp-security-auditor",
                        "rules": self._generate_sarif_rules()
                    }
                },
                "results": self._generate_sarif_results(),
                "invocations": [{
                    "executionSuccessful": True,
                    "endTimeUtc": self.results.scan_date
                }]
            }]
        }
        
        return json.dumps(sarif, indent=2)
    
    def _generate_sarif_rules(self) -> list:
        """Generate SARIF rules from findings."""
        rules = {}
        
        for finding in self.results.findings:
            rule_id = f"{finding.analyzer}/{finding.category}"
            if rule_id not in rules:
                rules[rule_id] = {
                    "id": rule_id,
                    "name": finding.title,
                    "shortDescription": {
                        "text": finding.description[:200]
                    },
                    "defaultConfiguration": {
                        "level": self._severity_to_sarif_level(finding.severity)
                    },
                    "properties": {
                        "security-severity": self._severity_to_score(finding.severity)
                    }
                }
                
                if finding.cwe_id:
                    rules[rule_id]["properties"]["cwe"] = finding.cwe_id
        
        return list(rules.values())
    
    def _generate_sarif_results(self) -> list:
        """Generate SARIF results from findings."""
        results = []
        
        for finding in self.results.findings:
            result = {
                "ruleId": f"{finding.analyzer}/{finding.category}",
                "level": self._severity_to_sarif_level(finding.severity),
                "message": {
                    "text": finding.description
                },
                "fingerprints": {
                    "primary": finding.id
                }
            }
            
            if finding.location:
                result["locations"] = [{
                    "physicalLocation": {
                        "artifactLocation": {
                            "uri": finding.location.file
                        },
                        "region": {
                            "startLine": finding.location.line_start,
                            "endLine": finding.location.line_end
                        }
                    }
                }]
            
            if finding.remediation:
                result["fixes"] = [{
                    "description": {
                        "text": finding.remediation.description
                    }
                }]
            
            results.append(result)
        
        return results
    
    def _severity_to_sarif_level(self, severity: str) -> str:
        """Convert severity to SARIF level."""
        mapping = {
            "critical": "error",
            "high": "error",
            "medium": "warning",
            "low": "note",
            "info": "none"
        }
        return mapping.get(severity, "note")
    
    def _severity_to_score(self, severity: str) -> str:
        """Convert severity to security-severity score."""
        mapping = {
            "critical": "9.5",
            "high": "7.5",
            "medium": "5.0",
            "low": "2.5",
            "info": "1.0"
        }
        return mapping.get(severity, "1.0")
    
    def to_html(self) -> str:
        """Generate HTML report."""
        html = f"""<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MCP Security Audit Report</title>
    <style>
        :root {{
            --critical: #dc3545;
            --high: #fd7e14;
            --medium: #ffc107;
            --low: #17a2b8;
            --info: #6c757d;
            --success: #28a745;
        }}
        * {{ box-sizing: border-box; margin: 0; padding: 0; }}
        body {{
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            line-height: 1.6;
            color: #333;
            background: #f5f5f5;
        }}
        .container {{ max-width: 1200px; margin: 0 auto; padding: 20px; }}
        header {{
            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
            color: white;
            padding: 40px 20px;
            text-align: center;
        }}
        header h1 {{ font-size: 2.5rem; margin-bottom: 10px; }}
        header p {{ opacity: 0.8; }}
        .card {{
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            margin: 20px 0;
            overflow: hidden;
        }}
        .card-header {{
            background: #f8f9fa;
            padding: 15px 20px;
            border-bottom: 1px solid #eee;
            font-weight: 600;
        }}
        .card-body {{ padding: 20px; }}
        .summary-grid {{
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin-bottom: 20px;
        }}
        .summary-item {{
            text-align: center;
            padding: 15px;
            background: #f8f9fa;
            border-radius: 8px;
        }}
        .summary-item .value {{
            font-size: 2rem;
            font-weight: 700;
        }}
        .summary-item .label {{ color: #666; font-size: 0.9rem; }}
        .risk-score {{
            text-align: center;
            padding: 30px;
        }}
        .risk-score .score {{
            font-size: 4rem;
            font-weight: 700;
        }}
        .risk-critical .score {{ color: var(--critical); }}
        .risk-high .score {{ color: var(--high); }}
        .risk-medium .score {{ color: var(--medium); }}
        .risk-low .score {{ color: var(--low); }}
        .risk-minimal .score {{ color: var(--success); }}
        .severity-badge {{
            display: inline-block;
            padding: 4px 12px;
            border-radius: 20px;
            font-size: 0.8rem;
            font-weight: 600;
            text-transform: uppercase;
            color: white;
        }}
        .severity-critical {{ background: var(--critical); }}
        .severity-high {{ background: var(--high); }}
        .severity-medium {{ background: var(--medium); color: #333; }}
        .severity-low {{ background: var(--low); }}
        .severity-info {{ background: var(--info); }}
        .finding {{
            border: 1px solid #eee;
            border-radius: 8px;
            margin: 15px 0;
            overflow: hidden;
        }}
        .finding-header {{
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 15px 20px;
            background: #fafafa;
            cursor: pointer;
        }}
        .finding-header h3 {{ font-size: 1rem; }}
        .finding-body {{ padding: 20px; display: none; }}
        .finding.expanded .finding-body {{ display: block; }}
        .finding-meta {{
            display: flex;
            gap: 20px;
            flex-wrap: wrap;
            margin-bottom: 15px;
            font-size: 0.9rem;
            color: #666;
        }}
        .code-block {{
            background: #1e1e1e;
            color: #d4d4d4;
            padding: 15px;
            border-radius: 4px;
            overflow-x: auto;
            font-family: 'Fira Code', monospace;
            font-size: 0.9rem;
        }}
        .remediation {{
            background: #d4edda;
            border: 1px solid #c3e6cb;
            border-radius: 4px;
            padding: 15px;
            margin-top: 15px;
        }}
        .remediation h4 {{ color: #155724; margin-bottom: 5px; }}
        table {{
            width: 100%;
            border-collapse: collapse;
        }}
        th, td {{
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #eee;
        }}
        th {{ background: #f8f9fa; font-weight: 600; }}
        .chart-container {{ height: 200px; }}
        footer {{
            text-align: center;
            padding: 20px;
            color: #666;
            font-size: 0.9rem;
        }}
    </style>
</head>
<body>
    <header>
        <h1>🔒 MCP Security Audit Report</h1>
        <p>{self.results.server_info.name} | {self.results.scan_date[:10]}</p>
    </header>
    
    <div class="container">
        <div class="card">
            <div class="card-header">📊 Scan Summary</div>
            <div class="card-body">
                <div class="summary-grid">
                    <div class="summary-item">
                        <div class="value">{len(self.results.findings)}</div>
                        <div class="label">Total Findings</div>
                    </div>
                    <div class="summary-item">
                        <div class="value" style="color: var(--critical);">{self.results.findings_by_severity.get('critical', 0)}</div>
                        <div class="label">Critical</div>
                    </div>
                    <div class="summary-item">
                        <div class="value" style="color: var(--high);">{self.results.findings_by_severity.get('high', 0)}</div>
                        <div class="label">High</div>
                    </div>
                    <div class="summary-item">
                        <div class="value" style="color: var(--medium);">{self.results.findings_by_severity.get('medium', 0)}</div>
                        <div class="label">Medium</div>
                    </div>
                    <div class="summary-item">
                        <div class="value" style="color: var(--low);">{self.results.findings_by_severity.get('low', 0)}</div>
                        <div class="label">Low</div>
                    </div>
                </div>
            </div>
        </div>
        
        <div class="card">
            <div class="card-header">🎯 Risk Assessment</div>
            <div class="card-body">
                <div class="risk-score {self._get_risk_class()}">
                    <div class="score">{self.results.risk_score}/100</div>
                    <div class="label">{self._get_risk_label()} Risk</div>
                </div>
            </div>
        </div>
        
        <div class="card">
            <div class="card-header">📋 Server Information</div>
            <div class="card-body">
                <table>
                    <tr><th>Property</th><th>Value</th></tr>
                    <tr><td>Name</td><td>{self.results.server_info.name}</td></tr>
                    <tr><td>Language</td><td>{self.results.server_info.language}</td></tr>
                    <tr><td>Framework</td><td>{self.results.server_info.framework or 'Unknown'}</td></tr>
                    <tr><td>Tools</td><td>{len(self.results.server_info.tools)}</td></tr>
                    <tr><td>Dependencies</td><td>{len(self.results.server_info.dependencies)}</td></tr>
                    <tr><td>Scan Duration</td><td>{self.results.scan_duration_seconds}s</td></tr>
                </table>
            </div>
        </div>
        
        <div class="card">
            <div class="card-header">🔍 Findings ({len(self.results.findings)})</div>
            <div class="card-body">
                {self._generate_findings_html()}
            </div>
        </div>
    </div>
    
    <footer>
        <p>Generated by MCP Security Auditor v{self.results.scanner_version}</p>
        <p>Scan ID: {self.results.scan_id} | {self.results.scan_date}</p>
    </footer>
    
    <script>
        document.querySelectorAll('.finding-header').forEach(header => {{
            header.addEventListener('click', () => {{
                header.parentElement.classList.toggle('expanded');
            }});
        }});
    </script>
</body>
</html>"""
        return html
    
    def _get_risk_class(self) -> str:
        """Get CSS class for risk score."""
        score = self.results.risk_score
        if score >= 70: return "risk-critical"
        if score >= 50: return "risk-high"
        if score >= 30: return "risk-medium"
        if score >= 10: return "risk-low"
        return "risk-minimal"
    
    def _get_risk_label(self) -> str:
        """Get label for risk score."""
        score = self.results.risk_score
        if score >= 70: return "Critical"
        if score >= 50: return "High"
        if score >= 30: return "Medium"
        if score >= 10: return "Low"
        return "Minimal"
    
    def _generate_findings_html(self) -> str:
        """Generate HTML for findings list."""
        if not self.results.findings:
            return "<p>✅ No security findings detected!</p>"
        
        html_parts = []
        for finding in self.results.findings:
            location_html = ""
            if finding.location:
                location_html = f'<span>📍 {finding.location.file}:{finding.location.line_start}</span>'
                if finding.location.snippet:
                    snippet_escaped = finding.location.snippet.replace('<', '&lt;').replace('>', '&gt;')
                    location_html += f'<pre class="code-block">{snippet_escaped}</pre>'
            
            remediation_html = ""
            if finding.remediation:
                remediation_html = f'''
                <div class="remediation">
                    <h4>💡 Remediation</h4>
                    <p>{finding.remediation.description}</p>
                </div>'''
            
            html_parts.append(f'''
            <div class="finding">
                <div class="finding-header">
                    <h3>{finding.title}</h3>
                    <span class="severity-badge severity-{finding.severity}">{finding.severity}</span>
                </div>
                <div class="finding-body">
                    <div class="finding-meta">
                        <span>📁 {finding.category}</span>
                        <span>🔧 {finding.analyzer}</span>
                        {f'<span>📜 {finding.cwe_id}</span>' if finding.cwe_id else ''}
                    </div>
                    <p>{finding.description}</p>
                    {location_html}
                    {remediation_html}
                </div>
            </div>
            ''')
        
        return '\n'.join(html_parts)
    
    def to_markdown(self) -> str:
        """Generate Markdown report."""
        lines = [
            f"# MCP Security Audit Report",
            "",
            f"**Target:** {self.results.target_path}",
            f"**Scan ID:** {self.results.scan_id}",
            f"**Date:** {self.results.scan_date}",
            f"**Scanner Version:** {self.results.scanner_version}",
            "",
            "## Risk Score",
            "",
            f"**{self.results.risk_score}/100** ({self._get_risk_label()} Risk)",
            "",
            "## Summary",
            "",
            "| Severity | Count |",
            "|----------|-------|",
        ]
        
        for sev in ["critical", "high", "medium", "low", "info"]:
            count = self.results.findings_by_severity.get(sev, 0)
            lines.append(f"| {sev.capitalize()} | {count} |")
        
        lines.extend([
            "",
            "## Server Information",
            "",
            f"- **Name:** {self.results.server_info.name}",
            f"- **Language:** {self.results.server_info.language}",
            f"- **Framework:** {self.results.server_info.framework or 'Unknown'}",
            f"- **Tools:** {len(self.results.server_info.tools)}",
            f"- **Dependencies:** {len(self.results.server_info.dependencies)}",
            "",
            "## Findings",
            ""
        ])
        
        if not self.results.findings:
            lines.append("✅ No security findings detected!")
        else:
            for i, finding in enumerate(self.results.findings, 1):
                lines.append(f"### {i}. {finding.title}")
                lines.append("")
                lines.append(f"- **Severity:** {finding.severity.upper()}")
                lines.append(f"- **Category:** {finding.category}")
                lines.append(f"- **Analyzer:** {finding.analyzer}")
                if finding.cwe_id:
                    lines.append(f"- **CWE:** {finding.cwe_id}")
                lines.append("")
                lines.append(finding.description)
                lines.append("")
                
                if finding.location:
                    lines.append(f"**Location:** `{finding.location.file}:{finding.location.line_start}`")
                    if finding.location.snippet:
                        lines.append("```")
                        lines.append(finding.location.snippet)
                        lines.append("```")
                    lines.append("")
                
                if finding.remediation:
                    lines.append(f"**Remediation:** {finding.remediation.description}")
                    lines.append("")
        
        return '\n'.join(lines)
    
    def to_siem(self, format_type: str = "cef") -> str:
        """Generate SIEM-compatible output."""
        if format_type == "cef":
            return self._to_cef()
        elif format_type == "leef":
            return self._to_leef()
        elif format_type == "json-syslog":
            return self._to_json_syslog()
        elif format_type == "splunk":
            return self._to_splunk()
        else:
            return self._to_cef()
    
    def _to_cef(self) -> str:
        """Generate CEF (Common Event Format) output."""
        lines = []
        
        for finding in self.results.findings:
            # CEF:Version|Device Vendor|Device Product|Device Version|Signature ID|Name|Severity|Extension
            severity_map = {"critical": 10, "high": 8, "medium": 5, "low": 3, "info": 1}
            sev = severity_map.get(finding.severity, 1)
            
            extensions = [
                f"src={self.results.target_path}",
                f"cat={finding.category}",
                f"cs1={finding.analyzer}",
                f"cs1Label=Analyzer"
            ]
            
            if finding.location:
                extensions.append(f"filePath={finding.location.file}")
                extensions.append(f"fileId={finding.location.line_start}")
            
            if finding.cwe_id:
                extensions.append(f"cs2={finding.cwe_id}")
                extensions.append("cs2Label=CWE")
            
            cef_line = f"CEF:0|MCPAuditor|MCP Security Auditor|1.0|{finding.id}|{finding.title}|{sev}|{' '.join(extensions)}"
            lines.append(cef_line)
        
        return '\n'.join(lines)
    
    def _to_leef(self) -> str:
        """Generate LEEF (Log Event Extended Format) output."""
        lines = []
        
        for finding in self.results.findings:
            severity_map = {"critical": 10, "high": 8, "medium": 5, "low": 3, "info": 1}
            sev = severity_map.get(finding.severity, 1)
            
            parts = [
                f"LEEF:2.0|MCPAuditor|MCP Security Auditor|1.0|{finding.id}|",
                f"cat={finding.category}",
                f"sev={sev}",
                f"src={self.results.target_path}",
                f"devTimeFormat=yyyy-MM-dd'T'HH:mm:ss.SSSZ",
                f"devTime={self.results.scan_date}"
            ]
            
            if finding.location:
                parts.append(f"resource={finding.location.file}")
            
            lines.append('\t'.join(parts))
        
        return '\n'.join(lines)
    
    def _to_json_syslog(self) -> str:
        """Generate JSON syslog format."""
        events = []
        
        for finding in self.results.findings:
            event = {
                "timestamp": self.results.scan_date,
                "hostname": "mcp-auditor",
                "facility": "security",
                "severity": finding.severity,
                "appname": "mcp-security-auditor",
                "procid": self.results.scan_id,
                "msgid": finding.id,
                "structured_data": {
                    "finding": {
                        "title": finding.title,
                        "category": finding.category,
                        "analyzer": finding.analyzer,
                        "cwe": finding.cwe_id
                    },
                    "target": {
                        "path": self.results.target_path,
                        "name": self.results.server_info.name
                    }
                },
                "message": finding.description
            }
            
            if finding.location:
                event["structured_data"]["location"] = {
                    "file": finding.location.file,
                    "line": finding.location.line_start
                }
            
            events.append(json.dumps(event))
        
        return '\n'.join(events)
    
    def _to_splunk(self) -> str:
        """Generate Splunk-friendly format."""
        events = []
        
        for finding in self.results.findings:
            event = {
                "time": self.results.scan_date,
                "source": "mcp-security-auditor",
                "sourcetype": "mcp:security:finding",
                "index": "security",
                "event": {
                    "scan_id": self.results.scan_id,
                    "finding_id": finding.id,
                    "title": finding.title,
                    "description": finding.description,
                    "severity": finding.severity,
                    "category": finding.category,
                    "analyzer": finding.analyzer,
                    "cwe_id": finding.cwe_id,
                    "confidence": finding.confidence,
                    "target_path": self.results.target_path,
                    "server_name": self.results.server_info.name,
                    "language": self.results.server_info.language
                }
            }
            
            if finding.location:
                event["event"]["file"] = finding.location.file
                event["event"]["line"] = finding.location.line_start
            
            events.append(json.dumps(event))
        
        return '\n'.join(events)
