"""
Common mixins for API views following Django styleguide patterns.
"""

from typing import Any

from rest_framework import status
from rest_framework.request import Request
from rest_framework.response import Response

# Constants for error messages
_MISSING_INPUT_SERIALIZER_MSG = "Subclasses must set input_serializer class attribute"
_MISSING_OUTPUT_SERIALIZER_MSG = "Subclasses must set output_serializer class attribute"
_MISSING_GET_OBJECT_MSG = "Subclasses must implement get_object using selectors"


class ApiMixin:
    """
    Base mixin for API views providing standardized response formatting.
    """

    # Expose expected attributes so static type checkers know they exist on subclasses.
    # Subclasses or other bases (e.g. DRF GenericAPIView) will actually provide these.
    input_serializer: type[Any] | None = None
    output_serializer: type[Any] | None = None

    def create_response(
        self,
        *,
        data: Any = None,
        message: str | None = None,
        status_code: int = status.HTTP_200_OK,
        pagination: dict[str, Any] | None = None,
        metadata: dict[str, Any] | None = None,
    ) -> Response:
        """
        Create standardized API response.

        Args:
            data: Response data
            message: Optional success message
            status_code: HTTP status code
            pagination: Pagination information
            metadata: Additional metadata

        Returns:
            Standardized Response object
        """
        response_data = {
            "status": "success" if status_code < 400 else "error",
            "data": data,
        }

        if message:
            response_data["message"] = message

        if pagination:
            response_data["pagination"] = pagination

        if metadata:
            response_data["metadata"] = metadata

        return Response(response_data, status=status_code)

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

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

        Returns:
            Standardized error Response object
        """
        # explicitly allow any-shaped values in the error_data dict
        error_data: dict[str, Any] = {
            "code": error_code or "GENERIC_ERROR",
            "message": message,
        }

        if details:
            error_data["details"] = details

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

        return Response(response_data, status=status_code)

    # Provide lightweight stubs for methods commonly supplied by other bases (DRF GenericAPIView, etc.)
    # These will raise if not implemented; they also inform static analyzers about their existence.
    def paginate_queryset(self, queryset):
        """Override / implement in subclass or provided base if pagination is needed."""
        raise NotImplementedError("Subclasses must implement paginate_queryset to enable pagination")

    def get_paginated_response(self, data):
        """Override / implement in subclass or provided base to return paginated responses."""
        raise NotImplementedError("Subclasses must implement get_paginated_response to enable pagination")

    def get_object(self):
        """Default placeholder; subclasses should implement this."""
        raise NotImplementedError(_MISSING_GET_OBJECT_MSG)


class CreateApiMixin(ApiMixin):
    """
    Mixin for create API endpoints with standardized input/output handling.
    """

    def create(self, _request: Request, *_args, **_kwargs) -> Response:
        """Handle POST requests for creating resources."""
        serializer = self.get_input_serializer(data=_request.data)
        serializer.is_valid(raise_exception=True)

        # Create the object using the service layer
        obj = self.perform_create(**serializer.validated_data)

        # Serialize the output
        output_serializer = self.get_output_serializer(obj)

        return self.create_response(
            data=output_serializer.data,
            status_code=status.HTTP_201_CREATED,
            message="Resource created successfully",
        )

    def perform_create(self, **validated_data):
        """
        Override this method to implement object creation logic.
        Should use service layer methods.
        """
        raise NotImplementedError("Subclasses must implement perform_create")

    def get_input_serializer(self, *args, **kwargs):
        """Get the input serializer for validation."""
        if self.input_serializer is None:
            raise NotImplementedError(_MISSING_INPUT_SERIALIZER_MSG)
        return self.input_serializer(*args, **kwargs)

    def get_output_serializer(self, *args, **kwargs):
        """Get the output serializer for response."""
        if self.output_serializer is None:
            raise NotImplementedError(_MISSING_OUTPUT_SERIALIZER_MSG)
        return self.output_serializer(*args, **kwargs)


class UpdateApiMixin(ApiMixin):
    """
    Mixin for update API endpoints with standardized input/output handling.
    """

    def update(self, _request: Request, *_args, **_kwargs) -> Response:
        """Handle PUT/PATCH requests for updating resources."""
        instance = self.get_object()
        serializer = self.get_input_serializer(data=_request.data, partial=_kwargs.get("partial", False))
        serializer.is_valid(raise_exception=True)

        # Update the object using the service layer
        updated_obj = self.perform_update(instance, **serializer.validated_data)

        # Serialize the output
        output_serializer = self.get_output_serializer(updated_obj)

        return self.create_response(
            data=output_serializer.data,
            message="Resource updated successfully",
        )

    def perform_update(self, instance, **validated_data):
        """
        Override this method to implement object update logic.
        Should use service layer methods.
        """
        raise NotImplementedError("Subclasses must implement perform_update")

    def get_input_serializer(self, *args, **kwargs):
        """Get the input serializer for validation."""
        if self.input_serializer is None:
            raise NotImplementedError(_MISSING_INPUT_SERIALIZER_MSG)
        return self.input_serializer(*args, **kwargs)

    def get_output_serializer(self, *args, **kwargs):
        """Get the output serializer for response."""
        if self.output_serializer is None:
            raise NotImplementedError(_MISSING_OUTPUT_SERIALIZER_MSG)
        return self.output_serializer(*args, **kwargs)


class ListApiMixin(ApiMixin):
    """
    Mixin for list API endpoints with pagination and filtering.
    """

    def list(self, _request: Request, *_args, **_kwargs) -> Response:
        """Handle GET requests for listing resources."""
        # Use selector to get filtered queryset
        queryset = self.get_queryset()

        # Apply pagination
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_output_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        # No pagination
        serializer = self.get_output_serializer(queryset, many=True)
        return self.create_response(data=serializer.data)

    def get_queryset(self):
        """
        Override this method to use selector patterns.
        Should call selector functions, not access model managers directly.
        """
        raise NotImplementedError("Subclasses must implement get_queryset using selectors")

    def get_output_serializer(self, *args, **kwargs):
        """Get the output serializer for response."""
        if self.output_serializer is None:
            raise NotImplementedError(_MISSING_OUTPUT_SERIALIZER_MSG)
        return self.output_serializer(*args, **kwargs)


class RetrieveApiMixin(ApiMixin):
    """
    Mixin for retrieve API endpoints.
    """

    def retrieve(self, _request: Request, *_args, **_kwargs) -> Response:
        """Handle GET requests for retrieving a single resource."""
        instance = self.get_object()
        serializer = self.get_output_serializer(instance)

        return self.create_response(data=serializer.data)

    def get_object(self):
        """
        Override this method to use selector patterns.
        Should call selector functions for optimized queries.
        """
        raise NotImplementedError(_MISSING_GET_OBJECT_MSG)

    def get_output_serializer(self, *args, **kwargs):
        """Get the output serializer for response."""
        if self.output_serializer is None:
            raise NotImplementedError(_MISSING_OUTPUT_SERIALIZER_MSG)
        return self.output_serializer(*args, **kwargs)


class DestroyApiMixin(ApiMixin):
    """
    Mixin for delete API endpoints.
    """

    def destroy(self, _request: Request, *_args, **_kwargs) -> Response:
        """Handle DELETE requests for destroying resources."""
        instance = self.get_object()

        # Delete using service layer
        self.perform_destroy(instance)

        return self.create_response(
            status_code=status.HTTP_204_NO_CONTENT,
            message="Resource deleted successfully",
        )

    def perform_destroy(self, instance):
        """
        Override this method to implement object deletion logic.
        Should use service layer methods.
        """
        raise NotImplementedError("Subclasses must implement perform_destroy")

    def get_object(self):
        """
        Override this method to use selector patterns.
        Should call selector functions for optimized queries.
        """
        raise NotImplementedError(_MISSING_GET_OBJECT_MSG)
