"""OWASP Agentic Top 10 2026 Compliance Report Generator.

Generates a self-contained HTML report mapping mcpaudit findings
to the OWASP Agentic Top 10 2026 categories with executive summary,
coverage matrix, severity chart, and prioritized recommendations.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any

from . import __version__
from .models import Finding, ScanSummary, Severity


# ---------------------------------------------------------------------------
# OWASP Agentic Top 10 2026 categories
# ---------------------------------------------------------------------------

@dataclass(frozen=True)
class OWASPCategory:
    """A single OWASP Agentic Top 10 2026 category."""

    rank: int
    id: str
    name: str
    description: str
    rule_ids: tuple[str, ...]  # mcpaudit rule_ids that cover this category

    def to_dict(self) -> dict[str, Any]:
        """Serialize to dictionary."""
        return {
            "rank": self.rank,
            "id": self.id,
            "name": self.name,
            "description": self.description,
            "rule_ids": list(self.rule_ids),
        }


OWASP_AGENTIC_TOP10: list[OWASPCategory] = [
    OWASPCategory(
        rank=1,
        id="OWASP-A01",
        name="Prompt Injection",
        description=(
            "Malicious input manipulates the LLM through crafted tool descriptions, "
            "arguments, or return values that alter model instructions."
        ),
        rule_ids=("MCP-004", "TS-MCP-004"),
    ),
    OWASPCategory(
        rank=2,
        id="OWASP-A02",
        name="Insecure Tool Implementation",
        description=(
            "MCP tools execute unsafe operations: command injection, SQL injection, "
            "path traversal, or unsafe deserialization."
        ),
        rule_ids=(
            "CMD-001", "CMD-002", "SQL-001", "PATH-001",
            "DESER-001",
            "TS-CMD-001", "TS-SQL-001", "TS-PATH-001", "TS-DESER-001",
        ),
    ),
    OWASPCategory(
        rank=3,
        id="OWASP-A03",
        name="Excessive Agency",
        description=(
            "Tools have more permissions than necessary, enabling privilege "
            "escalation or unintended side effects."
        ),
        rule_ids=(
            "PERM-001", "PRIV-001",
            "TS-PERM-001", "TS-PRIV-001",
        ),
    ),
    OWASPCategory(
        rank=4,
        id="OWASP-A04",
        name="Insecure Output Handling",
        description=(
            "Sensitive information is leaked through error messages, stack traces, "
            "or unfiltered tool responses."
        ),
        rule_ids=("INFO-001", "ERR-001", "TS-INFO-001"),
    ),
    OWASPCategory(
        rank=5,
        id="OWASP-A05",
        name="Insufficient Access Control",
        description=(
            "MCP servers lack authentication or authorization, allowing "
            "unauthorized access to tools and resources."
        ),
        rule_ids=(
            "AUTH-001", "AUTHZ-001",
            "TS-AUTH-001", "TS-AUTHZ-001",
        ),
    ),
    OWASPCategory(
        rank=6,
        id="OWASP-A06",
        name="Improper Input Validation",
        description=(
            "Tool inputs are not validated, enabling injection attacks "
            "and insecure direct object references (IDOR)."
        ),
        rule_ids=(
            "VAL-001", "IDOR-001",
            "TS-VAL-001", "TS-IDOR-001",
        ),
    ),
    OWASPCategory(
        rank=7,
        id="OWASP-A07",
        name="Server-Side Request Forgery",
        description=(
            "Tools make HTTP requests with user-controlled URLs without "
            "allowlist validation, enabling SSRF attacks."
        ),
        rule_ids=("SSRF-001", "TS-SSRF-001"),
    ),
    OWASPCategory(
        rank=8,
        id="OWASP-A08",
        name="Data Exfiltration",
        description=(
            "Sensitive data is extracted through file access, hardcoded secrets, "
            "or confused deputy attacks exploiting trust boundaries."
        ),
        rule_ids=("SEC-001", "MCP-003", "FILE-001", "LOG-001"),
    ),
    OWASPCategory(
        rank=9,
        id="OWASP-A09",
        name="Denial of Service",
        description=(
            "Missing rate limiting, resource exhaustion, or ReDoS patterns "
            "cause the MCP server to become unavailable."
        ),
        rule_ids=("RATE-001", "RES-001", "REDOS-001", "TS-RATE-001"),
    ),
    OWASPCategory(
        rank=10,
        id="OWASP-A10",
        name="Insecure Configuration",
        description=(
            "Hardcoded paths, unsafe temp files, CORS misconfiguration, "
            "or missing tool descriptions weaken the security posture."
        ),
        rule_ids=(
            "PATH-002", "TEMP-001", "CORS-001",
            "MCP-001", "MCP-002",
        ),
    ),
]

# Index by category id for fast lookup
OWASP_BY_ID: dict[str, OWASPCategory] = {c.id: c for c in OWASP_AGENTIC_TOP10}


# ---------------------------------------------------------------------------
# Compliance status per category
# ---------------------------------------------------------------------------

@dataclass
class CategoryStatus:
    """Compliance status for a single OWASP category."""

    category: OWASPCategory
    findings: list[Finding] = field(default_factory=list)
    rules_checked: int = 0
    rules_triggered: int = 0

    @property
    def status(self) -> str:
        """Return PASS, FAIL, or PARTIAL."""
        if not self.findings:
            return "PASS"
        if self.rules_triggered >= len(self.category.rule_ids) // 2 + 1:
            return "FAIL"
        return "PARTIAL"

    @property
    def max_severity(self) -> Severity | None:
        """Return the highest severity finding, or None."""
        if not self.findings:
            return None
        severity_order = list(Severity)
        return min(self.findings, key=lambda f: severity_order.index(f.severity)).severity

    def to_dict(self) -> dict[str, Any]:
        """Serialize to dictionary."""
        return {
            "category": self.category.to_dict(),
            "status": self.status,
            "findings_count": len(self.findings),
            "max_severity": self.max_severity.value if self.max_severity else None,
            "rules_checked": self.rules_checked,
            "rules_triggered": self.rules_triggered,
        }


# ---------------------------------------------------------------------------
# Compliance report data
# ---------------------------------------------------------------------------

@dataclass
class ComplianceReport:
    """Full OWASP Agentic compliance report data."""

    project_name: str
    scan_summary: ScanSummary
    categories: list[CategoryStatus] = field(default_factory=list)
    timestamp: str = ""

    def __post_init__(self) -> None:
        if not self.timestamp:
            self.timestamp = datetime.now(timezone.utc).strftime(
                "%Y-%m-%d %H:%M:%S UTC"
            )

    @property
    def pass_count(self) -> int:
        """Number of categories with PASS status."""
        return sum(1 for c in self.categories if c.status == "PASS")

    @property
    def fail_count(self) -> int:
        """Number of categories with FAIL status."""
        return sum(1 for c in self.categories if c.status == "FAIL")

    @property
    def partial_count(self) -> int:
        """Number of categories with PARTIAL status."""
        return sum(1 for c in self.categories if c.status == "PARTIAL")

    @property
    def compliance_score(self) -> int:
        """Compliance score from 0-100. PASS=10pts, PARTIAL=5pts."""
        score = 0
        for c in self.categories:
            if c.status == "PASS":
                score += 10
            elif c.status == "PARTIAL":
                score += 5
        return score

    def to_dict(self) -> dict[str, Any]:
        """Serialize to dictionary."""
        return {
            "project_name": self.project_name,
            "timestamp": self.timestamp,
            "overall_grade": self.scan_summary.overall_grade,
            "compliance_score": self.compliance_score,
            "pass_count": self.pass_count,
            "fail_count": self.fail_count,
            "partial_count": self.partial_count,
            "categories": [c.to_dict() for c in self.categories],
        }


# ---------------------------------------------------------------------------
# Build compliance report from scan results
# ---------------------------------------------------------------------------

def build_compliance_report(
    summary: ScanSummary,
    project_name: str = "",
) -> ComplianceReport:
    """Build OWASP Agentic compliance report from scan results.

    Args:
        summary: Aggregate scan summary with all results.
        project_name: Name of the scanned project.

    Returns:
        ComplianceReport with per-category status.
    """
    all_findings: list[Finding] = [
        f for r in summary.results for f in r.findings
    ]

    # Index findings by rule_id
    findings_by_rule: dict[str, list[Finding]] = {}
    for finding in all_findings:
        findings_by_rule.setdefault(finding.rule_id, []).append(finding)

    categories: list[CategoryStatus] = []
    for owasp_cat in OWASP_AGENTIC_TOP10:
        status = CategoryStatus(category=owasp_cat)
        triggered_rules: set[str] = set()
        for rule_id in owasp_cat.rule_ids:
            rule_findings = findings_by_rule.get(rule_id, [])
            if rule_findings:
                status.findings.extend(rule_findings)
                triggered_rules.add(rule_id)
        status.rules_checked = len(owasp_cat.rule_ids)
        status.rules_triggered = len(triggered_rules)
        categories.append(status)

    if not project_name:
        # Derive from first file scanned
        if summary.results:
            first_file = summary.results[0].file
            parts = first_file.replace("\\", "/").split("/")
            project_name = parts[-2] if len(parts) >= 2 else parts[0]
        else:
            project_name = "Unknown Project"

    return ComplianceReport(
        project_name=project_name,
        scan_summary=summary,
        categories=categories,
    )


# ---------------------------------------------------------------------------
# HTML report generation
# ---------------------------------------------------------------------------

def generate_compliance_html(report: ComplianceReport) -> str:
    """Generate a self-contained HTML compliance report.

    Args:
        report: ComplianceReport data.

    Returns:
        Complete HTML document string with inline CSS.
    """
    grade = report.scan_summary.overall_grade
    grade_color = _grade_to_color(grade)
    counts = report.scan_summary.severity_counts

    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OWASP Agentic Compliance Report — {_escape(report.project_name)}</title>
<style>
{_get_compliance_css()}
</style>
</head>
<body>
<div class="container">

{_build_header(report, grade, grade_color)}

{_build_executive_summary(report, counts)}

{_build_owasp_matrix(report)}

{_build_severity_chart(counts)}

{_build_top_findings(report)}

{_build_recommendations(report)}

{_build_footer(report)}

</div>
</body>
</html>"""


