from django_ltree.models import TreeModel
from model_utils.models import TimeStampedModel

from django.contrib.auth import get_user_model
from django.db import models
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _

from mentor.core.models.mixins import ActiveModelMixin

from .utils import get_language, lookup_field

User = get_user_model()


class TranslatedFieldMixin:
    def get_translated_field(self, field, default=None):
        lang = get_language()
        return getattr(self, f"{field}_{lang}", default)


class StaffCountry(TranslatedFieldMixin, TimeStampedModel, ActiveModelMixin):
    code = models.CharField(_("код"), max_length=20)
    name_ru = models.CharField(_("название RU"), max_length=255)
    name_en = models.CharField(_("название EN"), max_length=255, blank=True)

    class Meta:
        verbose_name = _("страна")
        verbose_name_plural = _("страны")

    @property
    def name(self):
        return self.get_translated_field("name", self.name_ru)

    def __str__(self):
        return self.name

    @staticmethod
    def extract_from_dict(data: dict) -> dict:
        return {
            "code": data.get("code"),
            "name_ru": lookup_field(data, "name.ru"),
            "name_en": lookup_field(data, "name.en"),
            "is_active": not data.get("is_deleted", False),
        }


class StaffCity(TranslatedFieldMixin, TimeStampedModel, ActiveModelMixin):
    country = models.ForeignKey(
        StaffCountry,
        verbose_name=_("страна"),
        related_name="cities",
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
    )
    name_ru = models.CharField(_("название RU"), max_length=255)
    name_en = models.CharField(_("название EN"), max_length=255, blank=True)

    class Meta:
        verbose_name = _("город на Стаффе")
        verbose_name_plural = _("города")

    @property
    def name(self):
        return self.get_translated_field("name", self.name_ru)

    def __str__(self):
        return self.name

    @staticmethod
    def extract_from_dict(data: dict) -> dict:
        return {
            "name_ru": lookup_field(data, "name.ru"),
            "name_en": lookup_field(data, "name.en"),
            "is_active": not data.get("is_deleted", False),
        }


class StaffOffice(TranslatedFieldMixin, TimeStampedModel, ActiveModelMixin):
    city = models.ForeignKey(
        StaffCity,
        verbose_name=_("город"),
        related_name="offices",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )

    code = models.CharField(_("код"), max_length=255)
    name_ru = models.CharField(_("название RU"), max_length=255)
    name_en = models.CharField(_("название EN"), max_length=255, blank=True)

    class Meta:
        verbose_name = _("офис на Стаффе")
        verbose_name_plural = _("офисы")

    @property
    def name(self):
        return self.get_translated_field("name", self.name_ru)

    def __str__(self):
        return self.name

    @staticmethod
    def extract_from_dict(data: dict) -> dict:
        return {
            "code": data.get("code"),
            "name_ru": lookup_field(data, "name.ru"),
            "name_en": lookup_field(data, "name.en"),
            "is_active": not data.get("is_deleted", False),
        }


class StaffGroup(TimeStampedModel, ActiveModelMixin):
    class Types(models.TextChoices):
        DEPARTMENT = "department", _("Департамент")
        SERVICE = "service", _("Служба")
        SERVICEROLE = "servicerole", _("Служебная роль")
        WIKI = "wiki", _("Вики")

    parent = models.ForeignKey(
        "self",
        verbose_name=_("родительский уровень"),
        related_name="children",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
    )
    level = models.PositiveIntegerField(_("уровень"), default=0)
    name = models.CharField(_("название"), max_length=255)
    url = models.CharField(_("ссылка на группу"), max_length=255)

    group_type = models.CharField(
        _("тип"), max_length=20, choices=Types.choices, blank=True
    )

    class Meta:
        ordering = (
            "level",
            "name",
        )
        verbose_name = _("подразделение на Стаффе")
        verbose_name_plural = _("подразделения")

    def __str__(self):
        return self.name

    @cached_property
    def node_depth(self):
        return self.node.depth

    @staticmethod
    def extract_from_dict(data: dict) -> dict:
        return {
            "name": data.get("name", ""),
            "level": data.get("level", ""),
            "url": data.get("url", ""),
            "group_type": data.get("type", ""),
            "is_active": not data.get("is_deleted", False),
        }


class GroupNode(TreeModel):
    group = models.OneToOneField(
        StaffGroup,
        related_name="node",
        verbose_name=_("подразделение"),
        primary_key=True,
        on_delete=models.CASCADE,
    )

    class Meta:
        ordering = ("path",)
        verbose_name = _("узел подразделения")
        verbose_name_plural = _("дерево подразделений")

    def __str__(self):
        return "{0} [{1}]".format(self.group, self.path)

    @property
    def depth(self) -> int:
        return len(self.path)


class StaffProfile(TranslatedFieldMixin, TimeStampedModel, ActiveModelMixin):
    user = models.OneToOneField(
        User,
        verbose_name=_("пользователь"),
        related_name="staff_profile",
        on_delete=models.CASCADE,
    )
    joined_at = models.DateTimeField(_("дата выхода"), null=True, blank=True)
    is_dismissed = models.BooleanField(_("бывший сотрудник"), default=False)
    language_native = models.CharField(
        _("родной язык"), max_length=2, blank=True, db_index=True
    )
    first_name_ru = models.CharField(_("имя (ru)"), max_length=30, blank=True)
    first_name_en = models.CharField(_("имя (en)"), max_length=30, blank=True)
    last_name_ru = models.CharField(_("фамилия (ru)"), max_length=150, blank=True)
    last_name_en = models.CharField(_("фамилия (en)"), max_length=150, blank=True)
    position_ru = models.CharField(_("должность (ru)"), max_length=255, blank=True)
    position_en = models.CharField(_("должность (en)"), max_length=255, blank=True)
    city = models.ForeignKey(
        StaffCity,
        verbose_name=_("город"),
        related_name="profiles",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
    )
    office = models.ForeignKey(
        StaffOffice,
        verbose_name=_("офис"),
        related_name="profiles",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
    )
    groups = models.ManyToManyField(
        StaffGroup,
        blank=True,
        verbose_name=_("подразделения"),
    )

    YAUID_FIELD = "user__{}".format(User.YAUID_FIELD)

    class Meta:
        verbose_name = _("профиль на Стаффе")
        verbose_name_plural = _("профили сотрудников")

    def __str__(self):
        return str(self.user_id)

    @cached_property
    def uid(self) -> int:
        return self.user.yauid

    @cached_property
    def groups_str(self) -> str:
        group = self.groups.first()
        if group is not None:
            return " / ".join(
                [group_node.group.name for group_node in group.node.ancestors()]
            )

        return ""

    @cached_property
    def group_str(self) -> str:
        group = self.groups.first()
        if group is not None:
            return group.name

        return ""

    @cached_property
    def is_head(self) -> bool:
        return self.leaderships.filter(role=StaffLeadership.ROLE_CHIEF).exists()

    @cached_property
    def leadership_joined_dates(self) -> list:
        return list(
            self.leaderships.filter(is_active=True).values_list("joined_at", flat=True)
        )

    @cached_property
    def group_list_pks(self) -> list:
        return list(self.groups.values_list("pk", flat=True))

    @property
    def first_name(self) -> str:
        return self.get_translated_field("first_name", self.first_name_ru)

    @property
    def last_name(self) -> str:
        return self.get_translated_field("last_name", self.last_name_ru)

    @property
    def full_name(self) -> str:
        """Return full name or username."""
        return " ".join(n for n in [self.first_name, self.last_name] if n)

    @property
    def position(self) -> str:
        return self.get_translated_field("position", self.position_ru)

    def save(self, *args, **kwargs):
        if self.office:
            self.city = self.office.city

        super().save(*args, **kwargs)


class StaffLeadership(TimeStampedModel):
    profile = models.ForeignKey(
        StaffProfile,
        verbose_name=_("профиль"),
        related_name="leaderships",
        on_delete=models.CASCADE,
    )
    group = models.ForeignKey(
        StaffGroup,
        verbose_name=_("подразделение"),
        related_name="leaderships",
        on_delete=models.CASCADE,
    )

    ROLE_CHIEF = "chief"
    ROLE_DEPUTY = "deputy"

    _ROLE_CHOICES = (
        (ROLE_CHIEF, _("руководитель")),
        (ROLE_DEPUTY, _("заместитель")),
    )
    role = models.CharField(
        _("роль"), max_length=255, choices=_ROLE_CHOICES, default=ROLE_CHIEF
    )
    is_active = models.BooleanField(_("активно"), default=True)

    class Meta:
        unique_together = ("profile", "group", "role")
        verbose_name = _("руководство в подразделении")
        verbose_name_plural = _("руководство в подразделениях")
