from django.db.models import Q
from drf_spectacular.types import OpenApiTypes
from rest_framework.generics import ListAPIView

from fellchensammlung.api.serializers import LocationSerializer, AdoptionNoticeGeoJSONSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from django.db import transaction
from fellchensammlung.models import Log, TrustLevel, Location, AdoptionNoticeStatusChoices
from fellchensammlung.tasks import post_adoption_notice_save, post_rescue_org_save
from rest_framework import status, serializers
from rest_framework.permissions import IsAuthenticated

from .renderers import GeoJSONRenderer
from .serializers import (
    AnimalGetSerializer,
    AnimalCreateSerializer,
    RescueOrgeGeoJSONSerializer,
    AdoptionNoticeSerializer,
    ImageCreateSerializer,
    SpeciesSerializer, RescueOrganizationSerializer,
)
from fellchensammlung.models import Animal, RescueOrganization, AdoptionNotice, Species, Image
from drf_spectacular.utils import extend_schema, inline_serializer, OpenApiParameter


class AdoptionNoticeApiView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        parameters=[
            {
                'name': 'id',
                'required': False,
                'description': 'ID of the adoption notice to retrieve.',
                'type': int
            },
        ],
        responses={200: AdoptionNoticeSerializer(many=True)}
    )
    def get(self, request, *args, **kwargs):
        """
        Retrieve adoption notices with their related animals and images.
        """
        adoption_notice_id = kwargs.get("id")
        if adoption_notice_id:
            try:
                adoption_notice = AdoptionNotice.objects.get(pk=adoption_notice_id)
                serializer = AdoptionNoticeSerializer(adoption_notice, context={"request": request})
                return Response(serializer.data, status=status.HTTP_200_OK)
            except AdoptionNotice.DoesNotExist:
                return Response({"error": "Adoption notice not found."}, status=status.HTTP_404_NOT_FOUND)
        adoption_notices = AdoptionNotice.objects.all()
        serializer = AdoptionNoticeSerializer(adoption_notices, many=True, context={"request": request})
        return Response(serializer.data, status=status.HTTP_200_OK)

    @transaction.atomic
    @extend_schema(
        request=AdoptionNoticeSerializer,
        responses={201: 'Adoption notice created successfully!'}
    )
    def post(self, request, *args, **kwargs):
        """
        API view to add an adoption notice.
        """
        serializer = AdoptionNoticeSerializer(data=request.data, context={'request': request})
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        adoption_notice = serializer.save(owner=request.user_to_notify)

        # Add the location
        post_adoption_notice_save.delay_on_commit(adoption_notice.pk)

        # Only set active when user has trust level moderator or higher
        if request.user_to_notify.trust_level >= TrustLevel.MODERATOR:
            adoption_notice.adoption_notice_status = AdoptionNoticeStatusChoices.Active.SEARCHING
        else:
            adoption_notice.adoption_notice_status = AdoptionNoticeStatusChoices.AwaitingAction.WAITING_FOR_REVIEW

        # Log the action
        Log.objects.create(
            user=request.user_to_notify,
            action="add_adoption_notice",
            text=f"{request.user_to_notify} added adoption notice {adoption_notice.pk} via API",
        )

        # Return success response with new adoption notice details
        return Response(
            {"message": "Adoption notice created successfully!", "id": adoption_notice.pk},
            status=status.HTTP_201_CREATED,
        )


