"""
Base Views for Contract-Compliant API Responses

This module provides base view classes that ensure all API responses follow
consistent formats that match frontend TypeScript interfaces exactly.
"""

import logging
from typing import Any

from django.conf import settings
from rest_framework import status
from rest_framework.response import Response
from rest_framework.serializers import Serializer
from rest_framework.views import APIView

from apps.api.v1.serializers.shared import validate_filter_metadata_contract
from apps.core.utils import capture_and_log

logger = logging.getLogger(__name__)


class ContractCompliantAPIView(APIView):
    """
    Base API view that ensures all responses are contract-compliant.

    This view provides:
    - Standardized success response format
    - Consistent error response format
    - Automatic contract validation in DEBUG mode
    - Proper error logging with context
    """

    # Override in subclasses to specify response serializer
    response_serializer_class: type[Serializer] | None = None

    def dispatch(self, request, *args, **kwargs):
        """Override dispatch to add contract validation."""
        try:
            response = super().dispatch(request, *args, **kwargs)

            # Validate contract in DEBUG mode
            if settings.DEBUG and hasattr(response, "data"):
                self._validate_response_contract(response.data)

            return response

        except Exception as e:
            # Capture error to dashboard
            capture_and_log(
                e,
                f'API error in {self.__class__.__name__}',
                source='api',
                severity='high',
            )

            # Return standardized error response
            return self.error_response(
                message="An internal error occurred", status_code=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    def success_response(
        self,
        data: Any = None,
        message: str = None,
        status_code: int = status.HTTP_200_OK,
        headers: dict[str, str] = None,
    ) -> Response:
        """
        Create a standardized success response.

        Args:
            data: Response data
            message: Optional success message
            status_code: HTTP status code
            headers: Optional response headers

        Returns:
            Response with standardized format
        """
        response_data = {"success": True}

        if data is not None:
            response_data["data"] = data

        if message:
            response_data["message"] = message

        return Response(response_data, status=status_code, headers=headers)

    def error_response(
        self,
        message: str,
        status_code: int = status.HTTP_400_BAD_REQUEST,
        error_code: str = None,
        details: Any = None,
        headers: dict[str, str] = None,
    ) -> Response:
        """
        Create a standardized error response.

        Args:
            message: Error message
            status_code: HTTP status code
            error_code: Optional error code
            details: Optional error details
            headers: Optional response headers

        Returns:
            Response with standardized error format
        """
        error_data = {"code": error_code or "API_ERROR", "message": message}

        if details:
            error_data["details"] = details

        # Add user context if available
        if hasattr(self, "request") and hasattr(self.request, "user"):
            user = self.request.user
            if user and user.is_authenticated:
                error_data["request_user"] = user.username

        response_data = {"status": "error", "error": error_data, "data": None}

        return Response(response_data, status=status_code, headers=headers)

    def validation_error_response(self, errors: dict[str, Any], message: str = "Validation failed") -> Response:
        """
        Create a standardized validation error response.

        Args:
            errors: Validation errors dictionary
            message: Error message

        Returns:
            Response with validation errors
        """
        return Response({"success": False, "message": message, "errors": errors}, status=status.HTTP_400_BAD_REQUEST)

    def _validate_response_contract(self, data: Any) -> None:
        """
        Validate response data against expected contracts.

        This method is called automatically in DEBUG mode to catch
        contract violations during development.
        """
        try:
            # Check if this looks like filter metadata
            if isinstance(data, dict) and "categorical" in data and "ranges" in data:
                validate_filter_metadata_contract(data)

            # Add more contract validations as needed

        except Exception as e:
            logger.warning(
                f"Contract validation failed in {self.__class__.__name__}: {str(e)}",
                extra={
                    "view_class": self.__class__.__name__,
                    "validation_error": str(e),
                    "response_data_type": type(data).__name__,
                },
            )


class FilterMetadataAPIView(ContractCompliantAPIView):
    """
    Base view for filter metadata endpoints.

    This view ensures filter metadata responses always follow the correct
    contract that matches frontend TypeScript interfaces.
    """

    def get_filter_metadata(self) -> dict[str, Any]:
        """
        Override this method in subclasses to provide filter metadata.

        Returns:
            Filter metadata dictionary
        """
        raise NotImplementedError("Subclasses must implement get_filter_metadata()")

    def get(self, request, *args, **kwargs):
        """Handle GET requests for filter metadata."""
        try:
            metadata = self.get_filter_metadata()

            # Validate the metadata contract
            validated_metadata = validate_filter_metadata_contract(metadata)

            return self.success_response(validated_metadata)

        except Exception as e:
            capture_and_log(
                e,
                f'Get filter metadata in {self.__class__.__name__}',
                source='api',
            )

            return self.error_response(message="Failed to retrieve filter metadata", error_code="FILTER_METADATA_ERROR")


class HybridFilteringAPIView(ContractCompliantAPIView):
    """
    Base view for hybrid filtering endpoints.

    This view provides common functionality for hybrid filtering responses
    and ensures they follow the correct contract.
    """

    def get_hybrid_data(self, filters: dict[str, Any] = None) -> dict[str, Any]:
        """
        Override this method in subclasses to provide hybrid data.

        Args:
            filters: Filter parameters

        Returns:
            Hybrid response dictionary
        """
        raise NotImplementedError("Subclasses must implement get_hybrid_data()")

    def get(self, request, *args, **kwargs):
        """Handle GET requests for hybrid filtering."""
        try:
            # Extract filters from request parameters
            filters = self.extract_filters(request)

            # Get hybrid data
            hybrid_data = self.get_hybrid_data(filters)

            # Validate hybrid response structure
            self._validate_hybrid_response(hybrid_data)

            return self.success_response(hybrid_data)

        except Exception as e:
            capture_and_log(
                e,
                f'Hybrid filtering for {self.__class__.__name__}',
                source='api',
            )

            return self.error_response(message="Failed to retrieve filtered data", error_code="HYBRID_FILTERING_ERROR")

    def extract_filters(self, request) -> dict[str, Any]:
        """
        Extract filter parameters from request.

        Override this method in subclasses to customize filter extraction.

        Args:
            request: HTTP request object

        Returns:
            Dictionary of filter parameters
        """
        # Basic implementation - extract all query parameters
        filters = {}
        for key, value in request.query_params.items():
            if value:  # Only include non-empty values
                filters[key] = value

        # Store for error logging
        self._extracted_filters = filters

        return filters

    def _validate_hybrid_response(self, data: dict[str, Any]) -> None:
        """Validate hybrid response structure."""
        required_fields = ["strategy", "total_count"]

        for field in required_fields:
            if field not in data:
                raise ValueError(f"Hybrid response missing required field: {field}")

        # Validate strategy value
        if data["strategy"] not in ["client_side", "server_side"]:
            raise ValueError(f"Invalid strategy value: {data['strategy']}")

        # Validate filter metadata if present
        if "filter_metadata" in data:
            validate_filter_metadata_contract(data["filter_metadata"])


class PaginatedAPIView(ContractCompliantAPIView):
    """
    Base view for paginated responses.

    This view ensures paginated responses follow the correct contract
    with consistent pagination metadata.
    """

    default_page_size = 20
    max_page_size = 100

    def get_paginated_response(
        self, queryset, serializer_class: type[Serializer], request, page_size: int = None
    ) -> Response:
        """
        Create a paginated response.

        Args:
            queryset: Django queryset to paginate
            serializer_class: Serializer class for items
            request: HTTP request object
            page_size: Optional page size override

        Returns:
            Paginated response
        """
        from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator

        # Determine page size
        if page_size is None:
            page_size = min(int(request.query_params.get("page_size", self.default_page_size)), self.max_page_size)

        # Get page number
        page_number = request.query_params.get("page", 1)

        try:
            page_number = int(page_number)
        except (ValueError, TypeError):
            page_number = 1

        # Create paginator
        paginator = Paginator(queryset, page_size)

        try:
            page = paginator.page(page_number)
        except PageNotAnInteger:
            page = paginator.page(1)
        except EmptyPage:
            page = paginator.page(paginator.num_pages)

        # Serialize data
        serializer = serializer_class(page.object_list, many=True)

        # Build pagination URLs
        request_url = request.build_absolute_uri().split("?")[0]
        query_params = request.query_params.copy()

        next_url = None
        if page.has_next():
            query_params["page"] = page.next_page_number()
            next_url = f"{request_url}?{query_params.urlencode()}"

        previous_url = None
        if page.has_previous():
            query_params["page"] = page.previous_page_number()
            previous_url = f"{request_url}?{query_params.urlencode()}"

        # Create response data
        response_data = {
            "count": paginator.count,
            "next": next_url,
            "previous": previous_url,
            "results": serializer.data,
            "page_size": page_size,
            "current_page": page.number,
            "total_pages": paginator.num_pages,
        }

        return self.success_response(response_data)


def contract_compliant_view(view_class):
    """
    Decorator to make any view contract-compliant.

    This decorator can be applied to existing views to add contract
    validation without changing the base class.
    """
    original_dispatch = view_class.dispatch

    def new_dispatch(self, request, *args, **kwargs):
        try:
            response = original_dispatch(self, request, *args, **kwargs)

            # Add contract validation in DEBUG mode
            if settings.DEBUG and hasattr(response, "data"):
                # Basic validation - can be extended
                pass

            return response

        except Exception as e:
            capture_and_log(e, f'Decorated view {view_class.__name__}', source='api')

            # Return basic error response
            return Response(
                {
                    "status": "error",
                    "error": {"code": "API_ERROR", "detail": "An internal error occurred"},
                    "data": None,
                },
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

    view_class.dispatch = new_dispatch
    return view_class
