"""Base metrics collection for Robin AI.

Provides generic metrics collection using OpenTelemetry metrics API,
including performance counters, HTTP/DB/Cache metrics.
"""

import time
from typing import Any

from opentelemetry import metrics

from robin_commons.telemetry.config import get_observability_config


class BaseMetricsCollector:
    """Centralized metrics collection for generic microservice metrics."""

    def __init__(self) -> None:
        """Initialize metrics collector with OpenTelemetry meter."""
        self.config = get_observability_config()
        self._meter = metrics.get_meter(self.config.get_service_name())
        self._metrics = self._initialize_metrics()

    def _initialize_metrics(self) -> dict[str, Any]:
        """Initialize all generic metrics."""
        return {
            # HTTP Request Metrics
            "http_requests_total": self._meter.create_counter(
                name="http_requests_total",
                description="Total number of HTTP requests",
                unit="1",
            ),
            "http_request_duration": self._meter.create_histogram(
                name="http_request_duration_seconds",
                description="HTTP request duration in seconds",
                unit="s",
            ),
            "http_request_size": self._meter.create_histogram(
                name="http_request_size_bytes",
                description="HTTP request size in bytes",
                unit="By",
            ),
            "http_response_size": self._meter.create_histogram(
                name="http_response_size_bytes",
                description="HTTP response size in bytes",
                unit="By",
            ),
            # Database Metrics
            "database_queries_total": self._meter.create_counter(
                name="database_queries_total",
                description="Total number of database queries",
                unit="1",
            ),
            "database_query_duration": self._meter.create_histogram(
                name="database_query_duration_seconds",
                description="Database query duration in seconds",
                unit="s",
            ),
            "database_connections_active": self._meter.create_up_down_counter(
                name="database_connections_active",
                description="Number of active database connections",
                unit="1",
            ),
            "database_connection_pool_size": self._meter.create_up_down_counter(
                name="database_connection_pool_size",
                description="Database connection pool size",
                unit="1",
            ),
            # Cache Metrics
            "cache_operations_total": self._meter.create_counter(
                name="cache_operations_total",
                description="Total number of cache operations",
                unit="1",
            ),
            "cache_hit_ratio": self._meter.create_histogram(
                name="cache_hit_ratio",
                description="Cache hit ratio",
                unit="1",
            ),
            "cache_operation_duration": self._meter.create_histogram(
                name="cache_operation_duration_seconds",
                description="Cache operation duration in seconds",
                unit="s",
            ),
            # Error Metrics
            "errors_total": self._meter.create_counter(
                name="errors_total",
                description="Total number of errors",
                unit="1",
            ),
            "circuit_breaker_state": self._meter.create_up_down_counter(
                name="circuit_breaker_state",
                description="Circuit breaker state (0=closed, 1=open, 2=half-open)",
                unit="1",
            ),
            # System Metrics
            "active_users": self._meter.create_up_down_counter(
                name="active_users",
                description="Number of active users",
                unit="1",
            ),
        }

    # HTTP Metrics
    def record_http_request(
        self,
        method: str,
        route: str,
        status_code: int,
        duration_seconds: float,
        request_size: int = 0,
        response_size: int = 0,
    ) -> None:
        """Record HTTP request metrics."""
        if not self.config.is_metrics_enabled():
            return

        attributes = {
            "method": method,
            "route": route,
            "status_code": str(status_code),
            "status_class": f"{status_code // 100}xx",
        }

        self._metrics["http_requests_total"].add(1, attributes)
        self._metrics["http_request_duration"].record(duration_seconds, attributes)

        if request_size > 0:
            self._metrics["http_request_size"].record(request_size, attributes)

        if response_size > 0:
            self._metrics["http_response_size"].record(response_size, attributes)

    # Database Metrics
    def record_database_query(
        self,
        operation: str,
        table: str,
        duration_seconds: float,
        success: bool = True,
    ) -> None:
        """Record database query metrics."""
        if not self.config.is_metrics_enabled():
            return

        attributes = {
            "operation": operation,
            "table": table,
            "success": str(success).lower(),
        }

        self._metrics["database_queries_total"].add(1, attributes)
        self._metrics["database_query_duration"].record(duration_seconds, attributes)

    def update_database_connections(self, active: int, pool_size: int) -> None:
        """Update database connection metrics."""
        if not self.config.is_metrics_enabled():
            return

        self._metrics["database_connections_active"].add(active)
        self._metrics["database_connection_pool_size"].add(pool_size)

    # Cache Metrics
    def record_cache_operation(
        self,
        operation: str,
        hit: bool,
        duration_seconds: float,
    ) -> None:
        """Record cache operation metrics."""
        if not self.config.is_metrics_enabled():
            return

        attributes = {
            "operation": operation,
            "result": "hit" if hit else "miss",
        }

        self._metrics["cache_operations_total"].add(1, attributes)
        self._metrics["cache_operation_duration"].record(duration_seconds, attributes)
        self._metrics["cache_hit_ratio"].record(1.0 if hit else 0.0, attributes)

    # Error Metrics
    def record_error(
        self,
        error_type: str,
        component: str,
        severity: str = "error",
    ) -> None:
        """Record error metric."""
        if not self.config.is_metrics_enabled():
            return

        attributes = {
            "error_type": error_type,
            "component": component,
            "severity": severity,
        }

        self._metrics["errors_total"].add(1, attributes)

    def update_circuit_breaker_state(self, component: str, state: str) -> None:
        """Update circuit breaker state metric."""
        if not self.config.is_metrics_enabled():
            return

        state_value = {"closed": 0, "open": 1, "half_open": 2}.get(state, 0)
        attributes = {"component": component}

        self._metrics["circuit_breaker_state"].add(state_value, attributes)

    # System Metrics
    def update_active_users(self, count: int) -> None:
        """Update active users metric."""
        if not self.config.is_metrics_enabled():
            return

        self._metrics["active_users"].add(count)


