"""Agent logging for monitoring and metrics collection."""

from __future__ import annotations

import json
import uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any

from database import Database
from logging_config import get_logger

log = get_logger(__name__)


@dataclass
class AgentLogEntry:
    """Represents a single agent log entry."""

    id: str
    session_id: str | None
    timestamp: str
    event_type: str
    duration_ms: int | None = None
    tokens_input: int | None = None
    tokens_output: int | None = None
    tokens_total: int | None = None
    tool_name: str | None = None
    tool_args: str | None = None
    response_preview: str | None = None
    error: str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> dict[str, Any]:
        """Serialize to dictionary."""
        return {
            "id": self.id,
            "session_id": self.session_id,
            "timestamp": self.timestamp,
            "event_type": self.event_type,
            "duration_ms": self.duration_ms,
            "tokens_input": self.tokens_input,
            "tokens_output": self.tokens_output,
            "tokens_total": self.tokens_total,
            "tool_name": self.tool_name,
            "tool_args": self.tool_args,
            "response_preview": self.response_preview,
            "error": self.error,
            "metadata": self.metadata,
        }

    @classmethod
    def from_row(cls, row: Any) -> AgentLogEntry:
        """Create from database row."""
        return cls(
            id=row["id"],
            session_id=row["session_id"],
            timestamp=row["timestamp"],
            event_type=row["event_type"],
            duration_ms=row["duration_ms"],
            tokens_input=row["tokens_input"],
            tokens_output=row["tokens_output"],
            tokens_total=row["tokens_total"],
            tool_name=row["tool_name"],
            tool_args=row["tool_args"],
            response_preview=row["response_preview"],
            error=row["error"],
            metadata=json.loads(row["metadata"]) if row["metadata"] else {},
        )


@dataclass
class LogStats:
    """Aggregate statistics for agent logs."""

    total_requests: int = 0
    total_tool_calls: int = 0
    total_messages: int = 0
    total_errors: int = 0
    total_tokens_input: int = 0
    total_tokens_output: int = 0
    total_tokens: int = 0
    avg_duration_ms: float = 0.0
    tool_call_counts: dict[str, int] = field(default_factory=dict)

    def to_dict(self) -> dict[str, Any]:
        """Serialize to dictionary."""
        return {
            "total_requests": self.total_requests,
            "total_tool_calls": self.total_tool_calls,
            "total_messages": self.total_messages,
            "total_errors": self.total_errors,
            "total_tokens_input": self.total_tokens_input,
            "total_tokens_output": self.total_tokens_output,
            "total_tokens": self.total_tokens,
            "avg_duration_ms": self.avg_duration_ms,
            "tool_call_counts": self.tool_call_counts,
        }


