"""Domain metric model."""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import UTC, datetime
from enum import Enum
from typing import Any
from uuid import uuid4


class MetricType(str, Enum):
    """Types of metrics."""

    GAUGE = "gauge"  # Point-in-time value
    COUNTER = "counter"  # Monotonically increasing
    HISTOGRAM = "histogram"  # Distribution of values
    SUMMARY = "summary"  # Statistical summary


@dataclass
class DomainMetric:
    """
    Domain-agnostic metric representation.

    Supports various metric types and includes rich metadata
    for filtering, aggregation, and alerting.
    """

    name: str
    value: float
    metric_type: MetricType = MetricType.GAUGE
    timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))
    unit: str | None = None
    description: str | None = None
    tags: dict[str, str] = field(default_factory=dict)
    attributes: dict[str, Any] = field(default_factory=dict)
    collector: str | None = None
    domain: str = "it"  # it, security, compliance, etc.
    id: str = field(default_factory=lambda: str(uuid4()))

    def to_dict(self) -> dict[str, Any]:
        """Convert metric to dictionary for serialization."""
        return {
            "id": self.id,
            "name": self.name,
            "value": self.value,
            "type": self.metric_type.value,
            "timestamp": self.timestamp.isoformat(),
            "unit": self.unit,
            "description": self.description,
            "tags": self.tags,
            "attributes": self.attributes,
            "collector": self.collector,
            "domain": self.domain,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> DomainMetric:
        """Create metric from dictionary."""
        timestamp = data.get("timestamp")
        if isinstance(timestamp, str):
            timestamp = datetime.fromisoformat(timestamp)
        elif timestamp is None:
            timestamp = datetime.now(UTC)

        metric_type = data.get("type", "gauge")
        if isinstance(metric_type, str):
            metric_type = MetricType(metric_type)

        return cls(
            id=data.get("id", str(uuid4())),
            name=data["name"],
            value=data["value"],
            metric_type=metric_type,
            timestamp=timestamp,
            unit=data.get("unit"),
            description=data.get("description"),
            tags=data.get("tags", {}),
            attributes=data.get("attributes", {}),
            collector=data.get("collector"),
            domain=data.get("domain", "it"),
        )

    def with_tags(self, **tags: str) -> DomainMetric:
        """Return a copy with additional tags."""
        return DomainMetric(
            id=self.id,
            name=self.name,
            value=self.value,
            metric_type=self.metric_type,
            timestamp=self.timestamp,
            unit=self.unit,
            description=self.description,
            tags={**self.tags, **tags},
            attributes=self.attributes,
            collector=self.collector,
            domain=self.domain,
        )

    def with_attributes(self, **attrs: Any) -> DomainMetric:
        """Return a copy with additional attributes."""
        return DomainMetric(
            id=self.id,
            name=self.name,
            value=self.value,
            metric_type=self.metric_type,
            timestamp=self.timestamp,
            unit=self.unit,
            description=self.description,
            tags=self.tags,
            attributes={**self.attributes, **attrs},
            collector=self.collector,
            domain=self.domain,
        )


def gauge(
    name: str,
    value: float,
    *,
    unit: str | None = None,
    tags: dict[str, str] | None = None,
    collector: str | None = None,
) -> DomainMetric:
    """Create a gauge metric."""
    return DomainMetric(
        name=name,
        value=value,
        metric_type=MetricType.GAUGE,
        unit=unit,
        tags=tags or {},
        collector=collector,
    )


def counter(
    name: str,
    value: float,
    *,
    unit: str | None = None,
    tags: dict[str, str] | None = None,
    collector: str | None = None,
) -> DomainMetric:
    """Create a counter metric."""
    return DomainMetric(
        name=name,
        value=value,
        metric_type=MetricType.COUNTER,
        unit=unit,
        tags=tags or {},
        collector=collector,
    )
