"""Escalation Engine domain models.

Model inventory
---------------
EscalationTriggerType   — enum: time_based / rule_based / event_based
EscalationType          — enum: hierarchy / role / user
EscalationStatus        — enum: pending / in_progress / escalated / resolved / cancelled / failed
EscalationInstance      — core entity: tracks an escalation against an entity
EscalationRule          — defines when and how to escalate
EscalationLog           — audit trail for escalation actions
"""

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.db.scoped import ScopedSoftDeleteManager
from simorgh.core.models import (
    SoftDeleteModel,
    TenantScopedModel,
    TimeStampedModel,
    UUIDModel,
)


class EscalationTriggerType(models.TextChoices):
    TIME_BASED = "time_based", _("Time Based")
    RULE_BASED = "rule_based", _("Rule Based")
    EVENT_BASED = "event_based", _("Event Based")


class EscalationType(models.TextChoices):
    HIERARCHY = "hierarchy", _("Hierarchy Based")
    ROLE = "role", _("Role Based")
    USER = "user", _("User Based")


class EscalationStatus(models.TextChoices):
    PENDING = "pending", _("Pending")
    IN_PROGRESS = "in_progress", _("In Progress")
    ESCALATED = "escalated", _("Escalated")
    RESOLVED = "resolved", _("Resolved")
    CANCELLED = "cancelled", _("Cancelled")
    FAILED = "failed", _("Failed")


class EscalationRule(UUIDModel, TenantScopedModel, SoftDeleteModel):
    """Defines when and how escalations should happen.

    Can be scoped to specific entity types (content_type) and optionally
    filtered by priority or custom JSON conditions.
    """

    name = models.CharField(_("name"), max_length=300)
    description = models.TextField(_("description"), blank=True, default="")

    trigger_type = models.CharField(
        _("trigger type"),
        max_length=16,
        choices=EscalationTriggerType.choices,
        db_index=True,
    )
    trigger_config = models.JSONField(
        _("trigger configuration"),
        default=dict,
        blank=True,
        help_text=_(
            "JSON config defining trigger conditions: "
            "time_threshold_hours, event_names, conditions, etc."
        ),
    )

    escalation_type = models.CharField(
        _("escalation type"),
        max_length=16,
        choices=EscalationType.choices,
        db_index=True,
    )
    escalation_config = models.JSONField(
        _("escalation configuration"),
        default=dict,
        blank=True,
        help_text=_(
            "JSON config defining escalation target: "
            "target_role_id, target_org_unit_id, max_hierarchy_levels, etc."
        ),
    )
    max_levels = models.PositiveSmallIntegerField(
        _("maximum escalation levels"),
        default=3,
        help_text=_("Maximum number of escalation levels before final decision."),
    )

    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="escalation_rules",
        verbose_name=_("entity type"),
        help_text=_("If set, this escalation rule only applies to this entity type."),
    )
    conditions = models.JSONField(
        _("conditions"),
        default=dict,
        blank=True,
        help_text=_("JSON conditions for matching this rule to specific entities."),
    )
    priority = models.CharField(
        _("priority"),
        max_length=10,
        choices=(
            ("low", _("Low")),
            ("medium", _("Medium")),
            ("high", _("High")),
            ("urgent", _("Urgent")),
        ),
        blank=True,
        db_index=True,
    )
    is_active = models.BooleanField(_("active"), default=True, db_index=True)

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta(TenantScopedModel.Meta):
        verbose_name = _("escalation rule")
        verbose_name_plural = _("escalation rules")
        ordering = ("tenant", "name")
        constraints = (
            models.UniqueConstraint(
                fields=("tenant", "name"),
                name="escal_rule_unique_tenant_name",
            ),
        )
        indexes: ClassVar = [
            *TenantScopedModel.Meta.indexes,
            models.Index(
                fields=("tenant", "trigger_type", "is_active"),
                name="escal_rule_tenant_trig_act",
            ),
        ]

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


