"""Report rendering and gate logic (R-6, R-7).

Three output formats: human (default), json, and SARIF 2.1.0 for CI. Exit-code
policy: 0 when nothing meets the fail threshold, 2 when something does, 1 on tool
error (raised by the CLI, not here).
"""
from __future__ import annotations

import json
from typing import Optional

from .models import Report, Severity

FAIL_ON_CHOICES = ["critical", "high", "medium", "low", "any"]

_SEV_ICON = {
    Severity.CRITICAL: "CRIT",
    Severity.HIGH: "HIGH",
    Severity.MEDIUM: "MED ",
    Severity.LOW: "LOW ",
}


def meets_threshold(severity: Severity, fail_on: str) -> bool:
    if fail_on == "any":
        return True
    return severity.rank >= Severity.from_name(fail_on).rank


def exit_code(report: Report, fail_on: str) -> int:
    for f in report.findings:
        if meets_threshold(f.severity, fail_on):
            return 2
    return 0


def render_human(report: Report, fail_on: str = "high") -> str:
    lines: list[str] = []
    counts = report.severity_counts()
    n = len(report.findings)
    lines.append(
        f"PrivSweep scanned {len(report.scanned_files)} file(s); "
        f"{n} finding(s) "
        f"[critical {counts['critical']}, high {counts['high']}, "
        f"medium {counts['medium']}, low {counts['low']}]."
    )
    # Group by file, then severity rank.
    by_file: dict[str, list] = {}
    for f in report.sorted_findings():
        by_file.setdefault(f.file, []).append(f)
    for fp, items in by_file.items():
        lines.append("")
        lines.append(f"  {fp}")
        for f in items:
            role = f" role={f.role}" if f.role else ""
            conf = f" conf={f.confidence:.2f}" if f.confidence is not None else ""
            loc = f.location or "?"
            lines.append(
                f"    [{_SEV_ICON[f.severity]}] {f.type:<18} {loc:<16} "
                f"{f.preview}{role}{conf}"
            )
    if report.notes:
        lines.append("")
        lines.append("  Notes:")
        for note in report.notes:
            lines.append(f"    - {note}")
    if report.errors:
        lines.append("")
        lines.append("  Errors:")
        for err in report.errors:
            lines.append(f"    ! {err}")
    threshold_hit = exit_code(report, fail_on) == 2
    lines.append("")
    lines.append(
        f"  Gate: fail-on={fail_on} -> "
        + ("FAIL (exit 2)" if threshold_hit else "pass (exit 0)")
    )
    return "\n".join(lines)


def render_json(report: Report) -> str:
    return json.dumps(report.to_dict(), indent=2, sort_keys=False)


def render_sarif(report: Report) -> str:
    """Minimal valid SARIF 2.1.0 with one run and rule-per-finding-type."""
    rule_ids = sorted({f.type for f in report.findings})
    rules = [
        {
            "id": rid,
            "name": rid,
            "shortDescription": {"text": f"PrivSweep detector: {rid}"},
        }
        for rid in rule_ids
    ]
    results = []
    for f in report.sorted_findings():
        results.append(
            {
                "ruleId": f.type,
                "level": _sarif_level(f.severity),
                "message": {"text": f"{f.type} ({f.severity.value}): {f.preview}"},
                "locations": [
                    {
                        "physicalLocation": {
                            "artifactLocation": {"uri": _uri(f.file)},
                            "region": _region(f),
                        }
                    }
                ],
                "properties": {
                    "severity": f.severity.value,
                    "role": f.role,
                    "confidence": f.confidence,
                    "detector": f.detector,
                    "locationLabel": f.location,
                },
            }
        )
    sarif = {
        "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
        "version": "2.1.0",
        "runs": [
            {
                "tool": {
                    "driver": {
                        "name": "PrivSweep",
                        "informationUri": "https://github.com/nolanlawfirm/privsweep",
                        "version": "1.0.0",
                        "rules": rules,
                    }
                },
                "results": results,
            }
        ],
    }
    return json.dumps(sarif, indent=2)


def _sarif_level(severity: Severity) -> str:
    return {
        Severity.CRITICAL: "error",
        Severity.HIGH: "error",
        Severity.MEDIUM: "warning",
        Severity.LOW: "note",
    }[severity]


def _uri(path: str) -> str:
    return path.replace("\\", "/")


def _region(f) -> dict:
    if f.span:
        return {"charOffset": f.span[0], "charLength": max(0, f.span[1] - f.span[0])}
    return {"startLine": 1}


def render(report: Report, fmt: str = "human", fail_on: str = "high") -> str:
    if fmt == "json":
        return render_json(report)
    if fmt == "sarif":
        return render_sarif(report)
    return render_human(report, fail_on=fail_on)