# ---------------------------------------------------------------------------
# HTML section builders
# ---------------------------------------------------------------------------

def _build_header(
    report: ComplianceReport,
    grade: str,
    grade_color: str,
) -> str:
    """Build the report header section."""
    return f"""<header>
    <div class="header-content">
      <div class="header-left">
        <h1>OWASP Agentic Top 10 Compliance Report</h1>
        <p class="project-name">{_escape(report.project_name)}</p>
        <p class="timestamp">{report.timestamp}</p>
      </div>
      <div class="header-right">
        <div class="grade-badge" style="background:{grade_color}">
          <span class="grade-label">Grade</span>
          <span class="grade-value">{grade}</span>
        </div>
        <div class="score-badge">
          <span class="score-label">Score</span>
          <span class="score-value">{report.compliance_score}/100</span>
        </div>
      </div>
    </div>
  </header>"""


def _build_executive_summary(
    report: ComplianceReport,
    counts: dict[str, int],
) -> str:
    """Build the executive summary section."""
    total = report.scan_summary.total_findings
    files = report.scan_summary.total_files
    lines = report.scan_summary.total_lines

    # Determine risk level text
    if counts["CRITICAL"] > 0:
        risk = "CRITICAL — Immediate remediation required"
        risk_class = "risk-critical"
    elif counts["HIGH"] > 0:
        risk = "HIGH — Address before deployment"
        risk_class = "risk-high"
    elif counts["MEDIUM"] > 0:
        risk = "MODERATE — Plan remediation"
        risk_class = "risk-medium"
    elif total > 0:
        risk = "LOW — Minor improvements recommended"
        risk_class = "risk-low"
    else:
        risk = "CLEAN — No issues detected"
        risk_class = "risk-clean"

    return f"""<section class="executive-summary">
    <h2>Executive Summary</h2>
    <div class="summary-grid">
      <div class="summary-card">
        <div class="summary-number">{files}</div>
        <div class="summary-label">Files Scanned</div>
      </div>
      <div class="summary-card">
        <div class="summary-number">{lines:,}</div>
        <div class="summary-label">Lines Analyzed</div>
      </div>
      <div class="summary-card">
        <div class="summary-number">{total}</div>
        <div class="summary-label">Total Findings</div>
      </div>
      <div class="summary-card">
        <div class="summary-number">{report.pass_count}/{len(report.categories)}</div>
        <div class="summary-label">Categories Passed</div>
      </div>
      <div class="summary-card">
        <div class="summary-number">{report.compliance_score}%</div>
        <div class="summary-label">Compliance Score</div>
      </div>
      <div class="summary-card">
        <div class="summary-number {risk_class}">{risk.split(' — ')[0]}</div>
        <div class="summary-label">Risk Level</div>
      </div>
    </div>
    <p class="risk-description {risk_class}">{risk}</p>
  </section>"""


