"""System metrics collection."""

from __future__ import annotations

import asyncio
import time
from collections import deque
from dataclasses import dataclass

import psutil

from noteflow.infrastructure.logging import get_logger

logger = get_logger(__name__)


@dataclass(frozen=True, slots=True)
class PerformanceMetrics:
    """Point-in-time performance metrics.

    Attributes:
        timestamp: Unix timestamp of measurement.
        cpu_percent: CPU usage percentage (0-100).
        memory_percent: System memory usage percentage.
        memory_mb: System memory used in megabytes.
        disk_percent: Root disk usage percentage.
        network_bytes_sent: Network bytes sent since last measurement.
        network_bytes_recv: Network bytes received since last measurement.
        process_memory_mb: NoteFlow process memory in megabytes.
        active_connections: Number of active network connections.
    """

    timestamp: float
    cpu_percent: float
    memory_percent: float
    memory_mb: float
    disk_percent: float
    network_bytes_sent: int
    network_bytes_recv: int
    process_memory_mb: float
    active_connections: int


class MetricsCollector:
    """Collect and buffer system performance metrics.

    Provides real-time system metrics for the observability dashboard.
    Can run in background mode for continuous collection.
    """

    DEFAULT_HISTORY_SIZE = 60  # 1 minute of data at 1s intervals

    def __init__(self, history_size: int = DEFAULT_HISTORY_SIZE) -> None:
        """Initialize the metrics collector.

        Args:
            history_size: Maximum number of historical data points to retain.
        """
        self._history: deque[PerformanceMetrics] = deque(maxlen=history_size)
        self._process = psutil.Process()
        self._last_net_io = psutil.net_io_counters()
        self._running = False
        self._task: asyncio.Task[None] | None = None

    def collect_now(self) -> PerformanceMetrics:
        """Collect current system metrics.

        Returns:
            Current performance metrics snapshot.
        """
        # Get CPU usage (non-blocking, uses cached value)
        cpu = psutil.cpu_percent(interval=None)

        # Get memory info
        memory = psutil.virtual_memory()

        # Get disk info (root filesystem)
        try:
            disk = psutil.disk_usage("/")
            disk_percent = disk.percent
        except OSError:
            disk_percent = 0.0

        # Get network I/O (delta since last call)
        net_io = psutil.net_io_counters()
        bytes_sent = net_io.bytes_sent - self._last_net_io.bytes_sent
        bytes_recv = net_io.bytes_recv - self._last_net_io.bytes_recv
        self._last_net_io = net_io

        # Get process memory
        try:
            process_mem = self._process.memory_info().rss / (1024 * 1024)
        except psutil.NoSuchProcess:
            process_mem = 0.0

        # Get active connections (count only, no details)
        try:
            connections = len(psutil.net_connections(kind="inet"))
        except psutil.AccessDenied:
            connections = 0

        metrics = PerformanceMetrics(
            timestamp=time.time(),
            cpu_percent=cpu,
            memory_percent=memory.percent,
            memory_mb=memory.used / (1024 * 1024),
            disk_percent=disk_percent,
            network_bytes_sent=max(0, bytes_sent),
            network_bytes_recv=max(0, bytes_recv),
            process_memory_mb=process_mem,
            active_connections=connections,
        )

        self._history.append(metrics)
        return metrics

    def get_history(self, limit: int = 60) -> list[PerformanceMetrics]:
        """Get recent metrics history.

        Args:
            limit: Maximum data points to return.

        Returns:
            Historical metrics (oldest to newest).
        """
        return list(self._history)[-limit:]

    async def start_collection(self, interval: float = 1.0) -> None:
        """Start background metrics collection.

        Collects metrics at the specified interval until stopped.

        Args:
            interval: Collection interval in seconds.
        """
        if self._running:
            return

        self._running = True
        logger.info("Starting metrics collection (interval=%.1fs)", interval)

        # Initial CPU reading to prime the counter
        psutil.cpu_percent(interval=None)

        while self._running:
            try:
                self.collect_now()
            except (OSError, RuntimeError) as e:
                logger.exception("Error collecting metrics: %s", e)
            await asyncio.sleep(interval)

    def stop_collection(self) -> None:
        """Stop background metrics collection."""
        self._running = False
        if self._task and not self._task.done():
            self._task.cancel()
        logger.info("Stopped metrics collection")

    @property
    def is_running(self) -> bool:
        """Check if background collection is active."""
        return self._running


# Global singleton
_metrics_collector: MetricsCollector | None = None


def get_metrics_collector() -> MetricsCollector:
    """Get the global metrics collector singleton.

    Returns:
        The shared MetricsCollector instance.
    """
    global _metrics_collector
    if _metrics_collector is None:
        _metrics_collector = MetricsCollector()
    return _metrics_collector
