"""Session report — post-session summary of all intercepted calls."""

from __future__ import annotations

from collections import defaultdict
from dataclasses import dataclass, field

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

from interceptor.types import Decision, DecisionVerdict, RiskLevel


@dataclass
class _ToolRecord:
    """Accumulated stats for a single tool."""

    tool_name: str = ""
    total_calls: int = 0
    allowed: int = 0
    blocked: int = 0
    confirmations: int = 0
    highest_risk: RiskLevel = RiskLevel.LOW


class SessionReport:
    """Record decisions throughout a session and print a summary.

    Call :meth:`record` for each decision, then :meth:`print_report`
    at the end to display what happened, what was caught, and tips.
    """

    def __init__(self) -> None:
        self._decisions: list[Decision] = []
        self._tools: dict[str, _ToolRecord] = defaultdict(
            lambda: _ToolRecord()
        )

    def record(self, decision: Decision, tool_name: str) -> None:
        """Record a decision."""
        self._decisions.append(decision)

        rec = self._tools[tool_name]
        rec.tool_name = tool_name
        rec.total_calls += 1

        if decision.decision == DecisionVerdict.ALLOWED:
            rec.allowed += 1
        elif decision.decision == DecisionVerdict.BLOCKED:
            rec.blocked += 1
        elif decision.decision == DecisionVerdict.CONFIRMATION_REQUIRED:
            rec.confirmations += 1

        risk_order = {RiskLevel.LOW: 0, RiskLevel.MEDIUM: 1, RiskLevel.HIGH: 2}
        if risk_order.get(decision.risk_level, 0) > risk_order.get(rec.highest_risk, 0):
            rec.highest_risk = decision.risk_level

    def print_report(self, console: Console | None = None) -> None:
        """Print a rich, colorful session report to the console."""
        console = console or Console()
        total = len(self._decisions)
        if total == 0:
            console.print("[dim]No calls recorded this session.[/dim]")
            return

        allowed = sum(1 for d in self._decisions if d.decision == DecisionVerdict.ALLOWED)
        blocked = sum(1 for d in self._decisions if d.decision == DecisionVerdict.BLOCKED)
        confirmations = sum(1 for d in self._decisions if d.decision == DecisionVerdict.CONFIRMATION_REQUIRED)

        # --- Score: 0-100 based on how clean the session was ---
        score = int((allowed / total) * 100) if total > 0 else 100
        if score >= 80:
            score_color = "bold green"
            score_label = "EXCELLENT"
        elif score >= 50:
            score_color = "bold yellow"
            score_label = "NEEDS WORK"
        else:
            score_color = "bold red"
            score_label = "POOR"

        # --- Visual score bar ---
        bar_width = 20
        filled = int((score / 100) * bar_width)
        empty = bar_width - filled
        bar = "[bold green]" + ("=" * filled) + "[/bold green]"
        bar += "[dim]" + ("-" * empty) + "[/dim]"

        body = Text()

        # --- Header with score ---
        body.append("SESSION REPORT\n\n", style="bold magenta underline")
        body.append("Agent Score:  ", style="bold")
        body.append(f"{score}/100 ", style=score_color)
        body.append(f"({score_label})\n", style=score_color)

        # Score bar (use markup for this)
        console.print()
        score_bar_panel = Panel(
            f"  [bold magenta]Agent Score[/bold magenta]  [{score_color.replace('bold ', '')}]{score}/100 {score_label}[/{score_color.replace('bold ', '')}]\n"
            f"  {bar}\n\n"
            f"  [bold white]Total:[/bold white] {total}   "
            f"[bold green]Allowed:[/bold green] {allowed}   "
            f"[bold red]Blocked:[/bold red] {blocked}   "
            f"[bold yellow]Confirm:[/bold yellow] {confirmations}",
            title="[bold bright_magenta]INTERCEPTOR SESSION REPORT[/bold bright_magenta]",
            border_style="bright_magenta",
            expand=False,
        )
        console.print(score_bar_panel)

        # --- Threats caught ---
        threats = [d for d in self._decisions if d.decision == DecisionVerdict.BLOCKED]
        if threats:
            threat_lines: list[str] = []
            for d in threats:
                threat_lines.append(f"  [bold red]BLOCKED[/bold red]  {d.intent_summary}")
                if d.explanation:
                    threat_lines.append(f"           [italic bright_red]{d.explanation}[/italic bright_red]")
            threat_panel = Panel(
                "\n".join(threat_lines),
                title="[bold red]THREATS CAUGHT[/bold red]",
                border_style="red",
                expand=False,
            )
            console.print(threat_panel)

        # --- Confirmations needed ---
        confirms = [d for d in self._decisions if d.decision == DecisionVerdict.CONFIRMATION_REQUIRED]
        if confirms:
            confirm_lines: list[str] = []
            for d in confirms:
                confirm_lines.append(f"  [bold yellow]CONFIRM[/bold yellow]  {d.intent_summary}")
            confirm_panel = Panel(
                "\n".join(confirm_lines),
                title="[bold yellow]NEEDED CONFIRMATION[/bold yellow]",
                border_style="yellow",
                expand=False,
            )
            console.print(confirm_panel)

        # --- Safe calls ---
        safe = [d for d in self._decisions if d.decision == DecisionVerdict.ALLOWED and d.risk_level == RiskLevel.LOW]
        if safe:
            console.print(
                f"  [bold green]SAFE[/bold green]  {len(safe)} call(s) passed with no issues\n"
            )

        # --- Tips ---
        tips = self._generate_tips()
        if tips:
            tip_lines: list[str] = []
            for tip in tips:
                tip_lines.append(f"  [bold bright_cyan]>>[/bold bright_cyan] [italic]{tip}[/italic]")
            tip_panel = Panel(
                "\n".join(tip_lines),
                title="[bold bright_cyan]SMART TIPS[/bold bright_cyan]",
                border_style="cyan",
                expand=False,
            )
            console.print(tip_panel)

    def _generate_tips(self) -> list[str]:
        """Generate smart, actionable one-liner tips from session data."""
        tips: list[str] = []
        total = len(self._decisions)
        if total == 0:
            return tips

        total_blocks = sum(r.blocked for r in self._tools.values())
        total_confirms = sum(r.confirmations for r in self._tools.values())
        total_allowed = sum(r.allowed for r in self._tools.values())

        # 1. Wasted LLM cycles — every block = the LLM reasoned, planned,
        #    and generated a tool call that was thrown away.
        if total_blocks > 0:
            tips.append(
                f"{total_blocks} blocked call(s) = {total_blocks} wasted LLM reasoning cycle(s). "
                f"Teach your agent which tools it can't use to save tokens."
            )

        # 2. Repeated blocks on same tool — agent keeps trying something forbidden
        for rec in self._tools.values():
            if rec.blocked >= 2:
                tips.append(
                    f"Your agent tried '{rec.tool_name}' {rec.blocked} times and was blocked every time. "
                    f"It's burning tokens retrying a forbidden action."
                )

        # 3. Duplicate intents — same intent summary means same call repeated
        intent_counts: dict[str, int] = defaultdict(int)
        for d in self._decisions:
            intent_counts[d.intent_summary] += 1
        for intent, count in intent_counts.items():
            if count >= 3:
                short = intent[:60] + "..." if len(intent) > 60 else intent
                tips.append(
                    f"Identical call repeated {count}x: '{short}'. "
                    f"Cache the result instead of re-calling."
                )

        # 4. Confirmation fatigue — too many prompts degrade trust
        if total_confirms >= 3:
            tips.append(
                f"{total_confirms} calls needed confirmation. "
                f"Add these tools to a YAML policy as MEDIUM or switch to balanced mode."
            )

        # 5. Mode suggestion — if everything was LOW risk, strict is overkill
        all_low = all(d.risk_level == RiskLevel.LOW for d in self._decisions)
        if all_low and total >= 3:
            tips.append(
                "Every call was LOW risk. Switch to observe mode for less overhead."
            )

        # 6. High block rate — agent is fighting the guardrails
        if total > 3:
            block_rate = total_blocks / total
            if block_rate >= 0.5:
                tips.append(
                    f"{int(block_rate * 100)}% of calls were blocked. "
                    f"Your agent's tool selection needs tuning — it's wasting most of its output."
                )

        # 7. Clean session — positive reinforcement
        if total_blocks == 0 and total_confirms == 0 and total >= 3:
            tips.append(
                "Clean session — zero blocks, zero confirmations. Your agent is well-behaved."
            )

        # 8. Heavy single-tool usage — possible loop
        for rec in self._tools.values():
            if rec.total_calls >= 5:
                tips.append(
                    f"'{rec.tool_name}' fired {rec.total_calls} times. "
                    f"Looks like a loop — add error handling or a break condition."
                )

        return tips

    def reset(self) -> None:
        """Clear all recorded decisions."""
        self._decisions.clear()
        self._tools.clear()