def _build_owasp_matrix(report: ComplianceReport) -> str:
    """Build the OWASP coverage matrix table."""
    rows: list[str] = []
    for cs in report.categories:
        status = cs.status
        status_class = status.lower()
        status_icon = {"PASS": "PASS", "FAIL": "FAIL", "PARTIAL": "PARTIAL"}[status]
        severity_text = cs.max_severity.value if cs.max_severity else "-"
        severity_class = cs.max_severity.value.lower() if cs.max_severity else ""

        rows.append(
            f'<tr class="matrix-row {status_class}">'
            f'<td class="rank">A{cs.category.rank:02d}</td>'
            f'<td class="cat-name">{_escape(cs.category.name)}</td>'
            f'<td class="status-cell"><span class="status-badge {status_class}">'
            f'{status_icon}</span></td>'
            f'<td class="findings-count">{len(cs.findings)}</td>'
            f'<td class="max-sev"><span class="sev-tag {severity_class}">'
            f'{severity_text}</span></td>'
            f'<td class="cat-desc">{_escape(cs.category.description)}</td>'
            f"</tr>"
        )

    return f"""<section class="owasp-matrix">
    <h2>OWASP Agentic Top 10 Coverage Matrix</h2>
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Category</th>
          <th>Status</th>
          <th>Findings</th>
          <th>Max Severity</th>
          <th>Description</th>
        </tr>
      </thead>
      <tbody>
        {"".join(rows)}
      </tbody>
    </table>
  </section>"""


