"""Rich TUI terminal formatter for scan results."""
from __future__ import annotations

from collections import Counter

from rich.console import Console
from rich.markup import escape
from rich.panel import Panel
from rich.rule import Rule
from rich.table import Table, box

# Severity sort order (CRITICAL first)
_SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3, "INFO": 4}

# Severity → (rich style, icon)
_SEVERITY_STYLE: dict[str, tuple[str, str]] = {
    "CRITICAL": ("red bold", "❌"),
    "HIGH": ("dark_orange", "⚠️"),
    "MEDIUM": ("yellow", "⚡"),
    "LOW": ("blue", "ℹ️"),
    "INFO": ("dim", "ℹ️"),
}

# Classification → (color, icon, display label)
_CLASSIFICATION_STYLE: dict[str, tuple[str, str, str]] = {
    "PUBLIC": ("red", "🔓", "public"),
    "AUTHENTICATED": ("yellow", "🔐", "authenticated"),
    "ROLE_RESTRICTED": ("green", "🔒", "role_restr."),
    "POLICY_RESTRICTED": ("blue", "🛡️", "policy_restr."),
}


def _sev_name(severity) -> str:
    """Return severity as an uppercase string regardless of enum vs str."""
    return (severity.name if hasattr(severity, "name") else str(severity)).upper()


def _cls_name(classification) -> str:
    """Return classification as an uppercase string regardless of enum vs str."""
    return (classification.name if hasattr(classification, "name") else str(classification)).upper()


