"""Data models for MCP Security Audit."""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
from typing import Any


class Severity(Enum):
    """Severity levels ordered from most to least critical."""

    CRITICAL = "CRITICAL"
    HIGH = "HIGH"
    MEDIUM = "MEDIUM"
    LOW = "LOW"
    INFO = "INFO"

    @property
    def weight(self) -> int:
        """Numeric weight for scoring calculations."""
        return {
            Severity.CRITICAL: 10,
            Severity.HIGH: 5,
            Severity.MEDIUM: 2,
            Severity.LOW: 1,
            Severity.INFO: 0,
        }[self]

    @property
    def icon(self) -> str:
        """Terminal icon for this severity."""
        return {
            Severity.CRITICAL: "\u001b[91m\u25cf\u001b[0m",  # red circle
            Severity.HIGH: "\u001b[93m\u25cf\u001b[0m",       # yellow circle
            Severity.MEDIUM: "\u001b[33m\u25cb\u001b[0m",     # dim yellow
            Severity.LOW: "\u001b[36m\u25cb\u001b[0m",        # cyan
            Severity.INFO: "\u001b[90m\u2139\u001b[0m",       # gray info
        }[self]

    @property
    def icon_plain(self) -> str:
        """Plain text icon (no ANSI)."""
        return {
            Severity.CRITICAL: "[!]",
            Severity.HIGH: "[*]",
            Severity.MEDIUM: "[-]",
            Severity.LOW: "[.]",
            Severity.INFO: "[i]",
        }[self]

    @property
    def emoji(self) -> str:
        """Emoji for markdown reports."""
        return {
            Severity.CRITICAL: "\U0001f534",
            Severity.HIGH: "\U0001f7e0",
            Severity.MEDIUM: "\U0001f7e1",
            Severity.LOW: "\U0001f7e2",
            Severity.INFO: "\u2139\ufe0f",
        }[self]

    @classmethod
    def from_string(cls, value: str) -> Severity:
        """Parse a severity string (case-insensitive)."""
        try:
            return cls[value.upper()]
        except KeyError:
            valid = ", ".join(s.name.lower() for s in cls)
            raise ValueError(f"Invalid severity '{value}'. Valid: {valid}")

    def __lt__(self, other: Severity) -> bool:
        order = list(Severity)
        return order.index(self) < order.index(other)

    def __le__(self, other: Severity) -> bool:
        return self == other or self < other


@dataclass
class RuleMetadata:
    """Metadata for a security rule."""

    rule_id: str
    name: str
    description: str
    severity: Severity
    category: str
    cwe: str = ""          # CWE reference (e.g., "CWE-78")
    owasp: str = ""        # OWASP reference
    references: list[str] = field(default_factory=list)

    def to_dict(self) -> dict[str, Any]:
        return {
            "rule_id": self.rule_id,
            "name": self.name,
            "description": self.description,
            "severity": self.severity.value,
            "category": self.category,
            "cwe": self.cwe,
            "owasp": self.owasp,
        }


@dataclass
class Finding:
    """A single security finding."""

    rule_id: str
    severity: Severity
    title: str
    description: str
    file: str
    line: int
    code_snippet: str = ""
    recommendation: str = ""
    cwe: str = ""
    confidence: str = "HIGH"  # HIGH, MEDIUM, LOW
    remediation: str = ""     # Auto-remediation guidance

    def to_dict(self) -> dict[str, Any]:
        result = {
            "rule_id": self.rule_id,
            "severity": self.severity.value,
            "title": self.title,
            "description": self.description,
            "file": self.file,
            "line": self.line,
            "code_snippet": self.code_snippet,
            "recommendation": self.recommendation,
            "cwe": self.cwe,
            "confidence": self.confidence,
        }
        if self.remediation:
            result["remediation"] = self.remediation
        return result


@dataclass
class ScanResult:
    """Result of scanning a single file."""

    file: str
    findings: list[Finding] = field(default_factory=list)
    lines_scanned: int = 0
    scan_time_ms: float = 0.0
    parse_error: str = ""
    deobfuscation_findings: list[Any] = field(default_factory=list)

    @property
    def severity_counts(self) -> dict[str, int]:
        """Count findings by severity."""
        counts = {s.value: 0 for s in Severity}
        for f in self.findings:
            counts[f.severity.value] += 1
        return counts

    @property
    def weighted_score(self) -> int:
        """Weighted score: higher = more issues."""
        return sum(f.severity.weight for f in self.findings)

    @property
    def grade(self) -> str:
        """Security grade from A+ (clean) to F (critical issues)."""
        ws = self.weighted_score
        if ws == 0:
            return "A+"
        if ws <= 2:
            return "A"
        if ws <= 5:
            return "B"
        if ws <= 10:
            return "C"
        if ws <= 20:
            return "D"
        return "F"

    @property
    def grade_color(self) -> str:
        """ANSI color code for the grade."""
        return {
            "A+": "\u001b[92m",  # bright green
            "A": "\u001b[92m",
            "B": "\u001b[93m",   # yellow
            "C": "\u001b[33m",   # dim yellow
            "D": "\u001b[91m",   # red
            "F": "\u001b[91;1m", # bold red
        }.get(self.grade, "\u001b[0m")

    def to_dict(self) -> dict[str, Any]:
        return {
            "file": self.file,
            "grade": self.grade,
            "weighted_score": self.weighted_score,
            "lines_scanned": self.lines_scanned,
            "scan_time_ms": round(self.scan_time_ms, 2),
            "severity_counts": self.severity_counts,
            "findings_count": len(self.findings),
            "findings": [f.to_dict() for f in self.findings],
        }


@dataclass
class ScanSummary:
    """Aggregate summary of a scan across multiple files."""

    results: list[ScanResult] = field(default_factory=list)
    total_scan_time_ms: float = 0.0

    @property
    def total_files(self) -> int:
        return len(self.results)

    @property
    def total_lines(self) -> int:
        return sum(r.lines_scanned for r in self.results)

    @property
    def total_findings(self) -> int:
        return sum(len(r.findings) for r in self.results)

    @property
    def severity_counts(self) -> dict[str, int]:
        counts = {s.value: 0 for s in Severity}
        for r in self.results:
            for sev, cnt in r.severity_counts.items():
                counts[sev] += cnt
        return counts

    @property
    def overall_grade(self) -> str:
        """Worst grade across all files."""
        if not self.results:
            return "A+"
        grade_order = ["A+", "A", "B", "C", "D", "F"]
        worst = 0
        for r in self.results:
            idx = grade_order.index(r.grade) if r.grade in grade_order else 5
            worst = max(worst, idx)
        return grade_order[worst]

    @property
    def exit_code(self) -> int:
        """CI/CD exit code: 0=clean, 1=warnings, 2=critical."""
        counts = self.severity_counts
        if counts["CRITICAL"] > 0:
            return 2
        if counts["HIGH"] > 0:
            return 1
        return 0

    def to_dict(self) -> dict[str, Any]:
        return {
            "summary": {
                "files_scanned": self.total_files,
                "lines_scanned": self.total_lines,
                "total_findings": self.total_findings,
                "overall_grade": self.overall_grade,
                "severity_counts": self.severity_counts,
                "scan_time_ms": round(self.total_scan_time_ms, 2),
                "exit_code": self.exit_code,
            },
            "results": [r.to_dict() for r in self.results],
        }