def _build_severity_chart(counts: dict[str, int]) -> str:
    """Build CSS-only severity bar chart."""
    total = sum(counts.values())
    max_count = max(counts.values()) if counts.values() else 1

    bars: list[str] = []
    for sev_name, css_class in [
        ("CRITICAL", "critical"),
        ("HIGH", "high"),
        ("MEDIUM", "medium"),
        ("LOW", "low"),
        ("INFO", "info"),
    ]:
        count = counts.get(sev_name, 0)
        pct = (count / max_count * 100) if max_count > 0 else 0
        bars.append(
            f'<div class="chart-row">'
            f'<span class="chart-label">{sev_name}</span>'
            f'<div class="chart-bar-bg">'
            f'<div class="chart-bar {css_class}" style="width:{pct:.1f}%"></div>'
            f'</div>'
            f'<span class="chart-count">{count}</span>'
            f'</div>'
        )

    return f"""<section class="severity-chart">
    <h2>Findings by Severity</h2>
    <div class="chart-container">
      {"".join(bars)}
    </div>
    <p class="chart-total">Total: {total} findings</p>
  </section>"""


def _build_top_findings(report: ComplianceReport) -> str:
    """Build top 5 critical findings section."""
    all_findings: list[Finding] = [
        f for r in report.scan_summary.results for f in r.findings
    ]
    if not all_findings:
        return """<section class="top-findings">
    <h2>Top Critical Findings</h2>
    <div class="no-findings">No security findings detected.</div>
  </section>"""

    severity_order = list(Severity)
    sorted_findings = sorted(
        all_findings,
        key=lambda f: (severity_order.index(f.severity), -f.line),
    )
    top5 = sorted_findings[:5]

    cards: list[str] = []
    for i, f in enumerate(top5, 1):
        sev_class = f.severity.value.lower()
        snippet_html = ""
        if f.code_snippet:
            snippet_html = (
                f'<div class="code-block">'
                f'<code>{_escape(f.code_snippet)}</code></div>'
            )
        remediation_html = ""
        if f.recommendation:
            remediation_html = (
                f'<div class="remediation">'
                f'<strong>Remediation:</strong> {_escape(f.recommendation)}</div>'
            )
        elif f.remediation:
            remediation_html = (
                f'<div class="remediation">'
                f'<strong>Remediation:</strong> {_escape(f.remediation)}</div>'
            )

        cards.append(
            f'<div class="finding-card {sev_class}">'
            f'<div class="finding-rank">#{i}</div>'
            f'<div class="finding-detail">'
            f'<div class="finding-header">'
            f'<span class="sev-badge {sev_class}">{f.severity.value}</span>'
            f'<span class="rule-tag">{_escape(f.rule_id)}</span>'
            f'<span class="finding-title">{_escape(f.title)}</span>'
            f'</div>'
            f'<p class="finding-desc">{_escape(f.description)}</p>'
            f'<p class="finding-loc">{_escape(f.file)}:{f.line}</p>'
            f'{snippet_html}'
            f'{remediation_html}'
            f'</div></div>'
        )

    return f"""<section class="top-findings">
    <h2>Top {len(top5)} Critical Findings</h2>
    {"".join(cards)}
  </section>"""


