from typing import Optional

import swapper

from django.db.models import Model
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from dynamicforms import fields
from dynamicforms.fields import AutoGeneratedField
from dynamicforms.mixins import DisplayMode
from dynamicforms.serializers import ModelSerializer
from dynamicforms.viewsets import ModelViewSet
from rest_framework.exceptions import ValidationError

from django_project_base.account.middleware import ProjectNotSelectedError
from django_project_base.auth.models import BaseRole
from django_project_base.base.models import BaseProject


class ProjectRole:
    delimiter = "§"


class ProjectRoleSerializer(ModelSerializer):
    def to_internal_value(self, data):
        internal_value = super().to_internal_value(data)
        if self.instance:
            project = self.instance.name.split(ProjectRole.delimiter)[0]
            internal_value["name"] = f'{project}{ProjectRole.delimiter}{internal_value["name"]}'
        else:
            if not internal_value.get("project"):
                raise ValidationError(dict(project=[_("Project is required")]))
            internal_value["name"] = f'{internal_value["project"]}{ProjectRole.delimiter}{internal_value["name"]}'
        internal_value.pop("project", None)
        return internal_value

    def to_representation(self, instance, row_data=None):
        project_role = super().to_representation(instance, row_data)
        project_role["name"] = project_role["name"].split(ProjectRole.delimiter)[1]
        return project_role

    project = fields.CharField(max_length=512, write_only=True, required=False)
    permissions = AutoGeneratedField(display_table=DisplayMode.SUPPRESS)

    class Meta:
        model = swapper.load_model("django_project_base", "Role")
        exclude = ()


class ProjectRoleViewSet(ModelViewSet):
    serializer_class = ProjectRoleSerializer

    def __get_project(self) -> Optional[BaseProject]:
        try:
            self.request.selected_project.get_deferred_fields()  # force immediate LazyObject evaluation
            return self.request.selected_project
        except ProjectNotSelectedError:
            pass

        if project := self.request.GET.get("project", ""):
            try:
                ProjectModel = swapper.load_model("django_project_base", "Project")
                return ProjectModel.objects.prefetch_related("owner").get(pk=project)
            except Model.DoesNotExist:
                pass

        return None

    def get_queryset(self):
        if self.request.user.is_superuser:
            return self.serializer_class.Meta.model.objects.all()
        if self.action == "list":
            project: BaseProject = self.__get_project()
            if project and project.owner_id == self.request.user.id:
                return self.serializer_class.Meta.model.objects.filter(
                    name__startswith=f"{project.pk}{ProjectRole.delimiter}"
                )
            return self.serializer_class.Meta.model.objects.none()

        try:
            role_class = swapper.load_model("django_project_base", "Role")
            role: BaseRole = get_object_or_404(
                role_class, **{self.lookup_field: self.kwargs[self.lookup_url_kwarg or self.lookup_field]}
            )
            if ProjectRole.delimiter in role.name:
                project_pk: str = role.name.split(ProjectRole.delimiter)[0]
                project: BaseProject = (
                    swapper.load_model("django_project_base", "Project").objects.filter(pk=project_pk).first()
                )
                if project and project.owner_id == self.request.user.userprofile.pk:
                    return self.serializer_class.Meta.model.objects.filter(
                        name__startswith=f"{project_pk}{ProjectRole.delimiter}"
                    )
        except:
            pass

        return self.serializer_class.Meta.model.objects.none()