class AgentLogger:
    """Logs agent activity to the database for monitoring."""

    def __init__(self, db: Database) -> None:
        self._db = db
        self._log = log.bind(component="agent_logger")

    def _now(self) -> str:
        """Get current timestamp in ISO format."""
        return datetime.now(timezone.utc).isoformat()

    def _generate_id(self) -> str:
        """Generate a unique log entry ID."""
        return uuid.uuid4().hex[:16]

    def _insert(self, entry: AgentLogEntry) -> None:
        """Insert a log entry into the database."""
        self._db.conn.execute(
            """INSERT INTO agent_logs
               (id, session_id, timestamp, event_type, duration_ms,
                tokens_input, tokens_output, tokens_total, tool_name,
                tool_args, response_preview, error, metadata)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
            (
                entry.id,
                entry.session_id,
                entry.timestamp,
                entry.event_type,
                entry.duration_ms,
                entry.tokens_input,
                entry.tokens_output,
                entry.tokens_total,
                entry.tool_name,
                entry.tool_args,
                entry.response_preview,
                entry.error,
                json.dumps(entry.metadata, ensure_ascii=False),
            ),
        )
        self._db.conn.commit()
        self._log.debug("log_entry_created", entry_id=entry.id, event_type=entry.event_type)

    def log_llm_request(
        self,
        session_id: str | None,
        duration_ms: int,
        tokens_input: int | None = None,
        tokens_output: int | None = None,
        response_preview: str | None = None,
        error: str | None = None,
        metadata: dict[str, Any] | None = None,
    ) -> AgentLogEntry:
        """Log an LLM API request."""
        entry = AgentLogEntry(
            id=self._generate_id(),
            session_id=session_id,
            timestamp=self._now(),
            event_type="llm_request",
            duration_ms=duration_ms,
            tokens_input=tokens_input,
            tokens_output=tokens_output,
            tokens_total=(tokens_input or 0) + (tokens_output or 0) if tokens_input or tokens_output else None,
            response_preview=response_preview[:500] if response_preview else None,
            error=error,
            metadata=metadata or {},
        )
        self._insert(entry)
        return entry

    def log_tool_call(
        self,
        session_id: str | None,
        tool_name: str,
        tool_args: dict[str, Any],
        duration_ms: int,
        result_preview: str | None = None,
        error: str | None = None,
        metadata: dict[str, Any] | None = None,
    ) -> AgentLogEntry:
        """Log a tool/skill execution."""
        entry = AgentLogEntry(
            id=self._generate_id(),
            session_id=session_id,
            timestamp=self._now(),
            event_type="tool_call",
            duration_ms=duration_ms,
            tool_name=tool_name,
            tool_args=json.dumps(tool_args, ensure_ascii=False),
            response_preview=result_preview[:500] if result_preview else None,
            error=error,
            metadata=metadata or {},
        )
        self._insert(entry)
        return entry

    def log_message(
        self,
        session_id: str | None,
        role: str,
        content_preview: str | None = None,
        metadata: dict[str, Any] | None = None,
    ) -> AgentLogEntry:
        """Log a message (user input or assistant response)."""
        entry = AgentLogEntry(
            id=self._generate_id(),
            session_id=session_id,
            timestamp=self._now(),
            event_type=f"message_{role}",
            response_preview=content_preview[:500] if content_preview else None,
            metadata=metadata or {},
        )
        self._insert(entry)
        return entry

    def log_error(
        self,
        session_id: str | None,
        error: str,
        context: str | None = None,
        metadata: dict[str, Any] | None = None,
    ) -> AgentLogEntry:
        """Log an error event."""
        entry = AgentLogEntry(
            id=self._generate_id(),
            session_id=session_id,
            timestamp=self._now(),
            event_type="error",
            error=error,
            response_preview=context,
            metadata=metadata or {},
        )
        self._insert(entry)
        return entry

    def get_logs(
        self,
        session_id: str | None = None,
        event_type: str | None = None,
        limit: int = 100,
        offset: int = 0,
    ) -> list[AgentLogEntry]:
        """Retrieve log entries with optional filtering."""
        query = "SELECT * FROM agent_logs WHERE 1=1"
        params: list[Any] = []

        if session_id:
            query += " AND session_id = ?"
            params.append(session_id)

        if event_type:
            query += " AND event_type = ?"
            params.append(event_type)

        query += " ORDER BY timestamp DESC LIMIT ? OFFSET ?"
        params.extend([limit, offset])

        rows = self._db.conn.execute(query, params).fetchall()
        return [AgentLogEntry.from_row(row) for row in rows]

    def get_stats(self, session_id: str | None = None) -> LogStats:
        """Get aggregate statistics for logs."""
        session_filter = "WHERE session_id = ?" if session_id else "WHERE 1=1"
        params: list[Any] = [session_id] if session_id else []

        # Get counts by event type
        rows = self._db.conn.execute(
            f"""SELECT event_type, COUNT(*) as count
                FROM agent_logs {session_filter}
                GROUP BY event_type""",
            params,
        ).fetchall()

        stats = LogStats()
        for row in rows:
            event_type = row["event_type"]
            count = row["count"]
            if event_type == "llm_request":
                stats.total_requests = count
            elif event_type == "tool_call":
                stats.total_tool_calls = count
            elif event_type.startswith("message_"):
                stats.total_messages += count
            elif event_type == "error":
                stats.total_errors = count

        # Get token totals and average duration
        row = self._db.conn.execute(
            f"""SELECT
                COALESCE(SUM(tokens_input), 0) as total_input,
                COALESCE(SUM(tokens_output), 0) as total_output,
                COALESCE(SUM(tokens_total), 0) as total_tokens,
                COALESCE(AVG(duration_ms), 0) as avg_duration
                FROM agent_logs
                {session_filter} AND event_type = 'llm_request'""",
            params,
        ).fetchone()

        if row:
            stats.total_tokens_input = row["total_input"]
            stats.total_tokens_output = row["total_output"]
            stats.total_tokens = row["total_tokens"]
            stats.avg_duration_ms = round(row["avg_duration"], 2)

        # Get tool call counts by tool name
        tool_rows = self._db.conn.execute(
            f"""SELECT tool_name, COUNT(*) as count
                FROM agent_logs
                {session_filter} AND event_type = 'tool_call' AND tool_name IS NOT NULL
                GROUP BY tool_name
                ORDER BY count DESC""",
            params,
        ).fetchall()

        stats.tool_call_counts = {row["tool_name"]: row["count"] for row in tool_rows}

        return stats

    def clear_logs(self, session_id: str | None = None) -> int:
        """Clear logs, optionally for a specific session. Returns count deleted."""
        if session_id:
            cursor = self._db.conn.execute(
                "DELETE FROM agent_logs WHERE session_id = ?", (session_id,)
            )
        else:
            cursor = self._db.conn.execute("DELETE FROM agent_logs")

        self._db.conn.commit()
        return cursor.rowcount
