"""Report generation — JSON, HTML, and SARIF output."""

from __future__ import annotations

import json
from pathlib import Path

from jinja2 import Template

from llm_pentest.models import ScanResult

# --- SARIF ---

_SEVERITY_TO_SARIF = {
    "critical": "error",
    "high": "error",
    "medium": "warning",
    "low": "note",
    "info": "note",
}


def write_sarif(result: ScanResult, path: str | Path, payloads_by_id: dict | None = None) -> Path:
    """Write SARIF 2.1.0 report compatible with GitHub Code Scanning."""
    rules = []
    rule_ids_seen: set[str] = set()
    results_list = []

    for r in result.results:
        if not r.success:
            continue

        # Build rule if not seen
        if r.payload_id not in rule_ids_seen:
            rule_ids_seen.add(r.payload_id)
            payload = (payloads_by_id or {}).get(r.payload_id)
            severity = payload.severity.value if payload else "medium"
            rules.append({
                "id": r.payload_id,
                "name": payload.name if payload else r.payload_id,
                "shortDescription": {"text": payload.name if payload else r.payload_id},
                "fullDescription": {"text": payload.description if payload and payload.description else r.payload_id},
                "defaultConfiguration": {"level": _SEVERITY_TO_SARIF.get(severity, "warning")},
                "properties": {
                    "tags": [payload.category.value] if payload else [],
                    "security-severity": {"critical": "9.5", "high": "8.0", "medium": "5.5", "low": "3.0", "info": "1.0"}.get(severity, "5.5"),
                },
            })

        results_list.append({
            "ruleId": r.payload_id,
            "level": _SEVERITY_TO_SARIF.get(r.severity.value if hasattr(r, "severity") else "medium", "warning"),
            "message": {"text": f"Attack succeeded (confidence: {r.confidence:.0%}). Prompt: {r.prompt[:200]}"},
            "properties": {
                "confidence": r.confidence,
                "matched_indicators": r.matched_indicators,
            },
        })

    sarif = {
        "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
        "version": "2.1.0",
        "runs": [{
            "tool": {
                "driver": {
                    "name": "llm-pentest",
                    "version": "0.1.0",
                    "informationUri": "https://github.com/llm-pentest/llm-pentest",
                    "rules": rules,
                },
            },
            "results": results_list,
            "invocations": [{
                "executionSuccessful": True,
                "properties": {
                    "target": result.target,
                    "timestamp": result.timestamp,
                    "total_attacks": result.total_attacks,
                    "successful_attacks": result.successful_attacks,
                },
            }],
        }],
    }

    p = Path(path)
    p.write_text(json.dumps(sarif, indent=2))
    return p


# --- Baseline Diff ---


def diff_results(baseline: ScanResult, current: ScanResult) -> dict:
    """Compare two scan results and return a diff summary."""
    base_vulns = {(r.payload_id, r.prompt) for r in baseline.results if r.success}
    curr_vulns = {(r.payload_id, r.prompt) for r in current.results if r.success}

    fixed = base_vulns - curr_vulns
    new = curr_vulns - base_vulns
    persistent = base_vulns & curr_vulns

    return {
        "baseline_timestamp": baseline.timestamp,
        "current_timestamp": current.timestamp,
        "baseline_vulns": len(base_vulns),
        "current_vulns": len(curr_vulns),
        "fixed": len(fixed),
        "new": len(new),
        "persistent": len(persistent),
        "fixed_details": sorted(f"{pid}: {prompt[:80]}" for pid, prompt in fixed),
        "new_details": sorted(f"{pid}: {prompt[:80]}" for pid, prompt in new),
    }


# --- JSON ---


def write_json(result: ScanResult, path: str | Path) -> Path:
    p = Path(path)
    p.write_text(result.model_dump_json(indent=2))
    return p


# --- HTML ---

HTML_TEMPLATE = """\
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8">
<title>LLM Pentest Report — {{ result.target }}</title>
<style>
  body { font-family: system-ui, sans-serif; max-width: 900px; margin: 2rem auto; padding: 0 1rem; }
  .summary { display: flex; gap: 2rem; margin: 1rem 0; }
  .stat { padding: 1rem; border-radius: 8px; background: #f5f5f5; text-align: center; }
  .stat h2 { margin: 0; font-size: 2rem; }
  .vuln { border-left: 4px solid #e74c3c; padding: 0.5rem 1rem; margin: 1rem 0; background: #fdf2f2; }
  .safe { border-left: 4px solid #2ecc71; padding: 0.5rem 1rem; margin: 1rem 0; background: #f2fdf5; }
  .error { border-left: 4px solid #f39c12; padding: 0.5rem 1rem; margin: 1rem 0; background: #fef9f2; }
  pre { white-space: pre-wrap; word-break: break-word; font-size: 0.85rem; }
  details { cursor: pointer; }
</style></head><body>
<h1>🔓 LLM Pentest Report</h1>
<p>Target: <strong>{{ result.target }}</strong> — {{ result.timestamp }}</p>
<div class="summary">
  <div class="stat"><h2>{{ result.total_attacks }}</h2>Total</div>
  <div class="stat" style="background:#fdf2f2"><h2>{{ result.successful_attacks }}</h2>Succeeded</div>
  <div class="stat"><h2>{{ result.total_attacks - result.successful_attacks }}</h2>Blocked</div>
</div>
{% if diff %}
<h2>📊 Baseline Comparison</h2>
<div class="summary">
  <div class="stat" style="background:#f2fdf5"><h2>{{ diff.fixed }}</h2>Fixed</div>
  <div class="stat" style="background:#fdf2f2"><h2>{{ diff.new }}</h2>New</div>
  <div class="stat"><h2>{{ diff.persistent }}</h2>Persistent</div>
</div>
{% if diff.fixed_details %}<details><summary>✅ Fixed ({{ diff.fixed }})</summary><ul>{% for d in diff.fixed_details %}<li>{{ d }}</li>{% endfor %}</ul></details>{% endif %}
{% if diff.new_details %}<details><summary>🆕 New ({{ diff.new }})</summary><ul>{% for d in diff.new_details %}<li>{{ d }}</li>{% endfor %}</ul></details>{% endif %}
{% endif %}
<h2>Results</h2>
{% for r in result.results %}
<div class="{{ 'error' if r.error else 'vuln' if r.success else 'safe' }}">
  <strong>{{ r.payload_id }}</strong>
  {% if r.success %} — ⚠️ VULNERABLE (confidence: {{ "%.0f"|format(r.confidence * 100) }}%){% endif %}
  {% if r.error %} — ❌ ERROR{% endif %}
  <details><summary>Details</summary>
    <p><strong>Prompt:</strong></p><pre>{{ r.prompt }}</pre>
    <p><strong>Response:</strong></p><pre>{{ r.response[:1000] }}</pre>
    {% if r.matched_indicators %}<p><strong>Matched:</strong> {{ r.matched_indicators | join(', ') }}</p>{% endif %}
    {% if r.error %}<p><strong>Error:</strong> {{ r.error }}</p>{% endif %}
  </details>
</div>
{% endfor %}
</body></html>
"""


def write_html(result: ScanResult, path: str | Path, diff: dict | None = None) -> Path:
    p = Path(path)
    html = Template(HTML_TEMPLATE).render(result=result, diff=diff)
    p.write_text(html)
    return p