def _build_recommendations(report: ComplianceReport) -> str:
    """Build prioritized recommendations list."""
    recs: list[tuple[int, str, str]] = []

    for cs in report.categories:
        if cs.status == "FAIL":
            recs.append((
                1,
                f"[CRITICAL] {cs.category.name}",
                f"Address {len(cs.findings)} finding(s) in {cs.category.id}. "
                f"{cs.category.description}",
            ))
        elif cs.status == "PARTIAL":
            recs.append((
                2,
                f"[IMPROVE] {cs.category.name}",
                f"Reduce {len(cs.findings)} finding(s) in {cs.category.id}. "
                f"{cs.category.description}",
            ))

    if not recs:
        return """<section class="recommendations">
    <h2>Recommendations</h2>
    <div class="no-findings">All OWASP Agentic categories passed. No action needed.</div>
  </section>"""

    recs.sort(key=lambda r: r[0])
    items: list[str] = []
    for priority, title, desc in recs:
        priority_class = "priority-critical" if priority == 1 else "priority-improve"
        items.append(
            f'<li class="rec-item {priority_class}">'
            f'<strong>{_escape(title)}</strong>'
            f'<p>{_escape(desc)}</p></li>'
        )

    return f"""<section class="recommendations">
    <h2>Recommendations</h2>
    <ol class="rec-list">
      {"".join(items)}
    </ol>
  </section>"""


def _build_footer(report: ComplianceReport) -> str:
    """Build the report footer."""
    return f"""<footer>
    <p>Generated by <strong>mcpaudit v{__version__}</strong> — OWASP Agentic Top 10 2026 Compliance</p>
    <p>Scan completed at {report.timestamp}</p>
  </footer>"""


# ---------------------------------------------------------------------------
# Utilities
# ---------------------------------------------------------------------------

def _escape(text: str) -> str:
    """Escape HTML special characters."""
    return (
        text.replace("&", "&amp;")
        .replace("<", "&lt;")
        .replace(">", "&gt;")
        .replace('"', "&quot;")
    )


def _grade_to_color(grade: str) -> str:
    """Map grade to CSS color."""
    colors: dict[str, str] = {
        "A+": "#00c853",
        "A": "#66bb6a",
        "B": "#ffca28",
        "C": "#ff9800",
        "D": "#ff5722",
        "F": "#f44336",
    }
    return colors.get(grade, "#888")


