from api.models import Course, File, Professor, Semester, Topic, PeerUser, UserReport
from api.serializers import (
    CourseSerializer,
    FileSerializer,
    ProfessorSerializer,
    SemesterSerializer,
    TopicSerializer,
    UserSerializer,
)
from datetime import timedelta
from django.contrib.auth import authenticate
from django.db import transaction
from django.db.models import Count, Value, F, IntegerField
from django.utils import timezone
from rest_framework import generics, status
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import filters
from rest_framework.exceptions import ValidationError, NotFound
from api.utils.get_client_ip import update_user_ip


class TopicListCreateAPIView(generics.ListCreateAPIView):
    queryset = Topic.objects.all()
    serializer_class = TopicSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]
    filter_backends = [filters.SearchFilter]
    search_fields = ["name"]


class TopicDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Topic.objects.all()
    serializer_class = TopicSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]


class ProfessorListCreateAPIView(generics.ListCreateAPIView):
    queryset = Professor.objects.all()
    serializer_class = ProfessorSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]
    filter_backends = [filters.SearchFilter]
    search_fields = ["name"]


class ProfessorDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Professor.objects.all()
    serializer_class = ProfessorSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]


class SemesterListCreateAPIView(generics.ListCreateAPIView):
    queryset = Semester.objects.all()
    serializer_class = SemesterSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]
    filter_backends = [filters.SearchFilter]
    search_fields = ["name"]


class SemesterDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Semester.objects.all()
    serializer_class = SemesterSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

    def delete(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)


class CourseListCreateAPIView(generics.ListCreateAPIView):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]
    filter_backends = [filters.SearchFilter]
    search_fields = ["name"]


class CourseDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]


class FileListCreateAPIView(generics.ListCreateAPIView):
    queryset = File.objects.all()
    serializer_class = FileSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]
    filter_backends = [filters.SearchFilter]
    search_fields = ["name"]


class FileDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
    queryset = File.objects.all()
    serializer_class = FileSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]


