"""
OpenTelemetry middleware and custom instrumentation for ivatar.

This module provides custom OpenTelemetry instrumentation for avatar-specific
operations, including metrics and tracing for avatar generation, file uploads,
and authentication flows.
"""

import logging
import time
from functools import wraps

from django.http import HttpRequest, HttpResponse
from django.utils.deprecation import MiddlewareMixin

from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode

from ivatar.opentelemetry_config import get_tracer, get_meter, is_enabled

logger = logging.getLogger("ivatar")


class OpenTelemetryMiddleware(MiddlewareMixin):
    """
    Custom OpenTelemetry middleware for ivatar-specific metrics and tracing.

    This middleware adds custom attributes and metrics to OpenTelemetry spans
    for avatar-related operations.
    """

    def __init__(self, get_response):
        self.get_response = get_response
        # Don't get metrics instance here - get it lazily in __call__

    def __call__(self, request):
        # Get metrics instance lazily
        if not hasattr(self, "metrics"):
            self.metrics = get_avatar_metrics()

        # Process request to start tracing
        self.process_request(request)

        response = self.get_response(request)

        # Process response to complete tracing
        self.process_response(request, response)

        return response

    def process_request(self, request: HttpRequest) -> None:
        """Process incoming request and start tracing."""
        # Start span for the request
        span_name = f"{request.method} {request.path}"
        span = get_tracer("ivatar.middleware").start_span(span_name)

        # Add request attributes
        span.set_attributes(
            {
                "http.method": request.method,
                "http.url": request.build_absolute_uri(),
                "http.user_agent": request.META.get("HTTP_USER_AGENT", ""),
                "http.remote_addr": self._get_client_ip(request),
                "ivatar.path": request.path,
            }
        )

        # Check if this is an avatar request
        if self._is_avatar_request(request):
            span.set_attribute("ivatar.request_type", "avatar")
            self._add_avatar_attributes(span, request)

        # Store span in request for later use
        request._ot_span = span

        # Record request start time
        request._ot_start_time = time.time()

    def process_response(
        self, request: HttpRequest, response: HttpResponse
    ) -> HttpResponse:
        """Process response and complete tracing."""
        span = getattr(request, "_ot_span", None)
        if not span:
            return response

        try:
            # Calculate request duration
            start_time = getattr(request, "_ot_start_time", time.time())
            duration = time.time() - start_time

            # Add response attributes
            span.set_attributes(
                {
                    "http.status_code": response.status_code,
                    "http.response_size": (
                        len(response.content) if hasattr(response, "content") else 0
                    ),
                    "http.request.duration": duration,
                }
            )

            # Set span status based on response
            if response.status_code >= 400:
                span.set_status(
                    Status(StatusCode.ERROR, f"HTTP {response.status_code}")
                )
            else:
                span.set_status(Status(StatusCode.OK))

            # Record metrics
            # Note: HTTP request metrics are handled by Django instrumentation
            # We only record avatar-specific metrics here

            # Record avatar-specific metrics
            if self._is_avatar_request(request):
                # Record avatar request metric using the new metrics system
                self.metrics.record_avatar_request(
                    size=self._get_avatar_size(request),
                    format_type=self._get_avatar_format(request),
                )

        finally:
            span.end()

        return response

    def _is_avatar_request(self, request: HttpRequest) -> bool:
        """Check if this is an avatar request."""
        return request.path.startswith("/avatar/") or request.path.startswith("/avatar")

    def _add_avatar_attributes(self, span: trace.Span, request: HttpRequest) -> None:
        """Add avatar-specific attributes to span."""
        try:
            # Extract avatar parameters
            size = self._get_avatar_size(request)
            format_type = self._get_avatar_format(request)
            email = self._get_avatar_email(request)

            span.set_attributes(
                {
                    "ivatar.avatar_size": size,
                    "ivatar.avatar_format": format_type,
                    "ivatar.avatar_email": email,
                }
            )

        except Exception as e:
            logger.debug(f"Failed to add avatar attributes: {e}")

    def _get_avatar_size(self, request: HttpRequest) -> str:
        """Extract avatar size from request."""
        size = request.GET.get("s", "80")
        return str(size)

    def _get_avatar_format(self, request: HttpRequest) -> str:
        """Extract avatar format from request."""
        format_type = request.GET.get("d", "png")
        return str(format_type)

    def _get_avatar_email(self, request: HttpRequest) -> str:
        """Extract email from avatar request path."""
        try:
            # Extract email from path like /avatar/user@example.com
            path_parts = request.path.strip("/").split("/")
            if len(path_parts) >= 2 and path_parts[0] == "avatar":
                return path_parts[1]
        except Exception:
            pass
        return "unknown"

    def _get_client_ip(self, request: HttpRequest) -> str:
        """Get client IP address from request."""
        x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
        if x_forwarded_for:
            return x_forwarded_for.split(",")[0].strip()
        return request.META.get("REMOTE_ADDR", "unknown")


