"""Assignment Engine domain models.

Model inventory
---------------
AssignmentType      — enum: for_action / for_review / for_approval / for_information / for_signature / for_follow_up
AssignmentPriority  — enum: low / medium / high / urgent
AssignmentStatus    — enum: pending / in_progress / completed / cancelled
Assignment          — core entity: tenant-scoped assignment
"""

from __future__ import annotations

from typing import ClassVar

from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.translation import gettext_lazy as _

from simorgh.core.models import (
    TenantScopedModel,
    TimeStampedModel,
    UUIDModel,
)


class AssignmentType(models.TextChoices):
    FOR_ACTION      = "for_action",      _("For Action")
    FOR_REVIEW      = "for_review",      _("For Review")
    FOR_APPROVAL    = "for_approval",    _("For Approval")
    FOR_INFORMATION = "for_information", _("For Information")
    FOR_SIGNATURE   = "for_signature",   _("For Signature")
    FOR_FOLLOW_UP   = "for_follow_up",   _("For Follow Up")


class AssignmentPriority(models.TextChoices):
    LOW    = "low",    _("Low")
    MEDIUM = "medium", _("Medium")
    HIGH   = "high",   _("High")
    URGENT = "urgent", _("Urgent")


class AssignmentStatus(models.TextChoices):
    PENDING     = "pending",     _("Pending")
    IN_PROGRESS = "in_progress", _("In Progress")
    COMPLETED   = "completed",   _("Completed")
    CANCELLED   = "cancelled",   _("Cancelled")


class AssignmentTag(UUIDModel, TimeStampedModel):
    """Flat, tenant-scoped label applied to assignments."""

    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="assignment_tags",
    )
    name  = models.CharField(_("name"),  max_length=100)
    color = models.CharField(_("color"), max_length=20, blank=True, default="")

    class Meta:
        verbose_name        = _("assignment tag")
        verbose_name_plural = _("assignment tags")
        constraints = (
            models.UniqueConstraint(
                fields=("tenant", "name"),
                name="assignments_tag_unique_tenant_name",
            ),
        )

    def __str__(self) -> str:
        return self.name


class Assignment(UUIDModel, TenantScopedModel):
    """A reference/assignment between actors, optionally linked to any entity."""

    title       = models.CharField(_("title"), max_length=500)
    description = models.TextField(_("description"), blank=True, default="")

    assignment_type = models.CharField(
        _("assignment type"),
        max_length=20,
        choices=AssignmentType.choices,
        default=AssignmentType.FOR_ACTION,
        db_index=True,
    )
    status = models.CharField(
        _("status"),
        max_length=16,
        choices=AssignmentStatus.choices,
        default=AssignmentStatus.PENDING,
        db_index=True,
    )
    priority = models.CharField(
        _("priority"),
        max_length=10,
        choices=AssignmentPriority.choices,
        default=AssignmentPriority.MEDIUM,
        db_index=True,
    )

    assigned_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.PROTECT,
        related_name="assignments_sent",
        verbose_name=_("assigned by"),
    )
    assigned_to = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assignments_received",
        verbose_name=_("assigned to"),
    )
    assigned_role = models.ForeignKey(
        "iam.Role",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assignments",
        verbose_name=_("assigned role"),
    )
    assigned_org_unit = models.ForeignKey(
        "organizations.OrganizationNode",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assignments",
        verbose_name=_("assigned org unit"),
    )

    due_date     = models.DateTimeField(_("due date"), null=True, blank=True, db_index=True)
    completed_at = models.DateTimeField(_("completed at"), null=True, blank=True)
    completed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assignments_completed",
        verbose_name=_("completed by"),
    )

    is_confidential = models.BooleanField(_("confidential"), default=False)

    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_("entity type"),
    )
    object_id = models.PositiveIntegerField(_("entity id"), null=True, blank=True)
    content_object = GenericForeignKey("content_type", "object_id")

    tags = models.ManyToManyField(
        AssignmentTag,
        blank=True,
        related_name="assignments",
        verbose_name=_("tags"),
    )

    class Meta(TenantScopedModel.Meta):
        verbose_name        = _("assignment")
        verbose_name_plural = _("assignments")
        ordering            = ("-created_at",)
        indexes: ClassVar = [
            *TenantScopedModel.Meta.indexes,
            models.Index(fields=("tenant", "assigned_to", "status"), name="assignments_tenant_asgn_status"),
            models.Index(fields=("tenant", "assignment_type", "status"), name="assignments_tenant_type_status"),
            models.Index(fields=("tenant", "due_date"), name="assignments_tenant_due_date"),
            models.Index(fields=("content_type", "object_id"), name="assignments_content_object"),
        ]

    def __str__(self) -> str:
        return self.title
