# Python imports
import jwt
from datetime import date, datetime
from dateutil.relativedelta import relativedelta
from uuid import uuid4

# Django imports
from django.db import IntegrityError
from django.db.models import Prefetch
from django.conf import settings
from django.utils import timezone
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.contrib.sites.shortcuts import get_current_site
from django.db.models import (
    Prefetch,
    OuterRef,
    Func,
    F,
    Q,
    Count,
    Case,
    Value,
    CharField,
    When,
    Max,
    IntegerField,
)
from django.db.models.functions import ExtractWeek, Cast, ExtractDay
from django.db.models.fields import DateField
from django.contrib.auth.hashers import make_password

# Third party modules
from rest_framework import status
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from sentry_sdk import capture_exception

# Module imports
from plane.api.serializers import (
    WorkSpaceSerializer,
    WorkSpaceMemberSerializer,
    TeamSerializer,
    WorkSpaceMemberInviteSerializer,
    UserLiteSerializer,
    ProjectMemberSerializer,
    WorkspaceThemeSerializer,
    IssueActivitySerializer,
    IssueLiteSerializer,
    WorkspaceMemberAdminSerializer,
)
from plane.api.views.base import BaseAPIView
from . import BaseViewSet
from plane.db.models import (
    User,
    Workspace,
    WorkspaceMember,
    WorkspaceMemberInvite,
    Team,
    ProjectMember,
    IssueActivity,
    Issue,
    WorkspaceTheme,
    IssueAssignee,
    ProjectFavorite,
    CycleFavorite,
    ModuleMember,
    ModuleFavorite,
    PageFavorite,
    Page,
    IssueViewFavorite,
    IssueLink,
    IssueAttachment,
    IssueSubscriber,
    Project,
    Label,
    WorkspaceMember,
    CycleIssue,
    IssueReaction,
)
from plane.api.permissions import (
    WorkSpaceBasePermission,
    WorkSpaceAdminPermission,
    WorkspaceEntityPermission,
    WorkspaceViewerPermission,
)
from plane.bgtasks.workspace_invitation_task import workspace_invitation
from plane.utils.issue_filters import issue_filters
from plane.utils.grouper import group_results