class UpvoteFile(APIView):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request, file_id):
        user = request.user
        # update_user_ip(request)

        try:
            file = File.objects.get(id=file_id)
            if file.downvotes.contains(user):
                file.downvotes.remove(user)
            file.upvotes.add(user)
            file.save()
            return Response(
                {"msg": f"Upvoted {file.filename}", "file_points": file.points},
                status=status.HTTP_200_OK,
            )
        except Exception as e:
            print(f"Error: {str(e)}")
            return Response(
                {"error": "Please enter valid file id"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class DownvoteFile(APIView):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request, file_id):
        user = request.user
        # update_user_ip(request)

        try:
            file = File.objects.get(id=file_id)
            if file.upvotes.contains(user):
                file.upvotes.remove(user)
            file.downvotes.add(user)
            file.save()
            return Response(
                {"msg": f"Downvoted {file.filename}", "file_points": file.points},
                status=status.HTTP_200_OK,
            )
        except Exception as e:
            print(f"Error: {str(e)}")
            return Response(
                {"error": "Please enter valid file id"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class RegisterFile(APIView):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request):
        user = request.user
        # update_user_ip(request)
        data = request.data
        try:
            with transaction.atomic():
                user.last_poll = timezone.now()
                required_fields = set(
                    ["filename", "topic", "semester", "professor", "course"]
                )
                provided_fields = set(request.data.keys())
                missing_fields = required_fields - provided_fields

                if any(["filename", "topic"]) in missing_fields:
                    raise ValidationError(
                        "Missing one or more of the required field(s): filename, topic"
                    )

                file = File.objects.create(
                    filename=data["filename"], original_author=user
                )
                file.peer_users.add(user)

                file.course = (
                    Course.objects.get(id=data.get("course"))
                    if data.get("course")
                    else None
                )
                file.professor = (
                    Professor.objects.get(id=data.get("professor"))
                    if data.get("professor")
                    else None
                )
                file.semester = (
                    Semester.objects.get(id=data.get("semester"))
                    if data.get("semester")
                    else None
                )

                topic_id = data["topic"]

                try:
                    topic = Topic.objects.get(id=topic_id)
                    file.topic = topic
                except Exception as e:
                    topic_serializer = TopicSerializer(data=topic_id)
                    if topic_serializer.is_valid():
                        topic = topic_serializer.save()
                        file.topic = topic

                response_data = {
                    "id": file.id,
                    "filename": file.filename,
                    "topic": file.topic.name if file.topic else None,
                    "semester": file.semester.name if file.semester else None,
                    "course": file.course.name if file.course else None,
                    "professor": file.professor.name if file.professor else None,
                }
                user.points += 1
                file.save()
                user.save()
                return Response(
                    response_data,
                    status=status.HTTP_201_CREATED,
                )
        except Exception as e:
            print("Error", str(e))
            return Response(
                {"error(s)": "Something went wrong"}, status=status.HTTP_400_BAD_REQUEST
            )


class AddPeerToFile(APIView):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request, file_id):
        try:
            user = request.user
            file = File.objects.get(id=file_id)
            print("File fetched")
            print(file)
            print(file.peer_users)
            if not file.peer_users.filter(id=user.id).exists():
                file.peer_users.add(user)
                file.save()
            return Response(
                f"User added to file with id {file.id}", status=status.HTTP_200_OK
            )
        except Exception as e:
            print(f"Error occured while adding peer to file: {e}")
            return Response(
                "Unable to register peer to given file id",
                status=status.HTTP_400_BAD_REQUEST,
            )


class FileFilterView(APIView):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):
        try:
            # Extract filters from the query parameters
            # update_user_ip(request)
            topic_id = request.query_params.get("topic")
            professor_id = request.query_params.get("professor")
            course_id = request.query_params.get("course")
            semester_id = request.query_params.get("semester")

            # Check if provided filter IDs exist
            if topic_id and not Topic.objects.filter(id=topic_id).exists():
                return Response(
                    {"error": f"Topic with id {topic_id} does not exist"},
                    status=status.HTTP_400_BAD_REQUEST,
                )
            if professor_id and not Professor.objects.filter(id=professor_id).exists():
                return Response(
                    {"error": f"Professor with id {professor_id} does not exist"},
                    status=status.HTTP_400_BAD_REQUEST,
                )
            if course_id and not Course.objects.filter(id=course_id).exists():
                return Response(
                    {"error": f"Course with id {course_id} does not exist"},
                    status=status.HTTP_400_BAD_REQUEST,
                )
            if semester_id and not Semester.objects.filter(id=semester_id).exists():
                return Response(
                    {"error": f"Semester with id {semester_id} does not exist"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Start with the base queryset
            queryset = File.objects.all()

            # Apply filters if they are provided
            if topic_id:
                queryset = queryset.filter(topic__id=topic_id)
            if professor_id:
                queryset = queryset.filter(professor__id=professor_id)
            if course_id:
                queryset = queryset.filter(course__id=course_id)
            if semester_id:
                queryset = queryset.filter(semester__id=semester_id)

            queryset = queryset.annotate(
                upvote_count=Count("upvotes"), downvote_count=Count("downvotes")
            ).order_by(F("downvote_count") - F("upvote_count"))

            # Filter files based on active peers in the past hour
            active_peer_ids = PeerUser.objects.filter(
                last_poll__gte=timezone.now() - timedelta(hours=1)
            ).values_list("id", flat=True)
            queryset = queryset.filter(peer_users__in=active_peer_ids).distinct()

            serializer = FileSerializer(queryset, many=True)
            # Loop through each serialized file to sort its peer_users

            try:
                serialized_data = serializer.data
                print(serialized_data)
            except Exception as e:
                print(str(e))
            for file_data in serialized_data:
                print(file_data)
                file_obj = File.objects.get(id=file_data["id"])
                sorted_peer_users = sorted(
                    file_obj.peer_users.all(), key=lambda x: x.points, reverse=True
                )
                file_data["peer_users"] = UserSerializer(
                    sorted_peer_users, many=True
                ).data

            return Response(serialized_data)
        except Exception as e:
            return Response(
                {"error": f"Something went wrong: {str(e)}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )


class UserFilesView(APIView):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

    def get(self, request):
        user = request.user

        owned_files = user.owned_files.all()
        shared_files = user.shared_files.all()

        all_user_files = owned_files.union(shared_files)

        serializer = FileSerializer(all_user_files, many=True)

        try:
            return Response(serializer.data, status=status.HTTP_200_OK)
        except Exception as e:
            print(f"Error: {e}")
            return Response(
                {"error": f"An error occured {e}"}, status=status.HTTP_400_BAD_REQUEST
            )


class ReportUserView(APIView):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

    def post(self, request):
        data = request.data
        # update_user_ip(request)
        try:
            assert "file_id" in data
            assert "user_id" in data
            assert "description" in data
        except AssertionError as e:
            return Response(
                {"error": f"Missing Parameters. {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        try:
            file = File.objects.get(id=data["file_id"])
            malicious_user = PeerUser.objects.get(id=data["user_id"])
            reporting_user = request.user
            description = data["description"]
        except Exception as e:
            return Response(
                {"error": f"Missing/Incorrect Parameters. {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        existing_user_report_queryset = UserReport.objects.filter(
            user=malicious_user, file=file, reporting_user=reporting_user
        )

        if len(existing_user_report_queryset):
            existing_report = existing_user_report_queryset.first()
            existing_report.description = description
            existing_report.save()
            return Response(
                {
                    "msg": "User report already exists for this file. Updated description"
                },
                status=status.HTTP_208_ALREADY_REPORTED,
            )

        if (
            file not in malicious_user.owned_files.all()
            or file not in malicious_user.shared_files.all()
        ):
            return Response(
                {"error": "User does not own or host the file reported"},
                status=status.HTTP_404_NOT_FOUND,
            )

        UserReport.objects.create(
            user=malicious_user,
            reporting_user=reporting_user,
            file=file,
            description=description,
        )
        malicious_user.points -= 1
        malicious_user.save()
        return Response({"msg": "Report Created!"}, status=status.HTTP_201_CREATED)