class EscalationInstance(UUIDModel, TimeStampedModel):
    """Tracks a specific escalation against an entity.

    Linked generically to any entity via content_type/object_id.
    Supports multiple escalation levels (level 1 → level 2 → …).
    """

    rule = models.ForeignKey(
        EscalationRule,
        on_delete=models.PROTECT,
        related_name="instances",
        verbose_name=_("escalation rule"),
    )
    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="escalation_instances",
        verbose_name=_("tenant"),
    )

    source_content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        related_name="escalation_source_entities",
        verbose_name=_("source entity type"),
    )
    source_object_id = models.PositiveIntegerField(_("source entity id"))
    source_object = GenericForeignKey("source_content_type", "source_object_id")

    status = models.CharField(
        _("status"),
        max_length=16,
        choices=EscalationStatus.choices,
        default=EscalationStatus.PENDING,
        db_index=True,
    )
    escalation_level = models.PositiveSmallIntegerField(
        _("escalation level"),
        default=1,
        help_text=_("Current escalation level (1, 2, 3, …)."),
    )

    escalated_from_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name="escalations_from",
        verbose_name=_("escalated from"),
    )
    escalated_to_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="escalations_to",
        verbose_name=_("escalated to user"),
    )
    escalated_to_role = models.ForeignKey(
        "iam.Role",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="escalation_instances",
        verbose_name=_("escalated to role"),
    )

    triggered_at = models.DateTimeField(_("triggered at"), null=True, blank=True, db_index=True)
    escalated_at = models.DateTimeField(_("escalated at"), null=True, blank=True)
    resolved_at = models.DateTimeField(_("resolved at"), null=True, blank=True)
    reason = models.TextField(_("escalation reason"), blank=True, default="")
    resolution_note = models.TextField(_("resolution note"), blank=True, default="")

    class Meta:
        verbose_name = _("escalation instance")
        verbose_name_plural = _("escalation instances")
        ordering = ("-created_at",)
        indexes: ClassVar[list[models.Index]] = [
            models.Index(fields=("tenant", "status"), name="escalation_inst_tenant_status"),
            models.Index(
                fields=("tenant", "triggered_at"),
                name="escal_inst_tenant_trig",
            ),
            models.Index(
                fields=("source_content_type", "source_object_id"),
                name="escalation_inst_source_object",
            ),
            models.Index(fields=("rule", "status"), name="escalation_inst_rule_status"),
        ]

    def __str__(self) -> str:
        return (
            f"Escalation #{self.escalation_level}: "
            f"{self.source_content_type}:{self.source_object_id}"
        )


class EscalationLog(UUIDModel, TimeStampedModel):
    """Audit trail for escalation actions."""

    instance = models.ForeignKey(
        EscalationInstance,
        on_delete=models.CASCADE,
        related_name="logs",
        verbose_name=_("escalation instance"),
    )
    rule = models.ForeignKey(
        EscalationRule,
        on_delete=models.PROTECT,
        related_name="logs",
        verbose_name=_("escalation rule"),
    )
    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="escalation_logs",
        verbose_name=_("tenant"),
    )

    action = models.CharField(
        _("action"),
        max_length=24,
        choices=(
            ("triggered", _("Triggered")),
            ("notified", _("Notified")),
            ("escalated", _("Escalated")),
            ("reassigned", _("Reassigned")),
            ("resolved", _("Resolved")),
            ("cancelled", _("Cancelled")),
            ("failed", _("Failed")),
        ),
        db_index=True,
    )
    actor = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="escalation_logs",
        verbose_name=_("actor"),
    )
    note = models.TextField(_("note"), blank=True, default="")
    metadata = models.JSONField(_("metadata"), default=dict, blank=True)

    class Meta:
        verbose_name = _("escalation log")
        verbose_name_plural = _("escalation logs")
        ordering = ("-created_at",)
        indexes: ClassVar[list[models.Index]] = [
            models.Index(fields=("tenant", "action"), name="escalation_log_tenant_action"),
            models.Index(
                fields=("instance", "created_at"),
                name="escal_log_inst_created",
            ),
        ]

    def __str__(self) -> str:
        return f"Escalation Log: {self.get_action_display()} | {self.instance_id}"