def trace_avatar_operation(operation_name: str):
    """
    Decorator to trace avatar operations.

    Args:
        operation_name: Name of the operation being traced
    """

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if not is_enabled():
                return func(*args, **kwargs)

            tracer = get_tracer("ivatar.avatar")
            with tracer.start_as_current_span(f"avatar.{operation_name}") as span:
                try:
                    result = func(*args, **kwargs)
                    span.set_status(Status(StatusCode.OK))
                    return result
                except Exception as e:
                    span.set_status(Status(StatusCode.ERROR, str(e)))
                    span.set_attribute("error.message", str(e))
                    raise

        return wrapper

    return decorator


def trace_file_upload(operation_name: str):
    """
    Decorator to trace file upload operations.

    Args:
        operation_name: Name of the file upload operation being traced
    """

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            tracer = get_tracer("ivatar.file_upload")
            with tracer.start_as_current_span(f"file_upload.{operation_name}") as span:
                try:
                    # Add file information if available
                    if args and hasattr(args[0], "FILES"):
                        files = args[0].FILES
                        if files:
                            file_info = list(files.values())[0]
                            span.set_attributes(
                                {
                                    "file.name": file_info.name,
                                    "file.size": file_info.size,
                                    "file.content_type": file_info.content_type,
                                }
                            )

                    result = func(*args, **kwargs)
                    span.set_status(Status(StatusCode.OK))
                    return result
                except Exception as e:
                    span.set_status(Status(StatusCode.ERROR, str(e)))
                    span.set_attribute("error.message", str(e))
                    raise

        return wrapper

    return decorator


def trace_authentication(operation_name: str):
    """
    Decorator to trace authentication operations.

    Args:
        operation_name: Name of the authentication operation being traced
    """

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            tracer = get_tracer("ivatar.auth")
            with tracer.start_as_current_span(f"auth.{operation_name}") as span:
                try:
                    result = func(*args, **kwargs)
                    span.set_status(Status(StatusCode.OK))
                    return result
                except Exception as e:
                    span.set_status(Status(StatusCode.ERROR, str(e)))
                    span.set_attribute("error.message", str(e))
                    raise

        return wrapper

    return decorator


class AvatarMetrics:
    """
    Custom metrics for avatar operations.

    This class provides methods to record custom metrics for avatar-specific
    operations like generation, caching, and external service calls.
    """

    def __init__(self):
        self.meter = get_meter("ivatar.avatar")

        # Create custom metrics
        self.avatar_generated = self.meter.create_counter(
            name="ivatar_avatars_generated_total",
            description="Total number of avatars generated",
            unit="1",
        )

        self.avatar_requests = self.meter.create_counter(
            name="ivatar_avatar_requests_total",
            description="Total number of avatar image requests",
            unit="1",
        )

        self.avatar_cache_hits = self.meter.create_counter(
            name="ivatar_avatar_cache_hits_total",
            description="Total number of avatar cache hits",
            unit="1",
        )

        self.avatar_cache_misses = self.meter.create_counter(
            name="ivatar_avatar_cache_misses_total",
            description="Total number of avatar cache misses",
            unit="1",
        )

        self.external_avatar_requests = self.meter.create_counter(
            name="ivatar_external_avatar_requests_total",
            description="Total number of external avatar requests",
            unit="1",
        )

        self.file_uploads = self.meter.create_counter(
            name="ivatar_file_uploads_total",
            description="Total number of file uploads",
            unit="1",
        )

        self.file_upload_size = self.meter.create_histogram(
            name="ivatar_file_upload_size_bytes",
            description="File upload size in bytes",
            unit="bytes",
        )

    def record_avatar_request(self, size: str, format_type: str):
        """Record avatar request."""
        self.avatar_requests.add(
            1,
            {
                "size": size,
                "format": format_type,
            },
        )

    def record_avatar_generated(
        self, size: str, format_type: str, source: str = "generated"
    ):
        """Record avatar generation."""
        self.avatar_generated.add(
            1,
            {
                "size": size,
                "format": format_type,
                "source": source,
            },
        )

    def record_cache_hit(self, size: str, format_type: str):
        """Record cache hit."""
        self.avatar_cache_hits.add(
            1,
            {
                "size": size,
                "format": format_type,
            },
        )

    def record_cache_miss(self, size: str, format_type: str):
        """Record cache miss."""
        self.avatar_cache_misses.add(
            1,
            {
                "size": size,
                "format": format_type,
            },
        )

    def record_external_request(self, service: str, status_code: int):
        """Record external avatar service request."""
        self.external_avatar_requests.add(
            1,
            {
                "service": service,
                "status_code": str(status_code),
            },
        )

    def record_file_upload(self, file_size: int, content_type: str, success: bool):
        """Record file upload."""
        self.file_uploads.add(
            1,
            {
                "content_type": content_type,
                "success": str(success),
            },
        )

        self.file_upload_size.record(
            file_size,
            {
                "content_type": content_type,
                "success": str(success),
            },
        )


# Global metrics instance (lazy-loaded)
_avatar_metrics = None


def get_avatar_metrics():
    """Get the global avatar metrics instance."""
    global _avatar_metrics
    if _avatar_metrics is None:
        _avatar_metrics = AvatarMetrics()
    return _avatar_metrics


def reset_avatar_metrics():
    """Reset the global avatar metrics instance (for testing)."""
    global _avatar_metrics
    _avatar_metrics = None