class AnimalApiView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        responses=AnimalGetSerializer
    )
    def get(self, request, *args, **kwargs):
        """
        Get list of animals or a specific animal by ID.
        """
        animal_id = kwargs.get("id")
        if animal_id:
            try:
                animal = Animal.objects.get(pk=animal_id)
                serializer = AnimalGetSerializer(animal, context={"request": request})
                return Response(serializer.data, status=status.HTTP_200_OK)
            except Animal.DoesNotExist:
                return Response({"error": "Animal not found."}, status=status.HTTP_404_NOT_FOUND)
        animals = Animal.objects.all()
        serializer = AnimalGetSerializer(animals, many=True, context={"request": request})
        return Response(serializer.data, status=status.HTTP_200_OK)

    @transaction.atomic
    @extend_schema(
        request=AnimalCreateSerializer,
        responses={201: inline_serializer(
            name='Animal',
            fields={
                'id': serializers.IntegerField(),
                "message": serializers.Field()}),
            400: "json"}
    )
    @transaction.atomic
    def post(self, request, *args, **kwargs):
        """
        Create a new animal.
        """
        serializer = AnimalCreateSerializer(data=request.data, context={"request": request})
        if serializer.is_valid():
            animal = serializer.save(owner=request.user_to_notify)
            return Response(
                {"message": "Animal created successfully!", "id": animal.id},
                status=status.HTTP_201_CREATED,
            )
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class RescueOrganizationApiView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        parameters=[
            {
                'name': 'id',
                'required': False,
                'description': 'ID of the rescue organization to retrieve.',
                'type': int
            },
            {
                'name': 'trusted',
                'required': False,
                'description': 'Filter by trusted status (true/false).',
                'type': bool
            },
            {
                'name': 'external_object_identifier',
                'required': False,
                'description': 'Filter by external object identifier. Use "None" to filter for an empty field',
                'type': str
            },
            {
                'name': 'external_source_identifier',
                'required': False,
                'description': 'Filter by external source identifier. Use "None" to filter for an empty field',
                'type': str
            },
            {
                'name': 'search',
                'required': False,
                'description': 'Search by organization name or location name/city.',
                'type': str
            },
        ],
        responses={200: RescueOrganizationSerializer(many=True)}
    )
    def get(self, request, *args, **kwargs):
        """
        Get list of rescue organizations or a specific organization by ID or get a list with available filters for
        - external_object_identifier
        - external_source_identifier
        """
        org_id = request.query_params.get("id")
        external_object_identifier = request.query_params.get("external_object_identifier")
        external_source_identifier = request.query_params.get("external_source_identifier")
        search_query = request.query_params.get("search")

        if org_id:
            try:
                organization = RescueOrganization.objects.get(pk=org_id)
                serializer = RescueOrganizationSerializer(organization, context={"request": request})
                return Response(serializer.data, status=status.HTTP_200_OK)
            except RescueOrganization.DoesNotExist:
                return Response({"error": "Organization not found."}, status=status.HTTP_404_NOT_FOUND)

        organizations = RescueOrganization.objects.all()

        if external_object_identifier:
            if external_object_identifier == "None":
                external_object_identifier = None
            organizations = organizations.filter(external_object_identifier=external_object_identifier)

        if external_source_identifier:
            if external_source_identifier == "None":
                external_source_identifier = None
            organizations = organizations.filter(external_source_identifier=external_source_identifier)
        if search_query:
            organizations = organizations.filter(
                Q(name__icontains=search_query) |
                Q(location_string__icontains=search_query) |
                Q(location__name__icontains=search_query) |
                Q(location__city__icontains=search_query)
            )
        if organizations.count() == 0:
            return Response({"error": "No organizations found."}, status=status.HTTP_404_NOT_FOUND)

        serializer = RescueOrganizationSerializer(organizations, many=True, context={"request": request})
        return Response(serializer.data, status=status.HTTP_200_OK)

    @transaction.atomic
    @extend_schema(
        request=RescueOrganizationSerializer,
        responses={201: 'Rescue organization created successfully!'}
    )
    def post(self, request, *args, **kwargs):
        """
        Create or update a rescue organization.
        """
        serializer = RescueOrganizationSerializer(data=request.data, context={"request": request})
        if serializer.is_valid():
            rescue_org = serializer.save()
            if rescue_org.location is None:
                # Add the location
                post_rescue_org_save.delay_on_commit(rescue_org.pk)
            return Response(
                {"message": "Rescue organization created successfully!", "id": rescue_org.id},
                status=status.HTTP_201_CREATED,
            )

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @transaction.atomic
    @extend_schema(
        request=RescueOrganizationSerializer,
        responses={200: 'Rescue organization updated successfully!'}
    )
    def patch(self, request, *args, **kwargs):
        """
        Partially update a rescue organization.
        """
        org_id = request.data.get("id")
        if not org_id:
            return Response({"error": "ID is required for updating."}, status=status.HTTP_400_BAD_REQUEST)

        try:
            organization = RescueOrganization.objects.get(pk=org_id)
        except RescueOrganization.DoesNotExist:
            return Response({"error": "Organization not found."}, status=status.HTTP_404_NOT_FOUND)

        serializer = RescueOrganizationSerializer(organization, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response({"message": "Rescue organization updated successfully!"}, status=status.HTTP_200_OK)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class AddImageApiView(APIView):
    permission_classes = [IsAuthenticated]

    @transaction.atomic
    @extend_schema(
        request=ImageCreateSerializer,
        responses={201: 'Image added successfully!'}
    )
    def post(self, request, *args, **kwargs):
        """
        Add an image to an animal or adoption notice.
        """
        serializer = ImageCreateSerializer(data=request.data, context={"request": request})
        if serializer.is_valid():
            if serializer.validated_data["attach_to_type"] == "animal":
                object_to_attach_to = Animal.objects.get(id=serializer.validated_data["attach_to"])
            elif serializer.validated_data["attach_to_type"] == "adoption_notice":
                object_to_attach_to = AdoptionNotice.objects.get(id=serializer.validated_data["attach_to"])
            else:
                raise ValueError("Unknown attach_to_type given, should not happen. Check serializer")
            serializer.validated_data.pop('attach_to_type', None)
            serializer.validated_data.pop('attach_to', None)
            image = serializer.save(owner=request.user_to_notify)
            object_to_attach_to.photos.add(image)
            return Response(
                {"message": "Image added successfully!", "id": image.id},
                status=status.HTTP_201_CREATED,
            )
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class SpeciesApiView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        responses={200: SpeciesSerializer(many=True)}
    )
    def get(self, request, *args, **kwargs):
        """
        Retrieve a list of species.
        """
        species = Species.objects.all()
        serializer = SpeciesSerializer(species, many=True, context={"request": request})
        return Response(serializer.data, status=status.HTTP_200_OK)


