from django.contrib.auth import get_user_model
from django.contrib.postgres.expressions import ArraySubquery
from django.db import models
from django.db.models import OuterRef
from django.db.models.fields.related import RelatedField
from django.db.models.functions import JSONObject


from app.utils.helpers import recursive_getattr


from uuid import uuid4


User = get_user_model()


class BaseQuerySet(models.QuerySet):
    def headers(self):
        """Return the list of header for a list."""

        def _field(field):
            ret = {
                field.name: {
                    "text": field.verbose_name.capitalize(),
                    "align": "",
                    "encrypted": field.name in self.model.Encryption.fields,
                    "editable": field.name not in self.model.Serialization.excluded_fields_edit,
                    "field_widget": "v-textarea",
                    "choices": None,
                    "details": True,
                    "dynamic_field_type": field.name in self.model.Serialization.dynamic_field_type,
                }
            }

            if isinstance(field, RelatedField):
                ret[f"{field.name}__name"] = {
                    "text": f"{field.verbose_name.capitalize()} - Name",
                    "align": "",
                    "encrypted": True,
                }
                ret[f"{field.name}__name"] = {
                    "text": f"{field.verbose_name.capitalize()} - Identifier",
                    "align": "",
                    "encrypted": True,
                }

                ret[field.name].update(
                    align=" d-none",
                    testing=123,
                    field_widget="v-select",
                )

            if field.choices:
                ret[field.name].update(
                    field_widget="v-select",
                    choices=[
                        {
                            "value": c[0],
                            "text": c[1],
                        }
                        for c in field.choices
                    ],
                )

            return ret

        fields = {}
        for field in self.model._meta.fields:
            if field.name in self.model.Serialization.excluded_fields:
                continue

            fields.update(_field(field))

        return fields

    def serialize(self):
        """Serialize a queryset."""

        fields = []
        for field_name, _ in self.headers().items():
            fields.append(field_name)

        if hasattr(self.model, "history"):
            qs = self.annotate(
                history=ArraySubquery(
                    self.model.history.model.objects.filter(id=OuterRef("id")).values(
                        json=JSONObject(
                            id="history_id",
                            date="history_date",
                        )
                    )
                )
            )
            fields.append("history")

        else:
            qs = self

        return qs.values(*fields)


class BaseManager(models.Manager.from_queryset(BaseQuerySet)):
    pass


class BaseModel(models.Model):
    class Meta:
        abstract = True

    class Serialization:
        # Exclude fields from serialization
        excluded_fields = []
        excluded_fields_edit = ["id", "created_at", "last_modified_at"]
        dynamic_field_type = []

    class Encryption:
        fields = ["name", "description", "custom_identifier"]

    def serialize(self):
        ret = {}

        for field_name in self._meta.model.objects.headers().keys():
            value = recursive_getattr(self, field_name)

            if isinstance(value, BaseModel):
                value = value.id

            ret[field_name] = value

        return ret

    id = models.UUIDField(primary_key=True, default=uuid4, editable=False)

    name = models.TextField(max_length=2048, blank=True, null=True)
    description = models.TextField(max_length=2048, blank=True, null=True)

    custom_identifier = models.TextField(max_length=2048, null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    last_modified_at = models.DateTimeField(auto_now=True)

    objects = BaseManager()
