"""Agent status monitoring and reporting module.

This module provides the StatusMonitor class for monitoring the economic
status of all agents, generating reports, and detecting survival status changes.
"""

from __future__ import annotations

import json
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Any

from rich.console import Console
from rich.table import Table

from openclaw.core.economy import SurvivalStatus, TradingEconomicTracker


@dataclass
class AgentStatusSnapshot:
    """Snapshot of an agent's status at a specific point in time.

    Attributes:
        agent_id: Unique identifier for the agent
        timestamp: ISO format timestamp when the snapshot was taken
        balance: Current balance
        initial_capital: Starting capital
        status: Current survival status
        total_costs: Accumulated token and trade costs
        realized_pnl: Realized profit/loss
        net_profit: Net profit (PnL minus costs)
    """

    agent_id: str
    timestamp: str
    balance: float
    initial_capital: float
    status: SurvivalStatus
    total_costs: float
    realized_pnl: float
    net_profit: float

    def to_dict(self) -> dict[str, Any]:
        """Convert snapshot to dictionary."""
        return {
            "agent_id": self.agent_id,
            "timestamp": self.timestamp,
            "balance": self.balance,
            "initial_capital": self.initial_capital,
            "status": self.status.value,
            "total_costs": self.total_costs,
            "realized_pnl": self.realized_pnl,
            "net_profit": self.net_profit,
        }


@dataclass
class StatusChange:
    """Record of a status change event.

    Attributes:
        agent_id: Unique identifier for the agent
        timestamp: ISO format timestamp when the change occurred
        old_status: Previous survival status
        new_status: New survival status
        balance: Balance at the time of change
    """

    agent_id: str
    timestamp: str
    old_status: SurvivalStatus
    new_status: SurvivalStatus
    balance: float

    def __str__(self) -> str:
        return (
            f"[{self.timestamp}] {self.agent_id}: "
            f"{self.old_status.value} → {self.new_status.value} "
            f"(${self.balance:.2f})"
        )


@dataclass
class StatusReport:
    """Comprehensive status report for all monitored agents.

    Attributes:
        timestamp: ISO format timestamp when the report was generated
        total_agents: Total number of agents
        status_counts: Dictionary mapping status to count
        agents: List of agent status snapshots
        changes: Recent status changes
        summary: Text summary of the report
    """

    timestamp: str
    total_agents: int
    status_counts: dict[SurvivalStatus, int]
    agents: list[AgentStatusSnapshot]
    changes: list[StatusChange]
    summary: str = ""

    def to_dict(self) -> dict[str, Any]:
        """Convert report to dictionary."""
        return {
            "timestamp": self.timestamp,
            "total_agents": self.total_agents,
            "status_counts": {
                status.value: count for status, count in self.status_counts.items()
            },
            "agents": [agent.to_dict() for agent in self.agents],
            "changes": [
                {
                    "agent_id": change.agent_id,
                    "timestamp": change.timestamp,
                    "old_status": change.old_status.value,
                    "new_status": change.new_status.value,
                    "balance": change.balance,
                }
                for change in self.changes
            ],
            "summary": self.summary,
        }

    def to_json(self, indent: int = 2) -> str:
        """Convert report to JSON string."""
        return json.dumps(self.to_dict(), indent=indent, ensure_ascii=False)

    def to_text(self) -> str:
        """Generate formatted text report."""
        lines = [
            "=" * 60,
            f"OpenClaw Agent Status Report - {self.timestamp}",
            "=" * 60,
            f"Total Agents: {self.total_agents}",
            "",
            "Status Distribution:",
        ]

        for status in SurvivalStatus:
            count = self.status_counts.get(status, 0)
            lines.append(f"  {status.value}: {count}")

        lines.extend(["", "Agent Details:", "-" * 60])

        for agent in sorted(self.agents, key=lambda a: a.balance, reverse=True):
            lines.append(
                f"  {agent.agent_id:<20} {agent.status.value:<12} "
                f"${agent.balance:>10.2f} (PnL: ${agent.net_profit:>+8.2f})"
            )

        if self.changes:
            lines.extend(["", "Recent Status Changes:", "-" * 60])
            for change in self.changes[-10:]:  # Show last 10 changes
                lines.append(f"  {change}")

        lines.append("=" * 60)
        return "\n".join(lines)


