"""Prometheus metrics integration for Yera services."""

import time
from collections.abc import Callable
from contextlib import contextmanager
from functools import wraps
from typing import Any

from fastapi import Response
from prometheus_client import (
    CONTENT_TYPE_LATEST,
    CollectorRegistry,
    Counter,
    Gauge,
    Histogram,
    generate_latest,
)


class ServiceMetrics:
    """Prometheus metrics for a Yera service."""

    def __init__(self, service_name: str, registry: CollectorRegistry | None = None):
        """Initialize metrics for a service.

        Args:
            service_name: Name of the service (e.g., 'api', 'iam', 'stream')
            registry: Prometheus registry to use. If None, creates a new isolated registry.

        """
        self.service_name = service_name
        self.registry = registry or CollectorRegistry()
        prefix = f"{service_name}_"

        # Request metrics
        self.requests_total = Counter(
            f"{prefix}requests_total",
            f"Total number of {service_name} API requests",
            ["endpoint", "method", "status"],
            registry=self.registry,
        )

        self.request_duration_seconds = Histogram(
            f"{prefix}request_duration_seconds",
            f"Duration of {service_name} API requests",
            ["endpoint", "method"],
            buckets=[0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0],
            registry=self.registry,
        )

        # Error metrics
        self.errors_total = Counter(
            f"{prefix}errors_total",
            f"Total number of {service_name} errors",
            ["operation", "error_type"],
            registry=self.registry,
        )

        # Resource gauges (optional, can be extended)
        self._gauges: dict[str, Gauge] = {}

    def create_gauge(
        self, name: str, description: str, labels: list[str] | None = None
    ) -> Gauge:
        """Create a custom gauge metric."""
        full_name = f"{self.service_name}_{name}"
        if full_name in self._gauges:
            return self._gauges[full_name]

        gauge = Gauge(full_name, description, labels or [], registry=self.registry)
        self._gauges[full_name] = gauge
        return gauge

    def create_counter(
        self, name: str, description: str, labels: list[str] | None = None
    ) -> Counter:
        """Create a custom counter metric."""
        full_name = f"{self.service_name}_{name}"
        return Counter(full_name, description, labels or [], registry=self.registry)

    def create_histogram(
        self,
        name: str,
        description: str,
        labels: list[str] | None = None,
        buckets: list[float] | None = None,
    ) -> Histogram:
        """Create a custom histogram metric."""
        full_name = f"{self.service_name}_{name}"
        return Histogram(
            full_name,
            description,
            labels or [],
            buckets=buckets,
            registry=self.registry,
        )

    def track_request(
        self,
        endpoint: str,
        method: str,
        status_code: int,
        duration: float | None = None,
    ):
        """Track an API request."""
        self.requests_total.labels(
            endpoint=endpoint, method=method, status=str(status_code)
        ).inc()
        if duration is not None:
            self.request_duration_seconds.labels(
                endpoint=endpoint, method=method
            ).observe(duration)

    def track_error(self, operation: str, error_type: str):
        """Track an error."""
        self.errors_total.labels(operation=operation, error_type=error_type).inc()

    @contextmanager
    def track_operation_time(self, endpoint: str, method: str):
        """Context manager to track operation duration."""
        start_time = time.time()
        try:
            yield
        finally:
            duration = time.time() - start_time
            self.request_duration_seconds.labels(
                endpoint=endpoint, method=method
            ).observe(duration)


# Decorator for tracking requests
def track_request(metrics: ServiceMetrics, endpoint: str, method: str = "POST"):
    """Decorator to track request metrics."""

    def decorator(func: Callable) -> Callable:
        @wraps(func)
        async def wrapper(*args: Any, **kwargs: Any) -> Any:
            start_time = time.time()
            status_code = 200
            try:
                result = await func(*args, **kwargs)
                return result
            except Exception as e:
                if hasattr(e, "status_code"):
                    status_code = e.status_code
                else:
                    status_code = 500
                raise
            finally:
                duration = time.time() - start_time
                metrics.track_request(endpoint, method, status_code, duration)

        return wrapper

    return decorator


# Context manager for tracking operations
@contextmanager
def track_operation(metrics: ServiceMetrics, operation: str):
    """Context manager to track operation execution."""
    try:
        yield
    except Exception as e:
        error_type = type(e).__name__
        metrics.track_error(operation, error_type)
        raise


# Metrics endpoint generator
def get_metrics_endpoint(metrics: ServiceMetrics):
    """Get a FastAPI endpoint function for /metrics.

    Args:
        metrics: ServiceMetrics instance with the registry to use

    """

    async def metrics_endpoint() -> Response:
        """Prometheus metrics endpoint."""
        return Response(
            content=generate_latest(metrics.registry), media_type=CONTENT_TYPE_LATEST
        )

    return metrics_endpoint
