"""Webhook delivery metrics tracking.

Provides visibility into webhook health and delivery statistics.

Sprint GAP-003: Error Handling Mismatches
"""

from __future__ import annotations

import time
from collections import defaultdict
from dataclasses import dataclass, field
from threading import Lock
from typing import Final

from noteflow.domain.webhooks import WebhookEventType
from noteflow.infrastructure.logging import get_logger

_logger = get_logger(__name__)

# Metric window for rolling stats (5 minutes)
METRIC_WINDOW_SECONDS: Final[int] = 300


@dataclass(frozen=True, slots=True)
class WebhookDeliveryStats:
    """Point-in-time webhook delivery statistics."""

    total_deliveries: int
    successful_deliveries: int
    failed_deliveries: int
    total_retries: int
    avg_duration_ms: float
    success_rate: float

    @classmethod
    def empty(cls) -> WebhookDeliveryStats:
        """Create empty stats."""
        return cls(
            total_deliveries=0,
            successful_deliveries=0,
            failed_deliveries=0,
            total_retries=0,
            avg_duration_ms=0.0,
            success_rate=1.0,
        )


@dataclass
class _DeliveryMetric:
    """Single delivery metric."""

    timestamp: float
    success: bool
    duration_ms: int
    attempts: int
    event_type: str


@dataclass
class _MetricsBuffer:
    """Thread-safe buffer for rolling metrics."""

    metrics: list[_DeliveryMetric] = field(default_factory=list)
    lock: Lock = field(default_factory=Lock)

    def append(self, metric: _DeliveryMetric) -> None:
        """Append a metric and prune old entries."""
        cutoff = time.time() - METRIC_WINDOW_SECONDS
        with self.lock:
            self.metrics = [m for m in self.metrics if m.timestamp > cutoff]
            self.metrics.append(metric)

    def compute_stats(self) -> WebhookDeliveryStats:
        """Calculate current stats from buffer."""
        cutoff = time.time() - METRIC_WINDOW_SECONDS
        with self.lock:
            recent = [m for m in self.metrics if m.timestamp > cutoff]

        if not recent:
            return WebhookDeliveryStats.empty()

        total = len(recent)
        successful = sum(bool(m.success)
                     for m in recent)
        failed = total - successful
        total_retries = sum(m.attempts - 1 for m in recent if m.attempts > 1)
        durations = [m.duration_ms for m in recent if m.duration_ms > 0]
        avg_duration = sum(durations) / len(durations) if durations else 0.0
        success_rate = successful / total if total > 0 else 1.0

        return WebhookDeliveryStats(
            total_deliveries=total,
            successful_deliveries=successful,
            failed_deliveries=failed,
            total_retries=total_retries,
            avg_duration_ms=avg_duration,
            success_rate=success_rate,
        )

    def compute_stats_by_event(self) -> dict[str, WebhookDeliveryStats]:
        """Get stats broken down by event type."""
        cutoff = time.time() - METRIC_WINDOW_SECONDS
        with self.lock:
            recent = [m for m in self.metrics if m.timestamp > cutoff]

        by_type: dict[str, list[_DeliveryMetric]] = defaultdict(list)
        for m in recent:
            by_type[m.event_type].append(m)

        result: dict[str, WebhookDeliveryStats] = {}
        for event_type, metrics in by_type.items():
            total = len(metrics)
            successful = sum(bool(m.success)
                         for m in metrics)
            failed = total - successful
            total_retries = sum(m.attempts - 1 for m in metrics if m.attempts > 1)
            durations = [m.duration_ms for m in metrics if m.duration_ms > 0]
            avg_duration = sum(durations) / len(durations) if durations else 0.0
            success_rate = successful / total if total > 0 else 1.0

            result[event_type] = WebhookDeliveryStats(
                total_deliveries=total,
                successful_deliveries=successful,
                failed_deliveries=failed,
                total_retries=total_retries,
                avg_duration_ms=avg_duration,
                success_rate=success_rate,
            )

        return result


class WebhookMetrics:
    """Track webhook delivery metrics.

    Provides rolling window statistics for webhook health monitoring.
    Thread-safe for concurrent delivery tracking.
    """

    def __init__(self) -> None:
        """Initialize metrics tracker."""
        self._buffer = _MetricsBuffer()

    def record_delivery(
        self,
        event_type: WebhookEventType,
        success: bool,
        duration_ms: int,
        attempts: int,
    ) -> None:
        """Record a webhook delivery result.

        Args:
            event_type: Type of webhook event.
            success: Whether delivery succeeded.
            duration_ms: Total delivery time in milliseconds.
            attempts: Number of delivery attempts.
        """
        metric = _DeliveryMetric(
            timestamp=time.time(),
            success=success,
            duration_ms=duration_ms,
            attempts=attempts,
            event_type=event_type.value,
        )
        self._buffer.append(metric)

        # Log metric for observability
        status = "success" if success else "failed"
        _logger.info(
            "webhook_metric_recorded",
            event_type=event_type.value,
            status=status,
            duration_ms=duration_ms,
            attempts=attempts,
        )

    def get_stats(self) -> WebhookDeliveryStats:
        """Get current aggregate delivery statistics.

        Returns:
            Rolling window statistics for all deliveries.
        """
        return self._buffer.compute_stats()

    def get_stats_by_event(self) -> dict[str, WebhookDeliveryStats]:
        """Get delivery statistics broken down by event type.

        Returns:
            Mapping of event type to rolling window statistics.
        """
        return self._buffer.compute_stats_by_event()


# Global singleton
_webhook_metrics: WebhookMetrics | None = None


def get_webhook_metrics() -> WebhookMetrics:
    """Get the global webhook metrics singleton.

    Returns:
        Shared WebhookMetrics instance.
    """
    global _webhook_metrics
    if _webhook_metrics is None:
        _webhook_metrics = WebhookMetrics()
    return _webhook_metrics


def reset_webhook_metrics() -> None:
    """Reset the global webhook metrics singleton (testing helper)."""
    global _webhook_metrics
    _webhook_metrics = None
