"""Base collector interface."""

from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any

import structlog

if TYPE_CHECKING:
    from curadise_agent.config.schema import CollectorConfig
    from curadise_agent.models.domain.metric import DomainMetric

log = structlog.get_logger(__name__)


@dataclass
class CollectorStats:
    """Statistics about collector execution."""

    total_collections: int = 0
    successful_collections: int = 0
    failed_collections: int = 0
    total_metrics_collected: int = 0
    last_collection_time: datetime | None = None
    last_error: str | None = None
    average_duration_ms: float = 0.0

    @property
    def success_rate(self) -> float:
        """Calculate success rate."""
        if self.total_collections == 0:
            return 0.0
        return self.successful_collections / self.total_collections


@dataclass
class CollectorResult:
    """Result of a collection run."""

    metrics: list[DomainMetric]
    duration_ms: float
    success: bool = True
    error: str | None = None
    timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))


class DomainCollector(ABC):
    """
    Abstract base class for metric collectors.

    Collectors are responsible for gathering metrics from specific sources
    (system, HTTP endpoints, custom applications, etc.) and returning them
    in a standardized format.
    """

    def __init__(
        self,
        name: str,
        config: CollectorConfig | None = None,
        tags: dict[str, str] | None = None,
    ) -> None:
        """
        Initialize collector.

        Args:
            name: Unique collector name
            config: Collector configuration
            tags: Default tags to add to all metrics
        """
        self._name = name
        self._config = config
        self._tags = tags or {}
        self._stats = CollectorStats()
        self._enabled = config.enabled if config else True
        self._log = log.bind(collector=name)

    @property
    def name(self) -> str:
        """Get collector name."""
        return self._name

    @property
    def enabled(self) -> bool:
        """Check if collector is enabled."""
        return self._enabled

    @property
    def stats(self) -> CollectorStats:
        """Get collector statistics."""
        return self._stats

    @property
    def interval(self) -> float:
        """Get collection interval in seconds."""
        if self._config:
            return self._config.interval
        return 60.0

    @property
    def timeout(self) -> float:
        """Get collection timeout in seconds."""
        if self._config:
            return self._config.timeout
        return 30.0

    @property
    def options(self) -> dict[str, Any]:
        """Get collector-specific options."""
        if self._config:
            return self._config.options
        return {}

    def enable(self) -> None:
        """Enable the collector."""
        self._enabled = True
        self._log.info("collector_enabled")

    def disable(self) -> None:
        """Disable the collector."""
        self._enabled = False
        self._log.info("collector_disabled")

    @abstractmethod
    async def collect(self) -> list[DomainMetric]:
        """
        Collect metrics.

        Returns:
            List of collected metrics

        Raises:
            CollectorError: If collection fails
        """
        ...

    async def run(self) -> CollectorResult:
        """
        Execute collection with error handling and statistics.

        Returns:
            CollectorResult with metrics or error
        """
        if not self._enabled:
            return CollectorResult(
                metrics=[],
                duration_ms=0,
                success=False,
                error="Collector disabled",
            )

        start_time = datetime.now(UTC)
        self._stats.total_collections += 1

        try:
            metrics = await self.collect()

            # Add default tags to all metrics
            if self._tags:
                metrics = [m.with_tags(**self._tags) for m in metrics]

            # Set collector name on all metrics
            for metric in metrics:
                metric.collector = self._name

            duration_ms = (datetime.now(UTC) - start_time).total_seconds() * 1000

            self._stats.successful_collections += 1
            self._stats.total_metrics_collected += len(metrics)
            self._stats.last_collection_time = datetime.now(UTC)
            self._stats.last_error = None

            # Update average duration
            n = self._stats.successful_collections
            self._stats.average_duration_ms = (
                self._stats.average_duration_ms * (n - 1) + duration_ms
            ) / n

            self._log.debug(
                "collection_complete",
                metric_count=len(metrics),
                duration_ms=duration_ms,
            )

            return CollectorResult(
                metrics=metrics,
                duration_ms=duration_ms,
                success=True,
            )

        except Exception as e:
            duration_ms = (datetime.now(UTC) - start_time).total_seconds() * 1000
            error_msg = str(e)

            self._stats.failed_collections += 1
            self._stats.last_collection_time = datetime.now(UTC)
            self._stats.last_error = error_msg

            self._log.error("collection_failed", error=error_msg, duration_ms=duration_ms)

            return CollectorResult(
                metrics=[],
                duration_ms=duration_ms,
                success=False,
                error=error_msg,
            )

    async def initialize(self) -> None:
        """
        Initialize the collector.

        Override to perform setup tasks like establishing connections.
        """
        pass

    async def shutdown(self) -> None:
        """
        Shutdown the collector.

        Override to perform cleanup tasks like closing connections.
        """
        pass

    def get_status(self) -> dict[str, Any]:
        """Get collector status information."""
        return {
            "name": self._name,
            "enabled": self._enabled,
            "interval": self.interval,
            "stats": {
                "total_collections": self._stats.total_collections,
                "successful_collections": self._stats.successful_collections,
                "failed_collections": self._stats.failed_collections,
                "success_rate": self._stats.success_rate,
                "total_metrics": self._stats.total_metrics_collected,
                "average_duration_ms": self._stats.average_duration_ms,
                "last_collection": (
                    self._stats.last_collection_time.isoformat()
                    if self._stats.last_collection_time
                    else None
                ),
                "last_error": self._stats.last_error,
            },
        }
