from django.core.exceptions import ObjectDoesNotExist
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.generics import RetrieveAPIView
from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser
from rest_framework.response import Response

from apps.station import models
from apps.station.filters.station_checkin_filters import StationCheckInListFilter
from apps.station.filters.station_filters import (
    StationRatingListFilter,
    StationRatingMediaListFilter,
)
from apps.station.serializers import station_checkIn_rating_serializers
from base.permissions import (
    AdminOrPostDeleteOnlyPermission,
)
from base.views import BaseModelViewSet
from station_ev import settings


class CreateStationRatingModelViewSet(BaseModelViewSet):
    """
    This viewset allows authenticated users to create station rating records.

    Attributes:
        permission_classes (tuple): Permissions required to access this view (IsAuthenticated for authenticated users).
        serializer_class (StationRatingSerializer): The serializer used for input and output data.
        queryset (QuerySet): The base queryset for station rating records.
        http_method_names (list): The allowed HTTP methods, in this case, only POST and GET are allowed.

    Methods:
        get_queryset(self): Retrieves a queryset of station rating records for the requesting user.
        perform_create(serializer): Associates a newly created station rating record with the requesting user.
    """

    serializer_class = station_checkIn_rating_serializers.StationRatingSerializer
    queryset = models.StationRating.objects.all()
    http_method_names = ["post", "get"]

    def get_queryset(self):
        """
        Retrieves a queryset of station rating records for the requesting user.

        Returns:
            QuerySet: A filtered queryset containing station rating records associated with the user.
        """
        return self.queryset.filter(user=self.request.user)

    def create(self, request, *args, **kwargs):
        """
        Create a new StationRating instance and associated StationRatingAssets instances.

        Args:
            request (Request): The HTTP request object.
            *args: Variable-length argument list.
            **kwargs: Arbitrary keyword arguments.

        Returns:
            Response: The HTTP response object.
        """
        if (len(request.FILES)) > 5:
            return Response(
                {"message": "You can upload up-to 5 images"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Create StationRating instance
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save(user=self.request.user)

        # Create StationRatingAssets instances
        assets = []
        for index, file in enumerate(request.FILES):
            assets.append(
                {
                    "asset": request.FILES.get(f"asset[{index}]"),
                    "asset_type": request.data.get(f"asset_type[{index}]"),
                }
            )

        assets_serializer = station_checkIn_rating_serializers.RatingMediaSerializer(
            data=assets, many=True, context={"request": request}
        )
        assets_serializer.is_valid(raise_exception=True)
        station_rating = models.StationRating.objects.get(pk=serializer.data["id"])
        assets_serializer.save(
            rating_station=station_rating, is_verified=settings.ALLOW_AUTO_VERIFY_MEDIA
        )

        # Serialize and return the created StationRating instance
        serializer = self.get_serializer(station_rating, context={"request": request})

        return Response(serializer.data, status=status.HTTP_201_CREATED)


class CreateRatingMediaModelViewSet(BaseModelViewSet):
    """
    This viewset allows authenticated users to upload media assets for a specific station rating.

    Attributes:
        permission_classes (tuple): Permissions required to access this view (IsAuthenticated for authenticated users).
        serializer_class (RatingMediaSerializer): The serializer used for handling media asset uploads.
        parser_classes (tuple): Parsers used for request data (MultiPartParser for file uploads).
        queryset (QuerySet): The base queryset for station rating media records.
        http_method_names (list): The allowed HTTP methods, in this case, only POST is allowed.

    Methods:
        create(request, *args, **kwargs): Handles the uploading of station media assets and
        associates them with a specific station rating.
    """

    permission_classes = (IsAuthenticated, AdminOrPostDeleteOnlyPermission)
    serializer_class = station_checkIn_rating_serializers.RatingMediaSerializer
    queryset = models.StationRatingMedia.objects.all()
    filterset_class = StationRatingMediaListFilter
    http_method_names = ["get", "post", "patch", "delete"]

    def create(self, request, *args, **kwargs):
        """
        Handles the uploading of station media assets and associates them with a specific station rating.

        Args:
            request (HttpRequest): The HTTP request object containing media assets and associated data.
            *args: Variable-length argument list.
            **kwargs: Arbitrary keyword arguments.

        Returns:
            Response: A response indicating the success or failure of the media asset upload operation.

        This method processes the uploaded media assets, associates them with a specified station rating,
         and handles the creation
        of station media records. It verifies the provided check-in ID, asset data, and the associated user.
        """

        rating_id = self.kwargs["rating_id"]

        try:
            checkin_obj = models.StationRating.objects.get(id=rating_id)
            if checkin_obj.user != self.request.user:
                return Response(
                    {
                        "message": "You do not have permission to upload other users' rating media."
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )
        except ObjectDoesNotExist:
            return Response(
                {"message": "Invalid rating ID"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        assets = []
        for index, file in enumerate(request.FILES):
            assets.append(
                {
                    "asset": request.FILES.get(f"asset[{index}]"),
                    "asset_type": request.data.get(f"asset_type[{index}]"),
                }
            )

        if not assets:
            return Response(
                {"message": "Assets are required"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        # Per user can upload only 5 images
        if not request.user.is_superuser:
            station_media = models.StationRatingMedia.objects.filter(
                rating_station__user=request.user, rating_station=checkin_obj
            )
            if (station_media.count() + len(assets)) > 5:
                return Response(
                    {"message": "You can upload up-to 5 images"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

        serializer = self.get_serializer(
            data=assets, many=True, context={"request": request}
        )

        if serializer.is_valid():
            serializer.save(
                rating_station=checkin_obj, is_verified=settings.ALLOW_AUTO_VERIFY_MEDIA
            )
            return Response(
                serializer.data,
                status=status.HTTP_201_CREATED,
            )
        else:
            return Response(
                serializer.errors,
                status=status.HTTP_400_BAD_REQUEST,
            )

    def destroy(self, request, *args, **kwargs):
        """
        Remove a media from the user's media.

        Args:
            request (HttpRequest): The HTTP request object.
            *args: Variable-length argument list.
            **kwargs: Arbitrary keyword arguments.

        Returns:
            Response: A response indicating the success or failure of the media removal operation.
        """

        rating_id = self.kwargs.get("rating_id")

        # Fetch the bookmarked station based on station_id and the requesting user
        try:
            rating_station_media = models.StationRatingMedia.objects.get(
                rating_station=rating_id,
                rating_station__user=request.user,
                id=self.kwargs.get("pk"),
            )
        except models.StationRatingMedia.DoesNotExist:
            return Response(
                {"message": "Media not found"},
                status=status.HTTP_404_NOT_FOUND,
            )

        self.perform_destroy(rating_station_media)

        return Response(
            {"message": "Media deleted successful"},
            status=status.HTTP_204_NO_CONTENT,
        )


class StationRatingModelViewSet(BaseModelViewSet):
    """
    This viewset allows public access to retrieve station rating records associated with a specific station.

    Attributes:
        permission_classes (tuple): Permissions required to access this view (AllowAny for public access).
        serializer_class (StationRatingSerializer): The serializer used for station rating records.
        queryset (QuerySet): The base queryset for station rating records.
        http_method_names (list): The allowed HTTP methods, in this case, only GET is allowed.

    Methods:
        get_queryset(): Retrieves station rating records for a specific station based on the station ID.
    """

    permission_classes = (AllowAny,)
    serializer_class = station_checkIn_rating_serializers.StationRatingSerializer
    queryset = models.StationRating.objects.all()
    filterset_class = StationRatingListFilter
    http_method_names = ["get"]

    def get_queryset(self):
        """
        Retrieves station rating records for a specific station based on the station ID.

        Returns:
            QuerySet: A filtered queryset containing station rating records associated with the specified station.

        This method filters the base queryset to retrieve station rating records
        associated with the station specified by the station ID.
        """
        station_id = self.kwargs["station_id"]
        return self.queryset.filter(station__id=station_id)


class StationCheckInCalculationAPIView(RetrieveAPIView):
    """
    A view for retrieving Station Check-In calculations.
    This view allows retrieval of StationMaster data with specific permissions.
    """

    permission_classes = (AllowAny,)
    serializer_class = (
        station_checkIn_rating_serializers.StationCheckInCalculationSerializer
    )
    queryset = models.StationMaster.objects.all()


class StationCheckInModelViewSet(BaseModelViewSet):
    """
    A viewset for handling Station Check-In operations.
    This viewset supports creating StationCheckIn objects via HTTP POST.
    """

    serializer_class = station_checkIn_rating_serializers.StationCheckInSerializer
    queryset = models.StationCheckIn.objects.all()
    filterset_class = StationCheckInListFilter
    http_method_names = ["post", "get"]

    def perform_create(self, serializer):
        """
        Perform the creation of a StationCheckIn object.
        This method is called when a new object is created.
        It associates the object with the current user.

        Args:
            serializer (Serializer): The serializer for creating the object.

        Returns:
            None
        """
        serializer.save(user=self.request.user)


class UnverifiedStationRatingMediaViewSet(BaseModelViewSet):
    """
    This viewset provides endpoints to manage unverified station rating media.

    Attributes:
        permission_classes (tuple): Specifies the permissions required to access this view.
        serializer_class: The serializer class used for handling station rating media data.
        queryset (QuerySet): The queryset for accessing unverified station rating media records.
        filterset_class: The filterset class used for filtering station rating media records.
        http_method_names (list): Specifies the supported HTTP methods for this view,
        including 'get', 'delete', and 'patch'.
    """

    permission_classes = (IsAuthenticated, IsAdminUser)
    serializer_class = station_checkIn_rating_serializers.RatingMediaSerializer
    queryset = models.StationRatingMedia.objects.all()
    filterset_class = StationRatingMediaListFilter
    http_method_names = ["get", "delete", "patch"]

    @action(detail=False, methods=["delete"], url_path="delete")
    def delete_selected_media(self, request, *args, **kwargs):
        """
        Delete selected unverified rating media.

        Args:
            request (HttpRequest): The HTTP request object.
            *args: Variable-length argument list.
            **kwargs: Arbitrary keyword arguments.

        Returns:
            Response: A response indicating the success or failure of the rating media deletion operation.
        """
        media_ids = request.data.get("media_ids", [])
        try:
            # Delete selected media
            deleted_count, _ = models.StationRatingMedia.objects.filter(
                id__in=media_ids
            ).delete()
            return Response(
                {"message": f"{deleted_count} media deleted successfully"},
                status=status.HTTP_204_NO_CONTENT,
            )
        except Exception as e:
            return Response(
                {"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=False, methods=["patch"], url_path="verified")
    def verify_selected_media(self, request, *args, **kwargs):
        """
        Verify selected unverified rating media.

        Args:
            request (HttpRequest): The HTTP request object.
            *args: Variable-length argument list.
            **kwargs: Arbitrary keyword arguments.

        Returns:
            Response: A response indicating the success or failure of the rating media verification operation.
        """
        media_ids = request.data.get("media_ids", [])
        try:
            # Update is_verified for selected media
            updated_count = models.StationRatingMedia.objects.filter(
                id__in=media_ids
            ).update(is_verified=True)
            return Response(
                {"message": f"{updated_count} media verified successfully"},
                status=status.HTTP_200_OK,
            )
        except Exception as e:
            return Response(
                {"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )
