from rest_framework.generics import (ListAPIView, CreateAPIView, UpdateAPIView, RetrieveAPIView, DestroyAPIView)
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from datetime import datetime

from django.db.models import Count, OuterRef, Subquery, Sum, Q
from django.db.models.functions import Coalesce
from django.shortcuts import get_object_or_404

from apps.project.serializers.project_serializers import ProjectListSerializer, ProjectAddSerializer, \
    ProjectDetailSerializer, ProjectVendorSerializer, ProjectAssignEmployeeAPISerializer, ProjectDeleteAPISerializer, ProjectMinutesOfMeetingSerializer,\
    ProjectConsultantSerializer, ProjectContractorSerializer
from apps.project.models import Project
from apps.vendor.models import Vendor
from apps.consultant.models import Consultant
from apps.contractor.models import Contractor
from apps.employee.models import Activity
from apps.user.models import User




class ProjectListAPIView(ListAPIView):
    serializer_class = ProjectListSerializer
    filter_backends = [SearchFilter, OrderingFilter]
    search_fields = ['name', 'client_name', ]
    ordering_fields = ['id', 'name', 'client_name', 'total_vendors', 'total_working_duration', 'total_consultants', 'total_contractors']

    def get_queryset(self):
        employee_id = self.request.query_params.get('employee_id', None)
        client_id = self.request.query_params.get('client_id', None)
        vendor_id = self.request.query_params.get('vendor_id', None)
        consultant_id = self.request.query_params.get('consultant_id', None)
        contractor_id = self.request.query_params.get('contractor_id', None)
        project_without_employee_id = self.request.query_params.get('project_without_employee_id')
        with_trashed = self.request.query_params.get('with_trashed')

        queryset = Project.objects.only('id', 'name', 'client').select_related('client').annotate_client_name()

        activity_qs = Activity.objects.filter(project_id=OuterRef('pk')).values('project_id').annotate(total_working_duration=Sum('total_duration')).values('project_id', 'total_working_duration')

        if with_trashed == 'true':
            queryset = Project.objects.with_trashed().annotate_client_name()

        if employee_id:
            activity_qs = activity_qs.filter(user_id=OuterRef('project_employees__activity__user_id'))
            queryset = queryset.filter(project_employees=employee_id).distinct()

        if client_id:
            queryset = queryset.filter(client_id=client_id)

        if vendor_id:
            queryset = queryset.filter(vendors__id=vendor_id)

        if contractor_id:
            queryset = queryset.filter(contractors__id=contractor_id)

        if consultant_id:
            queryset = queryset.filter(consultants__id=consultant_id)

        if project_without_employee_id:
            queryset = queryset.exclude(project_employees__id=project_without_employee_id)


        queryset = queryset.annotate(
            total_working_duration=Subquery(activity_qs.values('total_working_duration')),
        ).annotate_vendors_count().annotate_consultants_count().annotate_contractors_count()

        return queryset


class ProjectCreateAPIView(CreateAPIView):
    serializer_class = ProjectAddSerializer
    permission_classes = [IsAuthenticated]


class ProjectEditAPIView(UpdateAPIView):
    http_method_names = ['patch']
    serializer_class = ProjectAddSerializer
    queryset = Project.objects.all()
    permission_classes = [IsAuthenticated]

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['project_id'] = self.kwargs['pk']
        return context


class ProjectDetailAPIView(RetrieveAPIView):
    serializer_class = ProjectDetailSerializer
    permission_classes = [IsAuthenticated]
    queryset = Project.objects.with_trashed().select_related('client')


class ProjectMinutesOfMeetingUpdateAPIView(UpdateAPIView):
    serializer_class = ProjectMinutesOfMeetingSerializer
    permission_classes = [IsAuthenticated]
    queryset = Project.objects.only('minutes_of_meeting')

    def update(self, request, *args, **kwargs):
        project = get_object_or_404(self.queryset, pk=kwargs['pk'])
        serializer = self.get_serializer(project, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)

        project.minutes_of_meeting = serializer.validated_data.get('minutes_of_meeting')
        project.save()

        return Response(serializer.data)


class ProjectDeleteAPIView(DestroyAPIView):
    serializer_class = ProjectDeleteAPISerializer

    def delete(self, request, pk, *args, **kwargs):
        project = get_object_or_404(Project, pk=pk)

        Project.objects.filter(id=pk).update(deleted_by=self.request.user, deleted_at=datetime.now())

        serializer = self.serializer_class(project)

        return Response(serializer.data)


class ProjectAssignVendorAPIView(APIView):
    def post(self, request, pk):
        project = get_object_or_404(Project, pk=pk)

        serializer = ProjectVendorSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        vendor_id = serializer.validated_data.get('vendor_id', [])
        vendors = Vendor.objects.filter(id__in=vendor_id)

        project.vendors.add(*vendors)

        return Response({'message': 'Vendors has been assigned successfully'}, status=status.HTTP_201_CREATED)


class ProjectAssignConsultantAPIView(APIView):
    def post(self, request, pk):
        project = get_object_or_404(Project, pk=pk)

        serializer = ProjectConsultantSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        consultant_id = serializer.validated_data.get('consultant_id', [])
        consultants = Consultant.objects.filter(id__in=consultant_id)

        project.consultants.add(*consultants)

        return Response({'message': 'consultants has been assigned successfully'}, status=status.HTTP_201_CREATED)


class ProjectAssignContractorAPIView(APIView):
    def post(self, request, pk):
        project = get_object_or_404(Project, pk=pk)

        serializer = ProjectContractorSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        contractor_id = serializer.validated_data.get('contractor_id', [])
        contractors = Contractor.objects.filter(id__in=contractor_id)

        project.contractors.add(*contractors)

        return Response({'message': 'Contractors has been assigned successfully'}, status=status.HTTP_201_CREATED)


class ProjectUnAssignVendorAPIView(APIView):
    def post(self, request, pk):
        project = get_object_or_404(Project, pk=pk)

        serializer = ProjectVendorSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        vendor_id = serializer.validated_data.get('vendor_id', [])
        vendors = Vendor.objects.filter(id__in=vendor_id)

        project.vendors.remove(*vendors)

        return Response({'message': 'Vendors has been unassigned successfully'}, status=status.HTTP_201_CREATED)


class ProjectUnAssignConsultantAPIView(APIView):
    def post(self, request, pk):
        project = get_object_or_404(Project, pk=pk)

        serializer = ProjectConsultantSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        consultant_id = serializer.validated_data.get('consultant_id', [])
        consultants = Consultant.objects.filter(id__in=consultant_id)

        project.consultants.remove(*consultants)

        return Response({'message': 'consultants has been unassigned successfully'}, status=status.HTTP_201_CREATED)


class ProjectUnAssignContractorAPIView(APIView):
    def post(self, request, pk):
        project = get_object_or_404(Project, pk=pk)

        serializer = ProjectContractorSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        contractor_id = serializer.validated_data.get('contractor_id', [])
        contractors = Contractor.objects.filter(id__in=contractor_id)

        project.contractors.remove(*contractors)

        return Response({'message': 'contractors has been unassigned successfully'}, status=status.HTTP_201_CREATED)


class ProjectAssignEmployeeAPIView(APIView):
    def post(self, request, pk):
        project = get_object_or_404(Project, pk=pk)

        serializer = ProjectAssignEmployeeAPISerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        employee_ids = serializer.validated_data.get('employee_ids', [])
        employees = User.objects.filter(id__in=employee_ids)
        project.project_employees.add(*employees)

        return Response({'message': 'Employees has been assigned successfully'}, status=status.HTTP_201_CREATED)


class ProjectUnAssignEmployeeAPIView(APIView):
    def post(self, request, pk):
        project = get_object_or_404(Project, pk=pk)

        serializer = ProjectAssignEmployeeAPISerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        employee_ids = serializer.validated_data.get('employee_ids', [])
        employees = User.objects.filter(id__in=employee_ids)
        project.project_employees.remove(*employees)

        return Response({'message': 'Employees has been unassigned successfully'}, status=status.HTTP_201_CREATED)