def _get_compliance_css() -> str:
    """Return complete CSS for the compliance report."""
    return """
* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
  background: #0f172a;
  color: #e2e8f0;
  line-height: 1.6;
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}

.container {
  max-width: 1100px;
  margin: 0 auto;
  padding: 2rem;
}

/* Header */
header {
  margin-bottom: 2rem;
  padding-bottom: 1.5rem;
  border-bottom: 2px solid #1e3a5f;
}
.header-content {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 1.5rem;
}
.header-left h1 {
  font-size: 1.6rem;
  color: #60a5fa;
  margin-bottom: 0.3rem;
}
.project-name {
  font-size: 1.1rem;
  color: #94a3b8;
}
.timestamp {
  font-size: 0.8rem;
  color: #475569;
  margin-top: 0.3rem;
}
.header-right {
  display: flex;
  gap: 1rem;
  align-items: center;
}
.grade-badge {
  border-radius: 12px;
  padding: 0.8rem 1.2rem;
  text-align: center;
  min-width: 80px;
}
.grade-label, .score-label {
  display: block;
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: rgba(255,255,255,0.8);
}
.grade-value {
  display: block;
  font-size: 2rem;
  font-weight: 900;
  color: #fff;
}
.score-badge {
  background: #1e293b;
  border: 2px solid #334155;
  border-radius: 12px;
  padding: 0.8rem 1.2rem;
  text-align: center;
  min-width: 80px;
}
.score-value {
  display: block;
  font-size: 1.3rem;
  font-weight: 700;
  color: #60a5fa;
}

/* Section headings */
h2 {
  font-size: 1.2rem;
  color: #e2e8f0;
  margin-bottom: 1rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid #1e3a5f;
}
section { margin-bottom: 2.5rem; }

/* Executive Summary */
.summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1rem;
  margin-bottom: 1rem;
}
.summary-card {
  background: #1e293b;
  border: 1px solid #334155;
  border-radius: 10px;
  padding: 1rem;
  text-align: center;
}
.summary-number {
  font-size: 1.6rem;
  font-weight: 700;
  color: #f1f5f9;
}
.summary-label {
  font-size: 0.75rem;
  color: #94a3b8;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-top: 0.2rem;
}
.risk-description {
  padding: 0.6rem 1rem;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.9rem;
}
.risk-critical { color: #fca5a5; background: rgba(239,68,68,0.12); }
.risk-high { color: #fdba74; background: rgba(249,115,22,0.12); }
.risk-medium { color: #fcd34d; background: rgba(234,179,8,0.12); }
.risk-low { color: #86efac; background: rgba(34,197,94,0.12); }
.risk-clean { color: #6ee7b7; background: rgba(16,185,129,0.12); }

/* OWASP Matrix */
.owasp-matrix table {
  width: 100%;
  border-collapse: collapse;
  background: #1e293b;
  border-radius: 10px;
  overflow: hidden;
}
.owasp-matrix th {
  text-align: left;
  padding: 0.7rem 1rem;
  background: #334155;
  font-size: 0.75rem;
  text-transform: uppercase;
  color: #94a3b8;
  letter-spacing: 0.04em;
}
.owasp-matrix td {
  padding: 0.6rem 1rem;
  border-bottom: 1px solid #334155;
  font-size: 0.85rem;
}
.rank { font-weight: 700; color: #60a5fa; width: 60px; }
.cat-name { font-weight: 600; white-space: nowrap; }
.cat-desc { color: #94a3b8; font-size: 0.8rem; }
.status-badge {
  padding: 0.2rem 0.6rem;
  border-radius: 4px;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
}
.status-badge.pass { background: #065f46; color: #6ee7b7; }
.status-badge.fail { background: #7f1d1d; color: #fca5a5; }
.status-badge.partial { background: #78350f; color: #fcd34d; }
.findings-count { text-align: center; font-weight: 600; }
.sev-tag {
  padding: 0.15rem 0.4rem;
  border-radius: 3px;
  font-size: 0.7rem;
  font-weight: 600;
}
.sev-tag.critical { background: rgba(239,68,68,0.2); color: #fca5a5; }
.sev-tag.high { background: rgba(249,115,22,0.2); color: #fdba74; }
.sev-tag.medium { background: rgba(234,179,8,0.2); color: #fcd34d; }
.sev-tag.low { background: rgba(34,197,94,0.2); color: #86efac; }
.sev-tag.info { background: rgba(59,130,246,0.2); color: #93c5fd; }
.matrix-row.fail { background: rgba(239,68,68,0.04); }
.matrix-row.partial { background: rgba(234,179,8,0.04); }

/* Severity Chart (CSS-only bars) */
.chart-container {
  background: #1e293b;
  border-radius: 10px;
  padding: 1.2rem;
}
.chart-row {
  display: flex;
  align-items: center;
  margin-bottom: 0.7rem;
  gap: 0.8rem;
}
.chart-row:last-child { margin-bottom: 0; }
.chart-label {
  width: 80px;
  font-size: 0.8rem;
  font-weight: 600;
  color: #94a3b8;
  text-align: right;
}
.chart-bar-bg {
  flex: 1;
  height: 24px;
  background: #0f172a;
  border-radius: 6px;
  overflow: hidden;
}
.chart-bar {
  height: 100%;
  border-radius: 6px;
  min-width: 2px;
  transition: width 0.3s ease;
}
.chart-bar.critical { background: #ef4444; }
.chart-bar.high { background: #f97316; }
.chart-bar.medium { background: #eab308; }
.chart-bar.low { background: #22c55e; }
.chart-bar.info { background: #3b82f6; }
.chart-count {
  width: 40px;
  font-size: 0.85rem;
  font-weight: 700;
  color: #f1f5f9;
}
.chart-total {
  text-align: right;
  font-size: 0.8rem;
  color: #64748b;
  margin-top: 0.6rem;
}

/* Top Findings */
.finding-card {
  display: flex;
  background: #1e293b;
  border: 1px solid #334155;
  border-radius: 10px;
  margin-bottom: 0.8rem;
  overflow: hidden;
}
.finding-card.critical { border-left: 4px solid #ef4444; }
.finding-card.high { border-left: 4px solid #f97316; }
.finding-card.medium { border-left: 4px solid #eab308; }
.finding-card.low { border-left: 4px solid #22c55e; }
.finding-card.info { border-left: 4px solid #3b82f6; }
.finding-rank {
  background: #0f172a;
  padding: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
  font-weight: 900;
  color: #475569;
  min-width: 50px;
}
.finding-detail { padding: 0.8rem 1rem; flex: 1; }
.finding-header {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 0.3rem;
}
.sev-badge {
  padding: 0.15rem 0.5rem;
  border-radius: 4px;
  font-size: 0.7rem;
  font-weight: 700;
  color: #fff;
}
.sev-badge.critical { background: #ef4444; }
.sev-badge.high { background: #f97316; }
.sev-badge.medium { background: #eab308; color: #000; }
.sev-badge.low { background: #22c55e; }
.sev-badge.info { background: #3b82f6; }
.rule-tag {
  font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
  font-size: 0.75rem;
  color: #94a3b8;
  background: #0f172a;
  padding: 0.1rem 0.4rem;
  border-radius: 3px;
}
.finding-title { font-weight: 600; font-size: 0.9rem; }
.finding-desc { font-size: 0.85rem; color: #94a3b8; margin-bottom: 0.2rem; }
.finding-loc {
  font-family: 'SF Mono', Monaco, monospace;
  font-size: 0.75rem;
  color: #60a5fa;
  margin-bottom: 0.3rem;
}
.code-block {
  background: #0f172a;
  border: 1px solid #334155;
  border-radius: 6px;
  padding: 0.5rem 0.8rem;
  margin: 0.3rem 0;
  overflow-x: auto;
}
.code-block code {
  font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
  font-size: 0.8rem;
  color: #e2e8f0;
}
.remediation {
  font-size: 0.8rem;
  color: #4ade80;
  margin-top: 0.3rem;
  padding: 0.3rem 0.6rem;
  background: rgba(74,222,128,0.08);
  border-radius: 4px;
}
.no-findings {
  background: #1e293b;
  border: 1px solid #334155;
  border-radius: 10px;
  padding: 2rem;
  text-align: center;
  color: #4ade80;
  font-size: 1.1rem;
}

/* Recommendations */
.rec-list {
  list-style: none;
  counter-reset: rec-counter;
}
.rec-item {
  counter-increment: rec-counter;
  background: #1e293b;
  border: 1px solid #334155;
  border-radius: 8px;
  padding: 0.8rem 1rem;
  margin-bottom: 0.6rem;
  position: relative;
  padding-left: 3rem;
}
.rec-item::before {
  content: counter(rec-counter);
  position: absolute;
  left: 1rem;
  font-weight: 900;
  font-size: 1rem;
  color: #475569;
}
.rec-item strong { display: block; margin-bottom: 0.2rem; }
.rec-item p { font-size: 0.85rem; color: #94a3b8; }
.priority-critical strong { color: #fca5a5; }
.priority-improve strong { color: #fcd34d; }

/* Footer */
footer {
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 2px solid #1e3a5f;
  text-align: center;
  color: #475569;
  font-size: 0.8rem;
}
footer strong { color: #60a5fa; }

/* Print styles */
@media print {
  body { background: #fff; color: #1e293b; }
  .container { max-width: 100%; padding: 1rem; }
  .summary-card, .chart-container, .finding-card, .owasp-matrix table,
  .rec-item { break-inside: avoid; }
  header { break-after: avoid; }
  .grade-badge { print-color-adjust: exact; }
}

/* Responsive */
@media (max-width: 768px) {
  .header-content { flex-direction: column; }
  .summary-grid { grid-template-columns: repeat(2, 1fr); }
  .owasp-matrix { overflow-x: auto; }
  .cat-desc { display: none; }
  .finding-card { flex-direction: column; }
  .finding-rank { padding: 0.5rem; }
}
"""
