"""Metrics collection and storage for tool calls, latency, errors, and resource usage."""

from __future__ import annotations

import json
import logging
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any

import aiosqlite

from mcp_bridge.config.settings import get_settings

logger = logging.getLogger(__name__)


class MetricsCollector:
    """
    Collects and stores metrics for all tool calls passing through the bridge.

    Stores data in SQLite for querying by the management API and extension dashboard.
    """

    def __init__(self, db_path: Path | None = None):
        self._db_path = db_path or get_settings().metrics_db_path
        self._db: aiosqlite.Connection | None = None

    async def initialize(self) -> None:
        """Initialize the metrics database."""
        self._db = await aiosqlite.connect(str(self._db_path))
        self._db.row_factory = aiosqlite.Row
        await self._create_tables()
        logger.info("Metrics collector initialized at %s", self._db_path)

    async def close(self) -> None:
        """Close the database connection."""
        if self._db:
            await self._db.close()
            self._db = None

    async def _create_tables(self) -> None:
        """Create metrics tables."""
        assert self._db is not None
        await self._db.executescript("""
            CREATE TABLE IF NOT EXISTS tool_calls (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp TEXT NOT NULL,
                agent_session_id TEXT NOT NULL,
                instance_id TEXT NOT NULL,
                server_id TEXT NOT NULL,
                tool_name TEXT NOT NULL,
                success INTEGER NOT NULL,
                latency_ms REAL NOT NULL,
                error_message TEXT,
                input_size_bytes INTEGER DEFAULT 0,
                output_size_bytes INTEGER DEFAULT 0
            );

            CREATE TABLE IF NOT EXISTS instance_events (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                timestamp TEXT NOT NULL,
                instance_id TEXT NOT NULL,
                server_id TEXT NOT NULL,
                agent_session_id TEXT NOT NULL,
                event_type TEXT NOT NULL,
                details TEXT DEFAULT '{}'
            );

            CREATE INDEX IF NOT EXISTS idx_tool_calls_timestamp ON tool_calls(timestamp);
            CREATE INDEX IF NOT EXISTS idx_tool_calls_server ON tool_calls(server_id);
            CREATE INDEX IF NOT EXISTS idx_tool_calls_agent ON tool_calls(agent_session_id);
            CREATE INDEX IF NOT EXISTS idx_instance_events_timestamp ON instance_events(timestamp);
        """)

    # --- Recording ---

    async def record_tool_call(
        self,
        agent_session_id: str,
        instance_id: str,
        server_id: str,
        tool_name: str,
        success: bool,
        latency_ms: float,
        error_message: str | None = None,
        input_size_bytes: int = 0,
        output_size_bytes: int = 0,
    ) -> None:
        """Record a tool call metric."""
        if not get_settings().metrics.enabled:
            return

        assert self._db is not None
        await self._db.execute(
            """
            INSERT INTO tool_calls
            (timestamp, agent_session_id, instance_id, server_id, tool_name,
             success, latency_ms, error_message, input_size_bytes, output_size_bytes)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
            (
                datetime.now().isoformat(),
                agent_session_id,
                instance_id,
                server_id,
                tool_name,
                1 if success else 0,
                latency_ms,
                error_message,
                input_size_bytes,
                output_size_bytes,
            ),
        )
        await self._db.commit()

    async def record_instance_event(
        self,
        instance_id: str,
        server_id: str,
        agent_session_id: str,
        event_type: str,
        details: dict | None = None,
    ) -> None:
        """Record an instance lifecycle event (started, stopped, error, etc)."""
        if not get_settings().metrics.enabled:
            return

        assert self._db is not None
        await self._db.execute(
            """
            INSERT INTO instance_events
            (timestamp, instance_id, server_id, agent_session_id, event_type, details)
            VALUES (?, ?, ?, ?, ?, ?)
            """,
            (
                datetime.now().isoformat(),
                instance_id,
                server_id,
                agent_session_id,
                event_type,
                json.dumps(details or {}),
            ),
        )
        await self._db.commit()

    # --- Querying ---

    async def get_summary(self, hours: int = 24) -> dict[str, Any]:
        """Get a summary of metrics for the last N hours."""
        assert self._db is not None
        since = (datetime.now() - timedelta(hours=hours)).isoformat()

        summary: dict[str, Any] = {}

        # Total calls and success rate
        async with self._db.execute(
            "SELECT COUNT(*) as total, SUM(success) as successes, AVG(latency_ms) as avg_latency "
            "FROM tool_calls WHERE timestamp >= ?",
            (since,),
        ) as cursor:
            row = await cursor.fetchone()
            if row:
                total = row["total"] or 0
                successes = row["successes"] or 0
                summary["total_calls"] = total
                summary["success_rate"] = (successes / total * 100) if total > 0 else 100.0
                summary["avg_latency_ms"] = round(row["avg_latency"] or 0, 2)

        # Calls per server
        async with self._db.execute(
            "SELECT server_id, COUNT(*) as count, AVG(latency_ms) as avg_latency, "
            "SUM(CASE WHEN success = 0 THEN 1 ELSE 0 END) as errors "
            "FROM tool_calls WHERE timestamp >= ? GROUP BY server_id ORDER BY count DESC",
            (since,),
        ) as cursor:
            summary["per_server"] = []
            async for row in cursor:
                summary["per_server"].append(
                    {
                        "server_id": row["server_id"],
                        "call_count": row["count"],
                        "avg_latency_ms": round(row["avg_latency"], 2),
                        "error_count": row["errors"],
                    }
                )

        # Most used tools
        async with self._db.execute(
            "SELECT tool_name, server_id, COUNT(*) as count "
            "FROM tool_calls WHERE timestamp >= ? GROUP BY tool_name, server_id "
            "ORDER BY count DESC LIMIT 10",
            (since,),
        ) as cursor:
            summary["top_tools"] = []
            async for row in cursor:
                summary["top_tools"].append(
                    {"tool_name": row["tool_name"], "server_id": row["server_id"], "count": row["count"]}
                )

        # Instance events
        async with self._db.execute(
            "SELECT event_type, COUNT(*) as count FROM instance_events WHERE timestamp >= ? GROUP BY event_type",
            (since,),
        ) as cursor:
            summary["instance_events"] = {}
            async for row in cursor:
                summary["instance_events"][row["event_type"]] = row["count"]

        return summary

    async def get_recent_calls(self, limit: int = 50, server_id: str | None = None) -> list[dict]:
        """Get recent tool calls, optionally filtered by server."""
        assert self._db is not None
        query = "SELECT * FROM tool_calls"
        params: list = []

        if server_id:
            query += " WHERE server_id = ?"
            params.append(server_id)

        query += " ORDER BY timestamp DESC LIMIT ?"
        params.append(limit)

        calls = []
        async with self._db.execute(query, params) as cursor:
            async for row in cursor:
                calls.append(dict(row))
        return calls

    async def cleanup_old_data(self) -> int:
        """Remove metrics older than the retention period. Returns rows deleted."""
        assert self._db is not None
        settings = get_settings()
        cutoff = (datetime.now() - timedelta(days=settings.metrics.retention_days)).isoformat()

        deleted = 0
        result = await self._db.execute("DELETE FROM tool_calls WHERE timestamp < ?", (cutoff,))
        deleted += result.rowcount
        result = await self._db.execute("DELETE FROM instance_events WHERE timestamp < ?", (cutoff,))
        deleted += result.rowcount
        await self._db.commit()

        if deleted > 0:
            logger.info("Cleaned up %d old metric records", deleted)
        return deleted

    async def export_json(self, hours: int = 24) -> dict:
        """Export aggregate metrics as JSON.

        Returns only anonymized, aggregate performance data:
        call counts, latencies, and success rates per server.
        No tool call content, arguments, results, or user-
        identifiable information is included in the export.
        """
        summary = await self.get_summary(hours)
        # Strip any per-call detail; only aggregate data
        return {
            "summary": {
                "total_calls": summary.get("total_calls", 0),
                "success_rate": summary.get("success_rate", 100.0),
                "avg_latency_ms": summary.get("avg_latency_ms", 0),
                "per_server": summary.get("per_server", []),
                "top_tools": summary.get("top_tools", []),
            },
            "exported_at": datetime.now().isoformat(),
        }
