"""HTML report renderer — produces a self-contained single-file report."""
from __future__ import annotations

import html
from datetime import datetime, timezone

from pyguard.models import ScanResult, Severity

_SEVERITY_COLOR: dict[Severity, str] = {
    Severity.CRITICAL: "#dc2626",
    Severity.HIGH: "#ea580c",
    Severity.MEDIUM: "#ca8a04",
    Severity.LOW: "#2563eb",
    Severity.INFO: "#6b7280",
}

_SEVERITY_BG: dict[Severity, str] = {
    Severity.CRITICAL: "#fef2f2",
    Severity.HIGH: "#fff7ed",
    Severity.MEDIUM: "#fefce8",
    Severity.LOW: "#eff6ff",
    Severity.INFO: "#f9fafb",
}


def _h(text: str) -> str:
    return html.escape(str(text))


def render_html(results: list[ScanResult]) -> str:
    now = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
    total_findings = sum(len(r.findings) for r in results)

    rows = "\n".join(_result_card(r) for r in results)
    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>pyguard Security Report</title>
<style>
  body {{ font-family: system-ui, sans-serif; margin: 0; background: #f3f4f6; color: #111; }}
  header {{ background: #1e293b; color: #f8fafc; padding: 1.5rem 2rem; }}
  header h1 {{ margin: 0; font-size: 1.5rem; }}
  header p {{ margin: 0.25rem 0 0; opacity: .7; font-size: .9rem; }}
  main {{ max-width: 960px; margin: 2rem auto; padding: 0 1rem; }}
  .summary {{ background: white; border-radius: 8px; padding: 1rem 1.5rem;
              margin-bottom: 1.5rem; box-shadow: 0 1px 3px rgba(0,0,0,.1); }}
  .summary h2 {{ margin: 0 0 .5rem; font-size: 1rem; color: #475569; text-transform: uppercase; }}
  .badge {{ display: inline-block; padding: .2rem .6rem; border-radius: 999px;
            font-size: .75rem; font-weight: 700; margin-right: .25rem; }}
  .card {{ background: white; border-radius: 8px; margin-bottom: 1.5rem;
           box-shadow: 0 1px 3px rgba(0,0,0,.1); overflow: hidden; }}
  .card-header {{ padding: .75rem 1.25rem; display: flex; justify-content: space-between;
                  align-items: center; border-bottom: 1px solid #e5e7eb; }}
  .card-header h2 {{ margin: 0; font-size: 1rem; }}
  .card-meta {{ font-size: .8rem; color: #6b7280; }}
  .finding {{ padding: .75rem 1.25rem; border-bottom: 1px solid #f3f4f6; }}
  .finding:last-child {{ border-bottom: none; }}
  .finding-title {{ font-weight: 600; margin-bottom: .25rem; }}
  .finding-desc {{ font-size: .85rem; color: #374151; }}
  .finding-fix {{ font-size: .8rem; color: #16a34a; margin-top: .25rem; }}
  .finding-loc {{ font-size: .75rem; color: #6b7280; font-family: monospace; }}
  .clean {{ padding: 1rem 1.25rem; color: #16a34a; font-weight: 600; }}
  footer {{ text-align: center; color: #9ca3af; font-size: .8rem; padding: 2rem 0; }}
</style>
</head>
<body>
<header>
  <h1>🛡 pyguard Security Report</h1>
  <p>Generated {now} · {len(results)} package(s) · {total_findings} finding(s)</p>
</header>
<main>
{_summary_section(results)}
{rows}
</main>
<footer>Generated by <strong>pyguard</strong></footer>
</body>
</html>"""


def _summary_section(results: list[ScanResult]) -> str:
    counts: dict[Severity, int] = {}
    for r in results:
        for f in r.findings:
            counts[f.severity] = counts.get(f.severity, 0) + 1
    badges = "".join(
        f'<span class="badge" style="background:{_SEVERITY_COLOR[s]};color:white">'
        f'{counts.get(s,0)} {s.value.upper()}</span>'
        for s in [Severity.CRITICAL, Severity.HIGH, Severity.MEDIUM, Severity.LOW]
    )
    return f'<div class="summary"><h2>Summary</h2>{badges}</div>'


def _result_card(result: ScanResult) -> str:
    color = _SEVERITY_COLOR[result.risk_label]
    badge = (
        f'<span class="badge" style="background:{color};color:white">'
        f'{result.risk_label.value.upper()} {result.risk_score:.1f}</span>'
    )
    header = (
        f'<div class="card-header">'
        f'<h2>{_h(result.package.name)} <span style="font-weight:400;color:#6b7280">'
        f'v{_h(result.package.version)}</span></h2>'
        f'<span>{badge} '
        f'<span class="card-meta">{len(result.findings)} finding(s) · '
        f'{result.scan_duration_ms} ms</span></span>'
        f'</div>'
    )
    if not result.findings:
        body = '<div class="clean">✅ No findings — package looks clean.</div>'
    else:
        sorted_findings = sorted(result.findings, key=lambda f: -_sev_rank(f.severity))
        body = "\n".join(_finding_row(f) for f in sorted_findings)

    return f'<div class="card">{header}{body}</div>'


def _finding_row(finding: object) -> str:
    from pyguard.models import Finding  # noqa: PLC0415

    f: Finding = finding  # type: ignore[assignment]
    color = _SEVERITY_COLOR[f.severity]
    bg = _SEVERITY_BG[f.severity]
    sev_badge = (
        f'<span class="badge" style="background:{color};color:white;font-size:.7rem">'
        f'{f.severity.value.upper()}</span>'
    )
    loc = ""
    if f.file:
        loc_str = f"{f.file}:{f.line}" if f.line else f.file
        loc = f'<div class="finding-loc">📄 {_h(loc_str)}</div>'
    fix = ""
    if f.remediation:
        fix = f'<div class="finding-fix">🔧 {_h(f.remediation)}</div>'
    return (
        f'<div class="finding" style="background:{bg}">'
        f'<div class="finding-title">{sev_badge} {_h(f.title)}</div>'
        f'<div class="finding-desc">{_h(f.description)}</div>'
        f'{fix}{loc}'
        f'</div>'
    )


def _sev_rank(sev: Severity) -> int:
    return {
        Severity.CRITICAL: 5,
        Severity.HIGH: 4,
        Severity.MEDIUM: 3,
        Severity.LOW: 2,
        Severity.INFO: 1,
    }[sev]