class LocationApiView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        parameters=[
            {
                'name': 'id',
                'required': False,
                'description': 'ID of the location to retrieve.',
                'type': int
            },
        ],
        responses={200: LocationSerializer(many=True)}
    )
    def get(self, request, *args, **kwargs):
        """
        Retrieve a location
        """
        location_id = kwargs.get("id")
        if location_id:
            try:
                location = Location.objects.get(pk=location_id)
                serializer = LocationSerializer(location, context={"request": request})
                return Response(serializer.data, status=status.HTTP_200_OK)
            except Location.DoesNotExist:
                return Response({"error": "Location not found."}, status=status.HTTP_404_NOT_FOUND)
        locations = Location.objects.all()
        serializer = LocationSerializer(locations, many=True, context={"request": request})
        return Response(serializer.data, status=status.HTTP_200_OK)

    @transaction.atomic
    @extend_schema(
        request=LocationSerializer,
        responses={201: 'Location created successfully!'}
    )
    def post(self, request, *args, **kwargs):
        """
        API view to add a location
        """
        serializer = LocationSerializer(data=request.data, context={'request': request})
        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        location = serializer.save()

        # Log the action
        Log.objects.create(
            user=request.user,
            action="add_location",
            text=f"{request.user} added adoption notice {location.pk} via API",
        )

        # Return success response with new adoption notice details
        return Response(
            {"message": "Location created successfully!", "id": location.pk},
            status=status.HTTP_201_CREATED,
        )


class AdoptionNoticeGeoJSONView(ListAPIView):
    queryset = AdoptionNotice.objects.select_related('location').filter(location__isnull=False).filter(
        adoption_notice_status__in=AdoptionNoticeStatusChoices.Active.values)
    serializer_class = AdoptionNoticeGeoJSONSerializer
    renderer_classes = [GeoJSONRenderer]


class RescueOrgGeoJSONView(ListAPIView):
    queryset = RescueOrganization.objects.select_related('location').filter(location__isnull=False)
    serializer_class = RescueOrgeGeoJSONSerializer
    renderer_classes = [GeoJSONRenderer]


class AdoptionNoticePerOrgApiView(APIView):
    permission_classes = [IsAuthenticated]

    @extend_schema(
        parameters=[
            OpenApiParameter(
                name='id',
                required=False,
                description='ID of the rescue organization from which to retrieve adoption notices.',
                type=OpenApiTypes.INT
            ),
            OpenApiParameter(
                name='in_hierarchy',
                type=OpenApiTypes.BOOL,
                required=False,
                description='Show all Adoption Notices in hierarchy.',
            ),
            OpenApiParameter(
                name='status',
                type=OpenApiTypes.STR,
                required=False,
                description='Show all Adoption Notices in a certain status. Comma separated list of values e.g. '
                            '"active,closed"',
            ),
        ],
        responses={200: AdoptionNoticeSerializer(many=True)}
    )
    def get(self, request, *args, **kwargs):
        """
        Retrieve adoption notices with their related animals and images.
        """
        org_id = kwargs.get("id")
        in_hierarchy = request.query_params.get("in_hierarchy")
        an_status = request.query_params.get("status")
        try:
            org = RescueOrganization.objects.get(id=org_id)
        except RescueOrganization.DoesNotExist:
            return Response({"error": "Rescue Organization notice not found."}, status=status.HTTP_404_NOT_FOUND)
        if in_hierarchy:
            adoption_notices = org.adoption_notices_in_hierarchy
        else:
            adoption_notices = AdoptionNotice.objects.filter(organization=org)
        if an_status:
            status_list = an_status.lower().strip().split(",")
            temporary_an_storage = []
            if "active" in status_list:
                active_ans = [adoption_notice for adoption_notice in adoption_notices if
                              adoption_notice.adoption_notice_status in AdoptionNoticeStatusChoices.Active.values]
                temporary_an_storage.extend(active_ans)
            if "closed" in status_list:
                closed_ans = [adoption_notice for adoption_notice in adoption_notices if
                              adoption_notice.adoption_notice_status in AdoptionNoticeStatusChoices.Closed.values]
                temporary_an_storage.extend(closed_ans)
            if "disabled" in status_list:
                disabled_ans = [adoption_notice for adoption_notice in adoption_notices if
                                adoption_notice.adoption_notice_status in AdoptionNoticeStatusChoices.Disabled.values]
                temporary_an_storage.extend(disabled_ans)
            if "awaiting_action" in status_list:
                awaiting_action_ans = [adoption_notice for adoption_notice in adoption_notices if
                                       adoption_notice.adoption_notice_status in AdoptionNoticeStatusChoices.AwaitingAction.values]
                temporary_an_storage.extend(awaiting_action_ans)
            adoption_notices = temporary_an_storage
        serializer = AdoptionNoticeSerializer(adoption_notices, many=True, context={"request": request})
        return Response(serializer.data, status=status.HTTP_200_OK)