# Global metrics collector instance
_metrics_collector: BaseMetricsCollector | None = None


def get_metrics_collector() -> BaseMetricsCollector:
    """Get the global metrics collector instance."""
    global _metrics_collector
    if _metrics_collector is None:
        _metrics_collector = BaseMetricsCollector()
    return _metrics_collector


# Convenience functions for common metrics
def record_http_request(
    method: str,
    route: str,
    status_code: int,
    duration_seconds: float,
    **kwargs: Any,
) -> None:
    """Record HTTP request metrics."""
    collector = get_metrics_collector()
    collector.record_http_request(
        method, route, status_code, duration_seconds, **kwargs
    )


def record_database_query(
    operation: str,
    table: str,
    duration_seconds: float,
    success: bool = True,
) -> None:
    """Record database query metrics."""
    collector = get_metrics_collector()
    collector.record_database_query(operation, table, duration_seconds, success)


def record_cache_operation(operation: str, hit: bool, duration_seconds: float) -> None:
    """Record cache operation metrics."""
    collector = get_metrics_collector()
    collector.record_cache_operation(operation, hit, duration_seconds)


def record_error(error_type: str, component: str, severity: str = "error") -> None:
    """Record error metrics."""
    collector = get_metrics_collector()
    collector.record_error(error_type, component, severity)


# Context manager for automatic timing
class timed_operation:
    """Context manager for automatic operation timing."""

    def __init__(self, metric_name: str, **attributes: Any):
        """Initialize timed operation.

        Args:
            metric_name: Name of the metric to record
            **attributes: Additional attributes for the metric
        """
        self.metric_name = metric_name
        self.attributes = attributes
        self.start_time = 0.0

    def __enter__(self) -> "timed_operation":
        """Start timing the operation."""
        self.start_time = time.perf_counter()
        return self

    def __exit__(
        self, exc_type: type | None, exc_val: Exception | None, exc_tb: Any
    ) -> None:
        """Record the operation duration."""
        duration = time.perf_counter() - self.start_time
        success = exc_val is None

        # Record based on metric type
        if self.metric_name == "http_request":
            record_http_request(duration_seconds=duration, **self.attributes)
        elif self.metric_name == "database_query":
            record_database_query(
                duration_seconds=duration, success=success, **self.attributes
            )
        elif self.metric_name == "cache_operation":
            record_cache_operation(duration_seconds=duration, **self.attributes)

        # Record error if operation failed
        if exc_val:
            record_error(
                error_type=type(exc_val).__name__,
                component=self.metric_name,
                severity="error",
            )