class TerminalFormatter:
    """Renders a rich TUI report matching the .NET version's layout."""

    def __init__(self, console: Console) -> None:
        self._console = console

    def render(self, result, duration_ms: int, risk=None) -> None:
        """Render the complete scan report to the console."""
        self._render_header()

        if result.findings:
            self._render_findings(result.findings)
            self._render_scroll_hint()

        self._render_summary(result, duration_ms)

        if result.findings:
            self._render_severity_breakdown(result.findings)

        if risk is not None:
            self._render_risk_score(risk)

        self._render_endpoints(result.endpoints)

        if not result.findings:
            self._console.print("\n[green]✓[/green] No security findings!")

    # ------------------------------------------------------------------ #
    # Private helpers                                                      #
    # ------------------------------------------------------------------ #

    def _render_header(self) -> None:
        self._console.print()
        self._console.print(Rule("[bold blue]ApiPosture Pro - Security Scan[/bold blue]"))
        self._console.print()

    def _render_findings(self, findings: list) -> None:
        sorted_findings = sorted(
            findings,
            key=lambda f: (
                _SEVERITY_ORDER.get(_sev_name(f.severity), 5),
                str(getattr(f.endpoint, "location", "")),
            ),
        )
        for finding in sorted_findings:
            self._render_finding_panel(finding)

    def _render_finding_panel(self, finding) -> None:
        sev = _sev_name(finding.severity)
        style, icon = _SEVERITY_STYLE.get(sev, ("white", "•"))
        border_color = style.split()[0]  # strip "bold" modifier for border

        route = escape(str(getattr(finding.endpoint, "full_route", finding.endpoint.route)))
        location = escape(str(getattr(finding.endpoint, "location", "")))

        lines = [f"[bold]Route:[/bold]          {route}"]
        if location:
            lines.append(f"[bold]Location:[/bold]       {location}")
        lines.append("")
        lines.append(escape(finding.message))
        if finding.recommendation:
            lines.append("")
            lines.append(f"[bold]Recommendation:[/bold] {escape(finding.recommendation)}")

        # Escape the rule_id so "[AP014]" is not treated as a Rich tag
        rule_id_escaped = f"\\[{finding.rule_id}]"
        title = (
            f"[{style}]{icon} {rule_id_escaped} {escape(finding.rule_name)} ({sev})[/]"
        )
        self._console.print(
            Panel("\n".join(lines), title=title, border_style=border_color, box=box.ROUNDED)
        )

    def _render_scroll_hint(self) -> None:
        self._console.print()
        self._console.print(Rule("^^^^ Scroll up for finding details ^^^^", style="grey50"))
        self._console.print()

    def _render_summary(self, result, duration_ms: int) -> None:
        findings = result.findings
        active_count = len([f for f in findings if not getattr(f, "suppressed", False)])
        total_findings = len(findings)

        findings_cell = (
            f"{total_findings} ({active_count} active)"
            if active_count != total_findings
            else str(total_findings)
        )

        table = Table(box=box.ROUNDED, show_header=True, header_style="bold")
        table.add_column("Metric", style="bold", min_width=22)
        table.add_column("Value", min_width=32)

        table.add_row("Scanned Path", str(result.scan_path))
        table.add_row("Files Scanned", str(len(getattr(result, "files_scanned", []))))
        table.add_row("Failed Files", str(len(getattr(result, "parse_errors", {}))))
        table.add_row("Total Endpoints", str(len(result.endpoints)))
        table.add_row("Total Findings", findings_cell)
        table.add_row("Scan Duration", f"{duration_ms:,}ms")

        self._console.print(table)

    def _render_severity_breakdown(self, findings: list) -> None:
        counts = Counter(_sev_name(f.severity) for f in findings)
        total = len(findings)

        self._console.print()
        for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"]:
            count = counts.get(sev, 0)
            if count == 0:
                continue
            style, _ = _SEVERITY_STYLE.get(sev, ("white", ""))
            bar_width = int((count / total) * 40)
            bar = "█" * max(bar_width, 1)
            pct = int(count / total * 100)
            self._console.print(
                f"  [{style}]{sev:<10}[/]  [{style}]{bar:<40}[/]  {count:>4}  ({pct}%)"
            )
        self._console.print()

    def _render_risk_score(self, risk) -> None:
        color = getattr(risk, "color", "white")
        total = risk.total_score
        level = (
            risk.risk_level.value if hasattr(risk.risk_level, "value") else str(risk.risk_level)
        )
        content = (
            f"[bold]Risk Score:[/bold]  [{color} bold]{total} / 100  ({level})[/]\n"
            f"  ├─ Findings:     {risk.findings_score} / 50\n"
            f"  ├─ Exposure:     {risk.exposure_score} / 30\n"
            f"  └─ Surface Area: {risk.surface_area_score} / 20"
        )
        self._console.print(
            Panel(content, title="[bold]Risk Score[/bold]", border_style=color, box=box.ROUNDED)
        )
        self._console.print()

    def _render_endpoints(self, endpoints: list) -> None:
        self._console.print(Rule("Discovered Endpoints", align="left"))
        self._console.print()

        if not endpoints:
            self._console.print("  [dim]No endpoints discovered.[/dim]")
            return

        table = Table(box=box.ROUNDED, show_header=True, header_style="bold")
        table.add_column("Route", style="cyan")
        table.add_column("Methods")
        table.add_column("Type")
        table.add_column("Classification")

        for ep in endpoints:
            route = escape(str(getattr(ep, "full_route", ep.route)))

            display_methods = getattr(ep, "display_methods", None)
            if display_methods:
                methods_str = str(display_methods)
            else:
                methods_str = ", ".join(
                    m.value if hasattr(m, "value") else str(m) for m in ep.methods
                )

            ep_type = ep.endpoint_type
            type_str = (
                ep_type.value if hasattr(ep_type, "value") else str(ep_type)
            ).lower()

            cls = _cls_name(ep.classification)
            cls_color, cls_icon, cls_label = _CLASSIFICATION_STYLE.get(
                cls, ("white", "•", cls.lower())
            )
            cls_cell = f"[{cls_color}]{cls_icon} {cls_label}[/]"

            table.add_row(route, methods_str, type_str, cls_cell)

        self._console.print(table)