class WorkSpaceViewSet(BaseViewSet):
    model = Workspace
    serializer_class = WorkSpaceSerializer
    permission_classes = [
        WorkSpaceBasePermission,
    ]

    search_fields = [
        "name",
    ]
    filterset_fields = [
        "owner",
    ]

    lookup_field = "slug"

    def get_queryset(self):
        member_count = (
            WorkspaceMember.objects.filter(
                workspace=OuterRef("id"), member__is_bot=False
            )
            .order_by()
            .annotate(count=Func(F("id"), function="Count"))
            .values("count")
        )

        issue_count = (
            Issue.objects.filter(workspace=OuterRef("id"))
            .order_by()
            .annotate(count=Func(F("id"), function="Count"))
            .values("count")
        )
        return (
            self.filter_queryset(super().get_queryset().select_related("owner"))
            .order_by("name")
            .filter(workspace_member__member=self.request.user)
            .annotate(total_members=member_count)
            .annotate(total_issues=issue_count)
            .select_related("owner")
        )

    def create(self, request):
        try:
            serializer = WorkSpaceSerializer(data=request.data)

            slug = request.data.get("slug", False)
            name = request.data.get("name", False)

            if not name or not slug:
                return Response(
                    {"error": "Both name and slug are required"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            if len(name) > 80 or len(slug) > 48:
                return Response(
                    {"error": "The maximum length for name is 80 and for slug is 48"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            if serializer.is_valid():
                serializer.save(owner=request.user)
                # Create Workspace member
                _ = WorkspaceMember.objects.create(
                    workspace_id=serializer.data["id"],
                    member=request.user,
                    role=20,
                    company_role=request.data.get("company_role", ""),
                )
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(
                [serializer.errors[error][0] for error in serializer.errors],
                status=status.HTTP_400_BAD_REQUEST,
            )

        ## Handling unique integrity error for now
        ## TODO: Extend this to handle other common errors which are not automatically handled by APIException
        except IntegrityError as e:
            if "already exists" in str(e):
                return Response(
                    {"slug": "The workspace with the slug already exists"},
                    status=status.HTTP_410_GONE,
                )
        except Exception as e:
            capture_exception(e)
            return Response(
                {
                    "error": "Something went wrong please try again later",
                    "identifier": None,
                },
                status=status.HTTP_400_BAD_REQUEST,
            )


class UserWorkSpacesEndpoint(BaseAPIView):
    search_fields = [
        "name",
    ]
    filterset_fields = [
        "owner",
    ]

    def get(self, request):
        try:
            member_count = (
                WorkspaceMember.objects.filter(
                    workspace=OuterRef("id"), member__is_bot=False
                )
                .order_by()
                .annotate(count=Func(F("id"), function="Count"))
                .values("count")
            )

            issue_count = (
                Issue.objects.filter(workspace=OuterRef("id"))
                .order_by()
                .annotate(count=Func(F("id"), function="Count"))
                .values("count")
            )

            workspace = (
                (
                    Workspace.objects.prefetch_related(
                        Prefetch(
                            "workspace_member", queryset=WorkspaceMember.objects.all()
                        )
                    )
                    .filter(
                        workspace_member__member=request.user,
                    )
                    .select_related("owner")
                )
                .annotate(total_members=member_count)
                .annotate(total_issues=issue_count)
            )

            serializer = WorkSpaceSerializer(self.filter_queryset(workspace), many=True)
            return Response(serializer.data, status=status.HTTP_200_OK)

        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkSpaceAvailabilityCheckEndpoint(BaseAPIView):
    def get(self, request):
        try:
            slug = request.GET.get("slug", False)

            if not slug or slug == "":
                return Response(
                    {"error": "Workspace Slug is required"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            workspace = Workspace.objects.filter(slug=slug).exists()
            return Response({"status": not workspace}, status=status.HTTP_200_OK)
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class InviteWorkspaceEndpoint(BaseAPIView):
    permission_classes = [
        WorkSpaceAdminPermission,
    ]

    def post(self, request, slug):
        try:
            emails = request.data.get("emails", False)
            # Check if email is provided
            if not emails or not len(emails):
                return Response(
                    {"error": "Emails are required"}, status=status.HTTP_400_BAD_REQUEST
                )

            # check for role level
            requesting_user = WorkspaceMember.objects.get(
                workspace__slug=slug, member=request.user
            )
            if len(
                [
                    email
                    for email in emails
                    if int(email.get("role", 10)) > requesting_user.role
                ]
            ):
                return Response(
                    {"error": "You cannot invite a user with higher role"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            workspace = Workspace.objects.get(slug=slug)

            # Check if user is already a member of workspace
            workspace_members = WorkspaceMember.objects.filter(
                workspace_id=workspace.id,
                member__email__in=[email.get("email") for email in emails],
            ).select_related("member", "workspace", "workspace__owner")

            if len(workspace_members):
                return Response(
                    {
                        "error": "Some users are already member of workspace",
                        "workspace_users": WorkSpaceMemberSerializer(
                            workspace_members, many=True
                        ).data,
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            workspace_invitations = []
            for email in emails:
                try:
                    validate_email(email.get("email"))
                    workspace_invitations.append(
                        WorkspaceMemberInvite(
                            email=email.get("email").strip().lower(),
                            workspace_id=workspace.id,
                            token=jwt.encode(
                                {
                                    "email": email,
                                    "timestamp": datetime.now().timestamp(),
                                },
                                settings.SECRET_KEY,
                                algorithm="HS256",
                            ),
                            role=email.get("role", 10),
                            created_by=request.user,
                        )
                    )
                except ValidationError:
                    return Response(
                        {
                            "error": f"Invalid email - {email} provided a valid email address is required to send the invite"
                        },
                        status=status.HTTP_400_BAD_REQUEST,
                    )
            WorkspaceMemberInvite.objects.bulk_create(
                workspace_invitations, batch_size=10, ignore_conflicts=True
            )

            workspace_invitations = WorkspaceMemberInvite.objects.filter(
                email__in=[email.get("email") for email in emails]
            ).select_related("workspace")

            # create the user if signup is disabled
            if settings.DOCKERIZED and not settings.ENABLE_SIGNUP:
                _ = User.objects.bulk_create(
                    [
                        User(
                            username=str(uuid4().hex),
                            email=invitation.email,
                            password=make_password(uuid4().hex),
                            is_password_autoset=True,
                        )
                        for invitation in workspace_invitations
                    ],
                    batch_size=100,
                )

            for invitation in workspace_invitations:
                workspace_invitation.delay(
                    invitation.email,
                    workspace.id,
                    invitation.token,
                    settings.WEB_URL,
                    request.user.email,
                )

            return Response(
                {
                    "message": "Emails sent successfully",
                },
                status=status.HTTP_200_OK,
            )

        except Workspace.DoesNotExist:
            return Response(
                {"error": "Workspace does not exists"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class JoinWorkspaceEndpoint(BaseAPIView):
    permission_classes = [
        AllowAny,
    ]

    def post(self, request, slug, pk):
        try:
            workspace_invite = WorkspaceMemberInvite.objects.get(
                pk=pk, workspace__slug=slug
            )

            email = request.data.get("email", "")

            if email == "" or workspace_invite.email != email:
                return Response(
                    {"error": "You do not have permission to join the workspace"},
                    status=status.HTTP_403_FORBIDDEN,
                )

            if workspace_invite.responded_at is None:
                workspace_invite.accepted = request.data.get("accepted", False)
                workspace_invite.responded_at = timezone.now()
                workspace_invite.save()

                if workspace_invite.accepted:
                    # Check if the user created account after invitation
                    user = User.objects.filter(email=email).first()

                    # If the user is present then create the workspace member
                    if user is not None:
                        WorkspaceMember.objects.create(
                            workspace=workspace_invite.workspace,
                            member=user,
                            role=workspace_invite.role,
                        )

                        user.last_workspace_id = workspace_invite.workspace.id
                        user.save()

                        # Delete the invitation
                        workspace_invite.delete()

                    return Response(
                        {"message": "Workspace Invitation Accepted"},
                        status=status.HTTP_200_OK,
                    )

                return Response(
                    {"message": "Workspace Invitation was not accepted"},
                    status=status.HTTP_200_OK,
                )

            return Response(
                {"error": "You have already responded to the invitation request"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        except WorkspaceMemberInvite.DoesNotExist:
            return Response(
                {"error": "The invitation either got expired or could not be found"},
                status=status.HTTP_404_NOT_FOUND,
            )
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkspaceInvitationsViewset(BaseViewSet):
    serializer_class = WorkSpaceMemberInviteSerializer
    model = WorkspaceMemberInvite

    permission_classes = [
        WorkSpaceAdminPermission,
    ]

    def get_queryset(self):
        return self.filter_queryset(
            super()
            .get_queryset()
            .filter(workspace__slug=self.kwargs.get("slug"))
            .select_related("workspace", "workspace__owner", "created_by")
        )

    def destroy(self, request, slug, pk):
        try:
            workspace_member_invite = WorkspaceMemberInvite.objects.get(
                pk=pk, workspace__slug=slug
            )
            # delete the user if signup is disabled
            if settings.DOCKERIZED and not settings.ENABLE_SIGNUP:
                user = User.objects.filter(email=workspace_member_invite.email).first()
                if user is not None:
                    user.delete()
            workspace_member_invite.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        except WorkspaceMemberInvite.DoesNotExist:
            return Response(
                {"error": "Workspace member invite does not exists"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class UserWorkspaceInvitationsEndpoint(BaseViewSet):
    serializer_class = WorkSpaceMemberInviteSerializer
    model = WorkspaceMemberInvite

    def get_queryset(self):
        return self.filter_queryset(
            super()
            .get_queryset()
            .filter(email=self.request.user.email)
            .select_related("workspace", "workspace__owner", "created_by")
            .annotate(total_members=Count("workspace__workspace_member"))
        )

    def create(self, request):
        try:
            invitations = request.data.get("invitations")
            workspace_invitations = WorkspaceMemberInvite.objects.filter(
                pk__in=invitations
            )

            WorkspaceMember.objects.bulk_create(
                [
                    WorkspaceMember(
                        workspace=invitation.workspace,
                        member=request.user,
                        role=invitation.role,
                        created_by=request.user,
                    )
                    for invitation in workspace_invitations
                ],
                ignore_conflicts=True,
            )

            # Delete joined workspace invites
            workspace_invitations.delete()

            return Response(status=status.HTTP_200_OK)
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkSpaceMemberViewSet(BaseViewSet):
    serializer_class = WorkspaceMemberAdminSerializer
    model = WorkspaceMember

    permission_classes = [
        WorkSpaceAdminPermission,
    ]

    search_fields = [
        "member__display_name",
        "member__first_name",
    ]

    def get_queryset(self):
        return self.filter_queryset(
            super()
            .get_queryset()
            .filter(workspace__slug=self.kwargs.get("slug"), member__is_bot=False)
            .select_related("workspace", "workspace__owner")
            .select_related("member")
        )

    def partial_update(self, request, slug, pk):
        try:
            workspace_member = WorkspaceMember.objects.get(pk=pk, workspace__slug=slug)
            if request.user.id == workspace_member.member_id:
                return Response(
                    {"error": "You cannot update your own role"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Get the requested user role
            requested_workspace_member = WorkspaceMember.objects.get(
                workspace__slug=slug, member=request.user
            )
            # Check if role is being updated
            # One cannot update role higher than his own role
            if (
                "role" in request.data
                and int(request.data.get("role", workspace_member.role))
                > requested_workspace_member.role
            ):
                return Response(
                    {
                        "error": "You cannot update a role that is higher than your own role"
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            serializer = WorkSpaceMemberSerializer(
                workspace_member, data=request.data, partial=True
            )

            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_200_OK)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except WorkspaceMember.DoesNotExist:
            return Response(
                {"error": "Workspace Member does not exist"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )

    def destroy(self, request, slug, pk):
        try:
            # Check the user role who is deleting the user
            workspace_member = WorkspaceMember.objects.get(workspace__slug=slug, pk=pk)

            # check requesting user role
            requesting_workspace_member = WorkspaceMember.objects.get(
                workspace__slug=slug, member=request.user
            )
            if requesting_workspace_member.role < workspace_member.role:
                return Response(
                    {"error": "You cannot remove a user having role higher than you"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Check for the only member in the workspace
            if (
                workspace_member.role == 20
                and WorkspaceMember.objects.filter(
                    workspace__slug=slug,
                    role=20,
                    member__is_bot=False,
                ).count()
                == 1
            ):
                return Response(
                    {"error": "Cannot delete the only Admin for the workspace"},
                    status=status.HTTP_400_BAD_REQUEST,
                )

            # Delete the user also from all the projects
            ProjectMember.objects.filter(
                workspace__slug=slug, member=workspace_member.member
            ).delete()
            # Remove all favorites
            ProjectFavorite.objects.filter(
                workspace__slug=slug, user=workspace_member.member
            ).delete()
            CycleFavorite.objects.filter(
                workspace__slug=slug, user=workspace_member.member
            ).delete()
            ModuleFavorite.objects.filter(
                workspace__slug=slug, user=workspace_member.member
            ).delete()
            PageFavorite.objects.filter(
                workspace__slug=slug, user=workspace_member.member
            ).delete()
            IssueViewFavorite.objects.filter(
                workspace__slug=slug, user=workspace_member.member
            ).delete()
            # Also remove issue from issue assigned
            IssueAssignee.objects.filter(
                workspace__slug=slug, assignee=workspace_member.member
            ).delete()

            # Remove if module member
            ModuleMember.objects.filter(
                workspace__slug=slug, member=workspace_member.member
            ).delete()
            # Delete owned Pages
            Page.objects.filter(
                workspace__slug=slug, owned_by=workspace_member.member
            ).delete()

            workspace_member.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        except WorkspaceMember.DoesNotExist:
            return Response(
                {"error": "Workspace Member does not exists"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class TeamMemberViewSet(BaseViewSet):
    serializer_class = TeamSerializer
    model = Team
    permission_classes = [
        WorkSpaceAdminPermission,
    ]

    search_fields = [
        "member__display_name",
        "member__first_name",
    ]

    def get_queryset(self):
        return self.filter_queryset(
            super()
            .get_queryset()
            .filter(workspace__slug=self.kwargs.get("slug"))
            .select_related("workspace", "workspace__owner")
            .prefetch_related("members")
        )

    def create(self, request, slug):
        try:
            members = list(
                WorkspaceMember.objects.filter(
                    workspace__slug=slug, member__id__in=request.data.get("members", [])
                )
                .annotate(member_str_id=Cast("member", output_field=CharField()))
                .distinct()
                .values_list("member_str_id", flat=True)
            )

            if len(members) != len(request.data.get("members", [])):
                users = list(set(request.data.get("members", [])).difference(members))
                users = User.objects.filter(pk__in=users)

                serializer = UserLiteSerializer(users, many=True)
                return Response(
                    {
                        "error": f"{len(users)} of the member(s) are not a part of the workspace",
                        "members": serializer.data,
                    },
                    status=status.HTTP_400_BAD_REQUEST,
                )

            workspace = Workspace.objects.get(slug=slug)

            serializer = TeamSerializer(
                data=request.data, context={"workspace": workspace}
            )
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except IntegrityError as e:
            if "already exists" in str(e):
                return Response(
                    {"error": "The team with the name already exists"},
                    status=status.HTTP_410_GONE,
                )
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class UserWorkspaceInvitationEndpoint(BaseViewSet):
    model = WorkspaceMemberInvite
    serializer_class = WorkSpaceMemberInviteSerializer

    permission_classes = [
        AllowAny,
    ]

    def get_queryset(self):
        return self.filter_queryset(
            super()
            .get_queryset()
            .filter(pk=self.kwargs.get("pk"))
            .select_related("workspace")
        )


class UserLastProjectWithWorkspaceEndpoint(BaseAPIView):
    def get(self, request):
        try:
            user = User.objects.get(pk=request.user.id)

            last_workspace_id = user.last_workspace_id

            if last_workspace_id is None:
                return Response(
                    {
                        "project_details": [],
                        "workspace_details": {},
                    },
                    status=status.HTTP_200_OK,
                )

            workspace = Workspace.objects.get(pk=last_workspace_id)
            workspace_serializer = WorkSpaceSerializer(workspace)

            project_member = ProjectMember.objects.filter(
                workspace_id=last_workspace_id, member=request.user
            ).select_related("workspace", "project", "member", "workspace__owner")

            project_member_serializer = ProjectMemberSerializer(
                project_member, many=True
            )

            return Response(
                {
                    "workspace_details": workspace_serializer.data,
                    "project_details": project_member_serializer.data,
                },
                status=status.HTTP_200_OK,
            )

        except User.DoesNotExist:
            return Response({"error": "Forbidden"}, status=status.HTTP_403_FORBIDDEN)
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkspaceMemberUserEndpoint(BaseAPIView):
    def get(self, request, slug):
        try:
            workspace_member = WorkspaceMember.objects.get(
                member=request.user, workspace__slug=slug
            )
            serializer = WorkSpaceMemberSerializer(workspace_member)
            return Response(serializer.data, status=status.HTTP_200_OK)
        except (Workspace.DoesNotExist, WorkspaceMember.DoesNotExist):
            return Response({"error": "Forbidden"}, status=status.HTTP_403_FORBIDDEN)
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkspaceMemberUserViewsEndpoint(BaseAPIView):
    def post(self, request, slug):
        try:
            workspace_member = WorkspaceMember.objects.get(
                workspace__slug=slug, member=request.user
            )
            workspace_member.view_props = request.data.get("view_props", {})
            workspace_member.save()

            return Response(status=status.HTTP_200_OK)
        except WorkspaceMember.DoesNotExist:
            return Response(
                {"error": "User not a member of workspace"},
                status=status.HTTP_403_FORBIDDEN,
            )
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class UserActivityGraphEndpoint(BaseAPIView):
    def get(self, request, slug):
        try:
            issue_activities = (
                IssueActivity.objects.filter(
                    actor=request.user,
                    workspace__slug=slug,
                    created_at__date__gte=date.today() + relativedelta(months=-6),
                )
                .annotate(created_date=Cast("created_at", DateField()))
                .values("created_date")
                .annotate(activity_count=Count("created_date"))
                .order_by("created_date")
            )

            return Response(issue_activities, status=status.HTTP_200_OK)
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class UserIssueCompletedGraphEndpoint(BaseAPIView):
    def get(self, request, slug):
        try:
            month = request.GET.get("month", 1)

            issues = (
                Issue.issue_objects.filter(
                    assignees__in=[request.user],
                    workspace__slug=slug,
                    completed_at__month=month,
                    completed_at__isnull=False,
                )
                .annotate(completed_week=ExtractWeek("completed_at"))
                .annotate(week=F("completed_week") % 4)
                .values("week")
                .annotate(completed_count=Count("completed_week"))
                .order_by("week")
            )

            return Response(issues, status=status.HTTP_200_OK)
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WeekInMonth(Func):
    function = "FLOOR"
    template = "(((%(expressions)s - 1) / 7) + 1)::INTEGER"


class UserWorkspaceDashboardEndpoint(BaseAPIView):
    def get(self, request, slug):
        try:
            issue_activities = (
                IssueActivity.objects.filter(
                    actor=request.user,
                    workspace__slug=slug,
                    created_at__date__gte=date.today() + relativedelta(months=-3),
                )
                .annotate(created_date=Cast("created_at", DateField()))
                .values("created_date")
                .annotate(activity_count=Count("created_date"))
                .order_by("created_date")
            )

            month = request.GET.get("month", 1)

            completed_issues = (
                Issue.issue_objects.filter(
                    assignees__in=[request.user],
                    workspace__slug=slug,
                    completed_at__month=month,
                    completed_at__isnull=False,
                )
                .annotate(day_of_month=ExtractDay("completed_at"))
                .annotate(week_in_month=WeekInMonth(F("day_of_month")))
                .values("week_in_month")
                .annotate(completed_count=Count("id"))
                .order_by("week_in_month")
            )

            assigned_issues = Issue.issue_objects.filter(
                workspace__slug=slug, assignees__in=[request.user]
            ).count()

            pending_issues_count = Issue.issue_objects.filter(
                ~Q(state__group__in=["completed", "cancelled"]),
                workspace__slug=slug,
                assignees__in=[request.user],
            ).count()

            completed_issues_count = Issue.issue_objects.filter(
                workspace__slug=slug,
                assignees__in=[request.user],
                state__group="completed",
            ).count()

            issues_due_week = (
                Issue.issue_objects.filter(
                    workspace__slug=slug,
                    assignees__in=[request.user],
                )
                .annotate(target_week=ExtractWeek("target_date"))
                .filter(target_week=timezone.now().date().isocalendar()[1])
                .count()
            )

            state_distribution = (
                Issue.issue_objects.filter(
                    workspace__slug=slug, assignees__in=[request.user]
                )
                .annotate(state_group=F("state__group"))
                .values("state_group")
                .annotate(state_count=Count("state_group"))
                .order_by("state_group")
            )

            overdue_issues = Issue.issue_objects.filter(
                ~Q(state__group__in=["completed", "cancelled"]),
                workspace__slug=slug,
                assignees__in=[request.user],
                target_date__lt=timezone.now(),
                completed_at__isnull=True,
            ).values("id", "name", "workspace__slug", "project_id", "target_date")

            upcoming_issues = Issue.issue_objects.filter(
                ~Q(state__group__in=["completed", "cancelled"]),
                target_date__gte=timezone.now(),
                workspace__slug=slug,
                assignees__in=[request.user],
                completed_at__isnull=True,
            ).values("id", "name", "workspace__slug", "project_id", "target_date")

            return Response(
                {
                    "issue_activities": issue_activities,
                    "completed_issues": completed_issues,
                    "assigned_issues_count": assigned_issues,
                    "pending_issues_count": pending_issues_count,
                    "completed_issues_count": completed_issues_count,
                    "issues_due_week_count": issues_due_week,
                    "state_distribution": state_distribution,
                    "overdue_issues": overdue_issues,
                    "upcoming_issues": upcoming_issues,
                },
                status=status.HTTP_200_OK,
            )

        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkspaceThemeViewSet(BaseViewSet):
    permission_classes = [
        WorkSpaceAdminPermission,
    ]
    model = WorkspaceTheme
    serializer_class = WorkspaceThemeSerializer

    def get_queryset(self):
        return super().get_queryset().filter(workspace__slug=self.kwargs.get("slug"))

    def create(self, request, slug):
        try:
            workspace = Workspace.objects.get(slug=slug)
            serializer = WorkspaceThemeSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save(workspace=workspace, actor=request.user)
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except Workspace.DoesNotExist:
            return Response(
                {"error": "Workspace does not exist"},
                status=status.HTTP_400_BAD_REQUEST,
            )
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkspaceUserProfileStatsEndpoint(BaseAPIView):
    def get(self, request, slug, user_id):
        try:
            filters = issue_filters(request.query_params, "GET")

            state_distribution = (
                Issue.issue_objects.filter(
                    workspace__slug=slug,
                    assignees__in=[user_id],
                    project__project_projectmember__member=request.user,
                )
                .filter(**filters)
                .annotate(state_group=F("state__group"))
                .values("state_group")
                .annotate(state_count=Count("state_group"))
                .order_by("state_group")
            )

            priority_order = ["urgent", "high", "medium", "low", None]

            priority_distribution = (
                Issue.objects.filter(
                    workspace__slug=slug,
                    assignees__in=[user_id],
                    project__project_projectmember__member=request.user,
                )
                .filter(**filters)
                .values("priority")
                .annotate(priority_count=Count("priority"))
                .annotate(
                    priority_order=Case(
                        *[
                            When(priority=p, then=Value(i))
                            for i, p in enumerate(priority_order)
                        ],
                        default=Value(len(priority_order)),
                        output_field=IntegerField(),
                    )
                )
                .order_by("priority_order")
            )

            created_issues = (
                Issue.issue_objects.filter(
                    workspace__slug=slug,
                    assignees__in=[user_id],
                    project__project_projectmember__member=request.user,
                    created_by_id=user_id,
                )
                .filter(**filters)
                .count()
            )

            assigned_issues_count = (
                Issue.issue_objects.filter(
                    workspace__slug=slug,
                    assignees__in=[user_id],
                    project__project_projectmember__member=request.user,
                )
                .filter(**filters)
                .count()
            )

            pending_issues_count = (
                Issue.issue_objects.filter(
                    ~Q(state__group__in=["completed", "cancelled"]),
                    workspace__slug=slug,
                    assignees__in=[user_id],
                    project__project_projectmember__member=request.user,
                )
                .filter(**filters)
                .count()
            )

            completed_issues_count = (
                Issue.issue_objects.filter(
                    workspace__slug=slug,
                    assignees__in=[user_id],
                    state__group="completed",
                    project__project_projectmember__member=request.user,
                )
                .filter(**filters)
                .count()
            )

            subscribed_issues_count = (
                IssueSubscriber.objects.filter(
                    workspace__slug=slug,
                    subscriber_id=user_id,
                    project__project_projectmember__member=request.user,
                )
                .filter(**filters)
                .count()
            )

            upcoming_cycles = CycleIssue.objects.filter(
                workspace__slug=slug,
                cycle__start_date__gt=timezone.now().date(),
                issue__assignees__in=[
                    user_id,
                ],
            ).values("cycle__name", "cycle__id", "cycle__project_id")

            present_cycle = CycleIssue.objects.filter(
                workspace__slug=slug,
                cycle__start_date__lt=timezone.now().date(),
                cycle__end_date__gt=timezone.now().date(),
                issue__assignees__in=[
                    user_id,
                ],
            ).values("cycle__name", "cycle__id", "cycle__project_id")

            return Response(
                {
                    "state_distribution": state_distribution,
                    "priority_distribution": priority_distribution,
                    "created_issues": created_issues,
                    "assigned_issues": assigned_issues_count,
                    "completed_issues": completed_issues_count,
                    "pending_issues": pending_issues_count,
                    "subscribed_issues": subscribed_issues_count,
                    "present_cycles": present_cycle,
                    "upcoming_cycles": upcoming_cycles,
                }
            )
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkspaceUserActivityEndpoint(BaseAPIView):
    permission_classes = [
        WorkspaceEntityPermission,
    ]

    def get(self, request, slug, user_id):
        try:
            projects = request.query_params.getlist("project", [])

            queryset = IssueActivity.objects.filter(
                workspace__slug=slug,
                project__project_projectmember__member=request.user,
                actor=user_id,
            ).select_related("actor", "workspace", "issue", "project")

            if projects:
                queryset = queryset.filter(project__in=projects)

            return self.paginate(
                request=request,
                queryset=queryset,
                on_results=lambda issue_activities: IssueActivitySerializer(
                    issue_activities, many=True
                ).data,
            )
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkspaceUserProfileEndpoint(BaseAPIView):
    def get(self, request, slug, user_id):
        try:
            user_data = User.objects.get(pk=user_id)

            requesting_workspace_member = WorkspaceMember.objects.get(
                workspace__slug=slug, member=request.user
            )
            projects = []
            if requesting_workspace_member.role >= 10:
                projects = (
                    Project.objects.filter(
                        workspace__slug=slug,
                        project_projectmember__member=request.user,
                    )
                    .annotate(
                        created_issues=Count(
                            "project_issue",
                            filter=Q(project_issue__created_by_id=user_id),
                        )
                    )
                    .annotate(
                        assigned_issues=Count(
                            "project_issue",
                            filter=Q(project_issue__assignees__in=[user_id]),
                        )
                    )
                    .annotate(
                        completed_issues=Count(
                            "project_issue",
                            filter=Q(
                                project_issue__completed_at__isnull=False,
                                project_issue__assignees__in=[user_id],
                            ),
                        )
                    )
                    .annotate(
                        pending_issues=Count(
                            "project_issue",
                            filter=Q(
                                project_issue__state__group__in=[
                                    "backlog",
                                    "unstarted",
                                    "started",
                                ],
                                project_issue__assignees__in=[user_id],
                            ),
                        )
                    )
                    .values(
                        "id",
                        "name",
                        "identifier",
                        "emoji",
                        "icon_prop",
                        "created_issues",
                        "assigned_issues",
                        "completed_issues",
                        "pending_issues",
                    )
                )

            return Response(
                {
                    "project_data": projects,
                    "user_data": {
                        "email": user_data.email,
                        "first_name": user_data.first_name,
                        "last_name": user_data.last_name,
                        "avatar": user_data.avatar,
                        "cover_image": user_data.cover_image,
                        "date_joined": user_data.date_joined,
                        "user_timezone": user_data.user_timezone,
                        "display_name": user_data.display_name,
                    },
                },
                status=status.HTTP_200_OK,
            )
        except WorkspaceMember.DoesNotExist:
            return Response({"error": "Forbidden"}, status=status.HTTP_403_FORBIDDEN)
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkspaceUserProfileIssuesEndpoint(BaseAPIView):
    permission_classes = [
        WorkspaceViewerPermission,
    ]

    def get(self, request, slug, user_id):
        try:
            filters = issue_filters(request.query_params, "GET")
            order_by_param = request.GET.get("order_by", "-created_at")
            issue_queryset = (
                Issue.issue_objects.filter(
                    Q(assignees__in=[user_id])
                    | Q(created_by_id=user_id)
                    | Q(issue_subscribers__subscriber_id=user_id),
                    workspace__slug=slug,
                    project__project_projectmember__member=request.user,
                )
                .filter(**filters)
                .annotate(
                    sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id"))
                    .order_by()
                    .annotate(count=Func(F("id"), function="Count"))
                    .values("count")
                )
                .select_related("project", "workspace", "state", "parent")
                .prefetch_related("assignees", "labels")
                .prefetch_related(
                    Prefetch(
                        "issue_reactions",
                        queryset=IssueReaction.objects.select_related("actor"),
                    )
                )
                .order_by("-created_at")
                .annotate(
                    link_count=IssueLink.objects.filter(issue=OuterRef("id"))
                    .order_by()
                    .annotate(count=Func(F("id"), function="Count"))
                    .values("count")
                )
                .annotate(
                    attachment_count=IssueAttachment.objects.filter(
                        issue=OuterRef("id")
                    )
                    .order_by()
                    .annotate(count=Func(F("id"), function="Count"))
                    .values("count")
                )
            ).distinct()

            # Priority Ordering
            if order_by_param == "priority" or order_by_param == "-priority":
                priority_order = (
                    priority_order
                    if order_by_param == "priority"
                    else priority_order[::-1]
                )
                issue_queryset = issue_queryset.annotate(
                    priority_order=Case(
                        *[
                            When(priority=p, then=Value(i))
                            for i, p in enumerate(priority_order)
                        ],
                        output_field=CharField(),
                    )
                ).order_by("priority_order")

            # State Ordering
            elif order_by_param in [
                "state__name",
                "state__group",
                "-state__name",
                "-state__group",
            ]:
                state_order = (
                    state_order
                    if order_by_param in ["state__name", "state__group"]
                    else state_order[::-1]
                )
                issue_queryset = issue_queryset.annotate(
                    state_order=Case(
                        *[
                            When(state__group=state_group, then=Value(i))
                            for i, state_group in enumerate(state_order)
                        ],
                        default=Value(len(state_order)),
                        output_field=CharField(),
                    )
                ).order_by("state_order")
            # assignee and label ordering
            elif order_by_param in [
                "labels__name",
                "-labels__name",
                "assignees__first_name",
                "-assignees__first_name",
            ]:
                issue_queryset = issue_queryset.annotate(
                    max_values=Max(
                        order_by_param[1::]
                        if order_by_param.startswith("-")
                        else order_by_param
                    )
                ).order_by(
                    "-max_values" if order_by_param.startswith("-") else "max_values"
                )
            else:
                issue_queryset = issue_queryset.order_by(order_by_param)

            issues = IssueLiteSerializer(issue_queryset, many=True).data

            ## Grouping the results
            group_by = request.GET.get("group_by", False)
            if group_by:
                return Response(
                    group_results(issues, group_by), status=status.HTTP_200_OK
                )

            return Response(issues, status=status.HTTP_200_OK)
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkspaceLabelsEndpoint(BaseAPIView):
    permission_classes = [
        WorkspaceViewerPermission,
    ]

    def get(self, request, slug):
        try:
            labels = Label.objects.filter(
                workspace__slug=slug,
                project__project_projectmember__member=request.user,
            ).values("parent", "name", "color", "id", "project_id", "workspace__slug")
            return Response(labels, status=status.HTTP_200_OK)
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )


class WorkspaceMembersEndpoint(BaseAPIView):
    permission_classes = [
        WorkspaceEntityPermission,
    ]

    def get(self, request, slug):
        try:
            workspace_members = WorkspaceMember.objects.filter(
                workspace__slug=slug,
                member__is_bot=False,
            ).select_related("workspace", "member")
            serialzier = WorkSpaceMemberSerializer(workspace_members, many=True)
            return Response(serialzier.data, status=status.HTTP_200_OK)
        except Exception as e:
            capture_exception(e)
            return Response(
                {"error": "Something went wrong please try again later"},
                status=status.HTTP_400_BAD_REQUEST,
            )
