"""Agent self-monitoring metrics collector."""

from __future__ import annotations

import contextlib
import os
from typing import TYPE_CHECKING, Any

import psutil
import structlog

from curadise_agent.collectors.base import DomainCollector
from curadise_agent.models.domain.metric import DomainMetric, MetricType

if TYPE_CHECKING:
    from curadise_agent.config.schema import CollectorConfig
    from curadise_agent.state.manager import StateManager
    from curadise_agent.transport.buffer import RingBuffer
    from curadise_agent.transport.client import TransportClient

log = structlog.get_logger(__name__)


class SelfMetricsCollector(DomainCollector):
    """
    Collects metrics about the agent itself.

    Metrics include:
    - Buffer utilization
    - Metrics sent/dropped
    - Collector errors
    - Command executions
    - Agent resource usage (CPU, memory)
    - Connection status
    """

    def __init__(
        self,
        name: str = "self",
        config: CollectorConfig | None = None,
        tags: dict[str, str] | None = None,
        buffer: RingBuffer[Any] | None = None,
        transport: TransportClient | None = None,
        state_manager: StateManager | None = None,
        collectors_stats: dict[str, Any] | None = None,
    ) -> None:
        """
        Initialize self-metrics collector.

        Args:
            name: Collector name
            config: Collector configuration
            tags: Default tags
            buffer: Reference to metric buffer
            transport: Reference to transport client
            state_manager: Reference to state manager
            collectors_stats: Dict to gather collector statistics
        """
        super().__init__(name=name, config=config, tags=tags)
        self._buffer = buffer
        self._transport = transport
        self._state_manager = state_manager
        self._collectors_stats = collectors_stats or {}
        self._process = psutil.Process(os.getpid())

        # Internal counters
        self._command_executions_total = 0
        self._command_failures_total = 0

    def record_command_execution(self, success: bool) -> None:
        """Record a command execution."""
        self._command_executions_total += 1
        if not success:
            self._command_failures_total += 1

    async def collect(self) -> list[DomainMetric]:
        """Collect agent self-monitoring metrics."""
        metrics: list[DomainMetric] = []

        # Buffer metrics
        if self._buffer:
            metrics.extend(self._collect_buffer_metrics())

        # Transport metrics
        if self._transport:
            metrics.extend(self._collect_transport_metrics())

        # Agent resource usage
        metrics.extend(self._collect_resource_metrics())

        # State metrics
        if self._state_manager:
            metrics.extend(self._collect_state_metrics())

        # Command execution metrics
        metrics.extend(self._collect_command_metrics())

        # Collector health metrics
        metrics.extend(self._collect_collector_health_metrics())

        return metrics

    def _collect_buffer_metrics(self) -> list[DomainMetric]:
        """Collect buffer-related metrics."""
        assert self._buffer is not None
        stats = self._buffer.stats()

        return [
            DomainMetric(
                name="agent.buffer.size_current",
                value=float(stats.current_size),
                metric_type=MetricType.GAUGE,
                unit="items",
                description="Current number of items in buffer",
            ),
            DomainMetric(
                name="agent.buffer.size_max",
                value=float(stats.max_size),
                metric_type=MetricType.GAUGE,
                unit="items",
                description="Maximum buffer capacity",
            ),
            DomainMetric(
                name="agent.buffer.utilization_percent",
                value=stats.utilization,
                metric_type=MetricType.GAUGE,
                unit="percent",
                description="Buffer utilization percentage",
            ),
            DomainMetric(
                name="agent.buffer.added_total",
                value=float(stats.total_added),
                metric_type=MetricType.COUNTER,
                unit="items",
                description="Total items added to buffer",
            ),
            DomainMetric(
                name="agent.buffer.removed_total",
                value=float(stats.total_removed),
                metric_type=MetricType.COUNTER,
                unit="items",
                description="Total items removed from buffer",
            ),
            DomainMetric(
                name="agent.buffer.dropped_total",
                value=float(stats.total_dropped),
                metric_type=MetricType.COUNTER,
                unit="items",
                description="Total items dropped due to overflow",
            ),
        ]

    def _collect_transport_metrics(self) -> list[DomainMetric]:
        """Collect transport-related metrics."""
        assert self._transport is not None
        stats = self._transport.stats

        return [
            DomainMetric(
                name="agent.transport.requests_sent_total",
                value=float(stats.requests_sent),
                metric_type=MetricType.COUNTER,
                unit="requests",
            ),
            DomainMetric(
                name="agent.transport.requests_successful_total",
                value=float(stats.requests_successful),
                metric_type=MetricType.COUNTER,
                unit="requests",
            ),
            DomainMetric(
                name="agent.transport.requests_failed_total",
                value=float(stats.requests_failed),
                metric_type=MetricType.COUNTER,
                unit="requests",
            ),
            DomainMetric(
                name="agent.transport.bytes_sent_total",
                value=float(stats.bytes_sent),
                metric_type=MetricType.COUNTER,
                unit="bytes",
            ),
            DomainMetric(
                name="agent.transport.bytes_received_total",
                value=float(stats.bytes_received),
                metric_type=MetricType.COUNTER,
                unit="bytes",
            ),
            DomainMetric(
                name="agent.transport.batches_sent_total",
                value=float(stats.batches_sent),
                metric_type=MetricType.COUNTER,
                unit="batches",
            ),
            DomainMetric(
                name="agent.transport.metrics_sent_total",
                value=float(stats.metrics_sent),
                metric_type=MetricType.COUNTER,
                unit="metrics",
            ),
            DomainMetric(
                name="agent.transport.circuit_state",
                value=1.0 if self._transport.circuit_state == "closed" else 0.0,
                metric_type=MetricType.GAUGE,
                attributes={"state": self._transport.circuit_state},
            ),
        ]

    def _collect_resource_metrics(self) -> list[DomainMetric]:
        """Collect agent resource usage metrics."""
        metrics: list[DomainMetric] = []

        try:
            # CPU usage
            cpu_percent = self._process.cpu_percent()
            metrics.append(
                DomainMetric(
                    name="agent.process.cpu_percent",
                    value=cpu_percent,
                    metric_type=MetricType.GAUGE,
                    unit="percent",
                )
            )

            # Memory usage
            mem_info = self._process.memory_info()
            metrics.extend(
                [
                    DomainMetric(
                        name="agent.process.memory_rss_bytes",
                        value=float(mem_info.rss),
                        metric_type=MetricType.GAUGE,
                        unit="bytes",
                        description="Resident set size",
                    ),
                    DomainMetric(
                        name="agent.process.memory_vms_bytes",
                        value=float(mem_info.vms),
                        metric_type=MetricType.GAUGE,
                        unit="bytes",
                        description="Virtual memory size",
                    ),
                ]
            )

            # Thread count
            metrics.append(
                DomainMetric(
                    name="agent.process.threads",
                    value=float(self._process.num_threads()),
                    metric_type=MetricType.GAUGE,
                    unit="threads",
                )
            )

            # File descriptors (Unix only)
            with contextlib.suppress(psutil.AccessDenied, AttributeError):
                metrics.append(
                    DomainMetric(
                        name="agent.process.open_files",
                        value=float(len(self._process.open_files())),
                        metric_type=MetricType.GAUGE,
                        unit="files",
                    )
                )

        except psutil.NoSuchProcess:
            pass

        return metrics

    def _collect_state_metrics(self) -> list[DomainMetric]:
        """Collect state-related metrics."""
        assert self._state_manager is not None

        state_values = {
            "initializing": 0,
            "registering": 1,
            "running": 2,
            "degraded": 3,
            "shutting_down": 4,
            "stopped": 5,
        }

        return [
            DomainMetric(
                name="agent.state",
                value=float(state_values.get(self._state_manager.state.value, -1)),
                metric_type=MetricType.GAUGE,
                attributes={"state_name": self._state_manager.state.value},
            ),
            DomainMetric(
                name="agent.state_duration_seconds",
                value=self._state_manager.state_duration_seconds,
                metric_type=MetricType.GAUGE,
                unit="seconds",
            ),
            DomainMetric(
                name="agent.registered",
                value=1.0 if self._state_manager.is_registered else 0.0,
                metric_type=MetricType.GAUGE,
            ),
            DomainMetric(
                name="agent.authenticated",
                value=1.0 if self._state_manager.is_authenticated else 0.0,
                metric_type=MetricType.GAUGE,
            ),
        ]

    def _collect_command_metrics(self) -> list[DomainMetric]:
        """Collect command execution metrics."""
        return [
            DomainMetric(
                name="agent.commands.executions_total",
                value=float(self._command_executions_total),
                metric_type=MetricType.COUNTER,
                unit="executions",
            ),
            DomainMetric(
                name="agent.commands.failures_total",
                value=float(self._command_failures_total),
                metric_type=MetricType.COUNTER,
                unit="failures",
            ),
        ]

    def _collect_collector_health_metrics(self) -> list[DomainMetric]:
        """Collect health metrics for all collectors."""
        metrics: list[DomainMetric] = []

        for name, stats in self._collectors_stats.items():
            if isinstance(stats, dict):
                tags = {"collector": name}
                metrics.extend(
                    [
                        DomainMetric(
                            name="agent.collector.collections_total",
                            value=float(stats.get("total_collections", 0)),
                            metric_type=MetricType.COUNTER,
                            tags=tags,
                        ),
                        DomainMetric(
                            name="agent.collector.errors_total",
                            value=float(stats.get("failed_collections", 0)),
                            metric_type=MetricType.COUNTER,
                            tags=tags,
                        ),
                    ]
                )

        return metrics