class StatusMonitor:
    """Monitor the economic status of all trading agents.

    The StatusMonitor tracks all agents' economic states, records status
    changes, generates reports, and provides real-time status displays.

    Example:
        monitor = StatusMonitor()

        # Register agents
        monitor.register_agent("trader_1", tracker1)
        monitor.register_agent("trader_2", tracker2)

        # Update and check status
        monitor.update()

        # Generate reports
        report = monitor.generate_report()
        print(report.to_text())

        # Display live status table
        monitor.display_live_status()
    """

    def __init__(self) -> None:
        """Initialize the status monitor."""
        self._agents: dict[str, TradingEconomicTracker] = {}
        self._last_status: dict[str, SurvivalStatus] = {}
        self._status_history: dict[str, list[StatusChange]] = {}
        self._console = Console()

    def register_agent(
        self, agent_id: str, tracker: TradingEconomicTracker
    ) -> None:
        """Register an agent to be monitored.

        Args:
            agent_id: Unique identifier for the agent
            tracker: The agent's economic tracker
        """
        self._agents[agent_id] = tracker
        current_status = tracker.get_survival_status()
        self._last_status[agent_id] = current_status
        self._status_history[agent_id] = []

    def unregister_agent(self, agent_id: str) -> None:
        """Unregister an agent from monitoring.

        Args:
            agent_id: Unique identifier for the agent
        """
        self._agents.pop(agent_id, None)
        self._last_status.pop(agent_id, None)
        self._status_history.pop(agent_id, None)

    def update(self) -> list[StatusChange]:
        """Update status for all agents and detect changes.

        Returns:
            List of status changes that occurred during this update
        """
        changes: list[StatusChange] = []
        timestamp = datetime.now().isoformat()

        for agent_id, tracker in self._agents.items():
            current_status = tracker.get_survival_status()
            last_status = self._last_status.get(agent_id)

            if last_status is not None and current_status != last_status:
                change = StatusChange(
                    agent_id=agent_id,
                    timestamp=timestamp,
                    old_status=last_status,
                    new_status=current_status,
                    balance=tracker.balance,
                )
                changes.append(change)
                self._status_history[agent_id].append(change)

            self._last_status[agent_id] = current_status

        return changes

    def get_snapshot(self, agent_id: str) -> AgentStatusSnapshot | None:
        """Get current status snapshot for a specific agent.

        Args:
            agent_id: Unique identifier for the agent

        Returns:
            AgentStatusSnapshot if agent is registered, None otherwise
        """
        tracker = self._agents.get(agent_id)
        if tracker is None:
            return None

        return AgentStatusSnapshot(
            agent_id=agent_id,
            timestamp=datetime.now().isoformat(),
            balance=tracker.balance,
            initial_capital=tracker.initial_capital,
            status=tracker.get_survival_status(),
            total_costs=tracker.total_costs,
            realized_pnl=tracker.realized_pnl,
            net_profit=tracker.net_profit,
        )

    def get_all_snapshots(self) -> list[AgentStatusSnapshot]:
        """Get current status snapshots for all agents.

        Returns:
            List of AgentStatusSnapshot for all registered agents
        """
        return [
            snapshot
            for agent_id in self._agents
            if (snapshot := self.get_snapshot(agent_id)) is not None
        ]

    def get_status_changes(self, agent_id: str | None = None) -> list[StatusChange]:
        """Get status change history.

        Args:
            agent_id: Optional agent ID to filter by. If None, returns
                     changes for all agents.

        Returns:
            List of StatusChange events
        """
        if agent_id is not None:
            return self._status_history.get(agent_id, [])

        all_changes: list[StatusChange] = []
        for changes in self._status_history.values():
            all_changes.extend(changes)
        return sorted(all_changes, key=lambda c: c.timestamp)

    def generate_report(self) -> StatusReport:
        """Generate a comprehensive status report.

        Returns:
            StatusReport containing current status of all agents
        """
        timestamp = datetime.now().isoformat()
        agents = self.get_all_snapshots()
        changes = self.get_status_changes()

        # Count agents by status
        status_counts: dict[SurvivalStatus, int] = {
            status: 0 for status in SurvivalStatus
        }
        for agent in agents:
            status_counts[agent.status] += 1

        # Generate summary
        total = len(agents)
        thriving = status_counts.get(SurvivalStatus.THRIVING, 0)
        bankrupt = status_counts.get(SurvivalStatus.BANKRUPT, 0)

        if bankrupt > 0:
            summary = f"ALERT: {bankrupt} agent(s) bankrupt! Immediate attention required."
        elif thriving == total and total > 0:
            summary = f"All {total} agent(s) thriving! Excellent performance."
        else:
            summary = f"Monitoring {total} agent(s). {thriving} thriving, {bankrupt} bankrupt."

        return StatusReport(
            timestamp=timestamp,
            total_agents=total,
            status_counts=status_counts,
            agents=agents,
            changes=changes,
            summary=summary,
        )

    def display_live_status(self, title: str = "OpenClaw Agent Status") -> None:
        """Display a live status table using Rich.

        Args:
            title: Title for the status table
        """
        self.update()
        agents = self.get_all_snapshots()

        table = Table(title=title, show_header=True, header_style="bold magenta")
        table.add_column("Agent ID", style="cyan", no_wrap=True)
        table.add_column("Status", justify="center")
        table.add_column("Balance", justify="right")
        table.add_column("Initial", justify="right")
        table.add_column("Net PnL", justify="right")
        table.add_column("Costs", justify="right")

        # Sort by balance descending
        for agent in sorted(agents, key=lambda a: a.balance, reverse=True):
            # Color code based on status
            status_color = {
                SurvivalStatus.THRIVING: "green",
                SurvivalStatus.STABLE: "blue",
                SurvivalStatus.STRUGGLING: "yellow",
                SurvivalStatus.CRITICAL: "red",
                SurvivalStatus.BANKRUPT: "dim red",
            }.get(agent.status, "white")

            # Color code PnL
            pnl_color = "green" if agent.net_profit >= 0 else "red"

            table.add_row(
                agent.agent_id,
                f"[{status_color}]{agent.status.value}[/{status_color}]",
                f"${agent.balance:,.2f}",
                f"${agent.initial_capital:,.2f}",
                f"[{pnl_color}]${agent.net_profit:+,.2f}[/{pnl_color}]",
                f"${agent.total_costs:,.2f}",
            )

        self._console.print(table)

    def display_status_changes(self, limit: int = 10) -> None:
        """Display recent status changes.

        Args:
            limit: Maximum number of changes to display
        """
        changes = self.get_status_changes()

        if not changes:
            self._console.print("[dim]No status changes recorded.[/dim]")
            return

        table = Table(title="Recent Status Changes", show_header=True)
        table.add_column("Time", style="dim")
        table.add_column("Agent", style="cyan")
        table.add_column("Change")
        table.add_column("Balance", justify="right")

        for change in changes[-limit:]:
            old_color = self._get_status_color(change.old_status)
            new_color = self._get_status_color(change.new_status)

            change_str = (
                f"[{old_color}]{change.old_status.value}[/{old_color}] → "
                f"[{new_color}]{change.new_status.value}[/{new_color}]"
            )

            table.add_row(
                change.timestamp.split("T")[1].split(".")[0],  # Show just time
                change.agent_id,
                change_str,
                f"${change.balance:,.2f}",
            )

        self._console.print(table)

    def _get_status_color(self, status: SurvivalStatus) -> str:
        """Get Rich color code for a status."""
        return {
            SurvivalStatus.THRIVING: "green",
            SurvivalStatus.STABLE: "blue",
            SurvivalStatus.STRUGGLING: "yellow",
            SurvivalStatus.CRITICAL: "red",
            SurvivalStatus.BANKRUPT: "dim red",
        }.get(status, "white")

    def save_report(
        self, filepath: str | Path, format: str = "json"
    ) -> None:
        """Save the current report to a file.

        Args:
            filepath: Path to save the report
            format: "json" or "text"
        """
        path = Path(filepath)
        path.parent.mkdir(parents=True, exist_ok=True)

        report = self.generate_report()

        if format.lower() == "json":
            path.write_text(report.to_json(), encoding="utf-8")
        else:
            path.write_text(report.to_text(), encoding="utf-8")

    @property
    def agent_count(self) -> int:
        """Return the number of registered agents."""
        return len(self._agents)

    @property
    def bankrupt_count(self) -> int:
        """Return the number of bankrupt agents."""
        return sum(
            1 for tracker in self._agents.values()
            if tracker.get_survival_status() == SurvivalStatus.BANKRUPT
        )

    @property
    def thriving_count(self) -> int:
        """Return the number of thriving agents."""
        return sum(
            1 for tracker in self._agents.values()
            if tracker.get_survival_status() == SurvivalStatus.THRIVING
        )
