"""Rich terminal output and JSON rendering for probe reports."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from rich.console import Console
from rich.panel import Panel
from rich.text import Text

from rewardprobe.types import Report, Severity

# ── JSON / dict helpers ──────────────────────────────────────────────


def report_to_dict(report: Report) -> dict[str, Any]:
    """Convert a Report to a plain dict for programmatic use."""
    return {
        "environment": report.environment,
        "rewardprobe_version": report.rewardprobe_version,
        "timestamp": report.timestamp,
        "passed": report.passed,
        "summary": {
            "families_total": len(report.families),
            "families_clean": sum(1 for f in report.families if f.all_passed),
            "attacks_total": report.total_attacks,
            "attacks_passed": report.total_passed,
            "critical": report.critical_count,
            "warning": report.warning_count,
        },
        "families": [
            {
                "name": fam.name,
                "display_name": fam.display_name,
                "total": fam.total,
                "passed": fam.passed,
                "all_passed": fam.all_passed,
                "findings": [
                    {
                        "attack": f.attack,
                        "family": f.family,
                        "severity": f.severity.value,
                        "input": f.input,
                        "expected_reward": f.expected_reward,
                        "actual_reward": f.actual_reward,
                        "affected_tasks": f.affected_tasks,
                        "error": f.error,
                        "recommendation": f.recommendation,
                    }
                    for f in fam.findings
                ],
            }
            for fam in report.families
        ],
    }


def report_to_json(report: Report, path: str | Path) -> None:
    """Write a Report as JSON to *path*."""
    data = report_to_dict(report)
    p = Path(path)
    p.parent.mkdir(parents=True, exist_ok=True)
    p.write_text(json.dumps(data, indent=2) + "\n")


def report_as_json(report: Report) -> str:
    """Return the report as a JSON string."""
    return json.dumps(report_to_dict(report), indent=2)


# ── Rich terminal rendering ─────────────────────────────────────────


def _severity_tag(severity: Severity) -> Text:
    if severity == Severity.CRITICAL:
        return Text(" critical ", style="bold white on red")
    if severity == Severity.WARNING:
        return Text(" warning ", style="bold black on yellow")
    return Text(" info ", style="bold white on blue")


def _clean_recommendation(rec: str) -> str:
    """Extract the plain-English description from a recommendation."""
    # Strip the [AI] prefix if present
    if rec.startswith("[AI] "):
        rec = rec[5:]
    if rec.startswith("[AI-generated] "):
        rec = rec[15:]
    # Strip fn name prefix like "Reward fn 'name' ..."
    if rec.startswith("Reward fn '"):
        # Find the closing quote and skip past it
        idx = rec.find("' ", 11)
        if idx > 0:
            rec = rec[idx + 2 :]
    if rec.startswith("Rubric "):
        pass  # Keep rubric-level findings as-is
    return rec[0].upper() + rec[1:] if rec else rec


def print_report(report: Report, console: Console | None = None) -> None:
    """Render a clean, simple probe report to the terminal."""
    con = console or Console(width=90)

    # ── Header ──
    con.print()
    con.print(
        Text("rewardprobe", style="bold cyan") + Text(f" — {report.environment}", style="bold")
    )
    con.print()

    # ── Collect findings, exclude INFO from default output ──
    all_findings = [f for f in report.findings if f.severity != Severity.INFO]
    if not all_findings:
        con.print(
            Panel(
                Text("No issues found.", style="bold green"),
                title="Result",
                border_style="green",
            )
        )
        con.print()
        passed = report.total_passed
        total = report.total_attacks
        con.print(Text(f"  {passed}/{total} checks passed.", style="dim"))
        con.print()
        return

    # Sort: critical first, then warning, then info
    severity_order = {Severity.CRITICAL: 0, Severity.WARNING: 1, Severity.INFO: 2}
    sorted_findings = sorted(all_findings, key=lambda f: severity_order.get(f.severity, 3))

    # ── Count ──
    n_critical = report.critical_count
    n_warning = report.warning_count
    len(sorted_findings)

    summary_parts = []
    if n_critical:
        summary_parts.append(f"[bold red]{n_critical} critical[/]")
    if n_warning:
        summary_parts.append(f"[bold yellow]{n_warning} warning[/]")
    summary_str = ", ".join(summary_parts) if summary_parts else "issues"

    con.print(f"  {summary_str} found\n")

    # ── List findings in plain English ──
    for i, finding in enumerate(sorted_findings, 1):
        desc = _clean_recommendation(finding.recommendation)

        # Severity tag
        tag = _severity_tag(finding.severity)

        # Number + description
        con.print(Text(f"  {i}. ", style="bold") + tag)
        con.print(Text(f"     {desc}", style=""))

        # Show the exploit input if available and short enough
        if finding.input and len(finding.input) > 0:
            input_display = finding.input
            if len(input_display) > 70:
                input_display = input_display[:67] + "..."
            if finding.actual_reward is not None:
                con.print(
                    Text(f'     Input: "{input_display}"', style="dim")
                    + Text(f" → {finding.actual_reward:.1f}", style="dim bold")
                )
            elif finding.error:
                con.print(Text(f"     {finding.error.split(':')[0]}", style="dim red"))

        con.print()

    # ── Footer ──
    passed = report.total_passed
    total = report.total_attacks
    con.print(Text(f"  {passed}/{total} checks passed.", style="dim"))
    con.print()
