"""Pydantic models for metrics export functionality."""

from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import Literal

from pydantic import BaseModel, Field


class MetricType(str, Enum):
    """Types of metrics that can be collected and alerted on."""

    CPU = "cpu"
    MEMORY = "memory"
    MEMORY_PERCENT = "memory_percent"
    NETWORK_RX = "network_rx"
    NETWORK_TX = "network_tx"
    BLOCK_READ = "block_read"
    BLOCK_WRITE = "block_write"
    PIDS = "pids"


class AlertSeverity(str, Enum):
    """Severity levels for alerts."""

    INFO = "info"
    WARNING = "warning"
    CRITICAL = "critical"


class AlertRule(BaseModel):
    """Configuration for an alert rule.

    Alert rules define conditions under which alerts should fire based on
    container metrics exceeding or falling below thresholds.

    Example:
        >>> rule = AlertRule(
        ...     name="high-cpu",
        ...     container_pattern="*",
        ...     metric=MetricType.CPU,
        ...     threshold=90.0,
        ...     comparison="gt",
        ...     severity=AlertSeverity.WARNING,
        ... )
    """

    name: str = Field(..., description="Unique name for this alert rule")
    container_pattern: str = Field(
        default="*", description="Glob pattern to match container names"
    )
    metric: MetricType = Field(..., description="Metric to monitor")
    threshold: float = Field(..., description="Threshold value for comparison")
    comparison: Literal["gt", "lt", "gte", "lte"] = Field(
        default="gt", description="Comparison operator"
    )
    severity: AlertSeverity = Field(
        default=AlertSeverity.WARNING, description="Alert severity level"
    )
    duration_seconds: int = Field(
        default=0, description="How long condition must persist before firing"
    )
    enabled: bool = Field(default=True, description="Whether this rule is active")
    description: str = Field(default="", description="Human-readable description")

    def evaluate(self, value: float) -> bool:
        """Evaluate whether the given value triggers this rule.

        Args:
            value: The metric value to compare

        Returns:
            True if the condition is met (alert should fire)
        """
        if self.comparison == "gt":
            return value > self.threshold
        elif self.comparison == "lt":
            return value < self.threshold
        elif self.comparison == "gte":
            return value >= self.threshold
        elif self.comparison == "lte":
            return value <= self.threshold
        return False

    def format_message(self, container_name: str, value: float) -> str:
        """Format an alert message for this rule.

        Args:
            container_name: Name of the container
            value: Current metric value

        Returns:
            Formatted alert message
        """
        op_symbols = {"gt": ">", "lt": "<", "gte": ">=", "lte": "<="}
        return (
            f"[{self.severity.value.upper()}] {self.name}: "
            f"Container '{container_name}' {self.metric.value} is {value:.2f} "
            f"({op_symbols[self.comparison]} {self.threshold})"
        )


class AlertState(BaseModel):
    """Current state of an alert.

    Tracks whether an alert is currently firing and when it started.
    """

    rule_name: str = Field(..., description="Name of the alert rule")
    container_name: str = Field(..., description="Container that triggered the alert")
    firing: bool = Field(default=False, description="Whether alert is currently firing")
    first_triggered: datetime | None = Field(
        default=None, description="When condition was first met"
    )
    last_checked: datetime | None = Field(
        default=None, description="Last time this alert was evaluated"
    )
    last_value: float = Field(default=0.0, description="Most recent metric value")
    message: str = Field(default="", description="Current alert message")


class MetricsServerConfig(BaseModel):
    """Configuration for the Prometheus metrics server.

    Example:
        >>> config = MetricsServerConfig(
        ...     host="0.0.0.0",
        ...     port=9100,
        ...     refresh_interval=15.0,
        ... )
    """

    host: str = Field(
        default="127.0.0.1", description="Host address to bind the server"
    )
    port: int = Field(default=9100, description="Port number for the HTTP server")
    refresh_interval: float = Field(
        default=15.0, description="How often to collect metrics (seconds)"
    )
    containers: list[str] | None = Field(
        default=None, description="Specific containers to monitor (None = all)"
    )
    enabled: bool = Field(default=True, description="Whether the server is enabled")


class ContainerMetrics(BaseModel):
    """Collected metrics for a single container.

    This model mirrors ContainerStats but adds container identification.
    """

    container_name: str = Field(..., description="Name of the container")
    runtime: str = Field(..., description="Runtime type (docker, nspawn, libvirt)")
    status: str = Field(..., description="Container status")
    collected_at: datetime = Field(
        default_factory=datetime.now, description="When metrics were collected"
    )

    # Resource metrics
    cpu_percent: float = Field(default=0.0, description="CPU usage percentage")
    memory_used_bytes: int = Field(default=0, description="Memory usage in bytes")
    memory_limit_bytes: int = Field(default=0, description="Memory limit in bytes")
    memory_percent: float = Field(default=0.0, description="Memory usage percentage")
    network_rx_bytes: int = Field(default=0, description="Network bytes received")
    network_tx_bytes: int = Field(default=0, description="Network bytes transmitted")
    block_read_bytes: int = Field(default=0, description="Block device bytes read")
    block_write_bytes: int = Field(default=0, description="Block device bytes written")
    pids: int = Field(default=0, description="Number of processes")

    def get_metric_value(self, metric: MetricType) -> float:
        """Get the value for a specific metric type.

        Args:
            metric: The metric type to retrieve

        Returns:
            The metric value as a float
        """
        mapping = {
            MetricType.CPU: self.cpu_percent,
            MetricType.MEMORY: float(self.memory_used_bytes),
            MetricType.MEMORY_PERCENT: self.memory_percent,
            MetricType.NETWORK_RX: float(self.network_rx_bytes),
            MetricType.NETWORK_TX: float(self.network_tx_bytes),
            MetricType.BLOCK_READ: float(self.block_read_bytes),
            MetricType.BLOCK_WRITE: float(self.block_write_bytes),
            MetricType.PIDS: float(self.pids),
        }
        return mapping.get(metric, 0.0)


class MetricsSnapshot(BaseModel):
    """A point-in-time snapshot of all container metrics."""

    collected_at: datetime = Field(
        default_factory=datetime.now, description="When snapshot was collected"
    )
    containers: list[ContainerMetrics] = Field(
        default_factory=list, description="Metrics for each container"
    )

    @property
    def container_count(self) -> int:
        """Number of containers in this snapshot."""
        return len(self.containers)

    def get_container(self, name: str) -> ContainerMetrics | None:
        """Get metrics for a specific container.

        Args:
            name: Container name

        Returns:
            ContainerMetrics or None if not found
        """
        for container in self.containers:
            if container.container_name == name:
                return container
        return None
