"""SLA Engine domain models.

Model inventory
---------------
SLAType            — enum: response / resolution / approval / assignment
SLABreachStatus    — enum: warning / breached / recovered
SLAPolicy          — core entity: tenant-scoped SLA policy definition
SLATimer           — per-entity SLA timer tracking (pause/resume, breach detection)
SLABreach          — records SLA breach events
"""

from __future__ import annotations

from typing import ClassVar

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 SLAType(models.TextChoices):
    RESPONSE   = "response",   _("Response SLA")
    RESOLUTION = "resolution", _("Resolution SLA")
    APPROVAL   = "approval",   _("Approval SLA")
    ASSIGNMENT = "assignment", _("Assignment SLA")


class SLABreachStatus(models.TextChoices):
    WARNING   = "warning",   _("Warning")
    BREACHED  = "breached",  _("Breached")
    RECOVERED = "recovered", _("Recovered")


class SLAPolicy(UUIDModel, TenantScopedModel, SoftDeleteModel):
    """An SLA policy defining response/resolution time targets.

    Can be scoped to a specific entity type (content_type) and optionally
    filtered by priority or other JSON conditions.
    """

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

    sla_type = models.CharField(
        _("SLA type"),
        max_length=16,
        choices=SLAType.choices,
        db_index=True,
    )
    target_hours = models.DecimalField(
        _("target hours"),
        max_digits=6,
        decimal_places=2,
        help_text=_("Maximum hours allowed before breach."),
    )
    warning_hours = models.DecimalField(
        _("warning hours"),
        max_digits=6,
        decimal_places=2,
        null=True,
        blank=True,
        help_text=_("Hours before breach to trigger a warning."),
    )
    business_hours_only = models.BooleanField(
        _("business hours only"),
        default=True,
        help_text=_("Count only business hours for the SLA timer."),
    )

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

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta(TenantScopedModel.Meta):
        verbose_name = _("SLA policy")
        verbose_name_plural = _("SLA policies")
        ordering = ("tenant", "name")
        constraints = (
            models.UniqueConstraint(
                fields=("tenant", "name"),
                name="sla_policy_unique_tenant_name",
            ),
        )
        indexes: ClassVar = [
            *TenantScopedModel.Meta.indexes,
            models.Index(fields=("tenant", "sla_type", "is_active"), name="sla_policy_tenant_type_active"),
        ]

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


class SLATimer(UUIDModel, TimeStampedModel):
    """An SLA timer tracking a specific entity's SLA compliance.

    Linked generically to any entity via content_type/object_id.
    Supports pause/resume for business-hours-only tracking.
    """

    policy = models.ForeignKey(
        SLAPolicy,
        on_delete=models.PROTECT,
        related_name="timers",
        verbose_name=_("SLA policy"),
    )
    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="sla_timers",
        verbose_name=_("tenant"),
    )

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

    started_at = models.DateTimeField(_("started at"), null=True, blank=True)
    paused_at = models.DateTimeField(_("paused at"), null=True, blank=True)
    accumulated_seconds = models.PositiveBigIntegerField(
        _("accumulated seconds"),
        default=0,
        help_text=_("Total elapsed seconds excluding pause periods."),
    )
    target_at = models.DateTimeField(
        _("target at"),
        null=True,
        blank=True,
        help_text=_("When the SLA target is due."),
    )
    warning_at = models.DateTimeField(
        _("warning at"),
        null=True,
        blank=True,
        help_text=_("When a warning should be triggered."),
    )
    warning_sent = models.BooleanField(_("warning sent"), default=False, db_index=True)
    is_breached = models.BooleanField(_("breached"), default=False, db_index=True)
    breached_at = models.DateTimeField(_("breached at"), null=True, blank=True)
    is_recovered = models.BooleanField(_("recovered"), default=False)
    recovered_at = models.DateTimeField(_("recovered at"), null=True, blank=True)
    is_stopped = models.BooleanField(_("stopped"), default=False, db_index=True)
    stopped_at = models.DateTimeField(_("stopped at"), null=True, blank=True)

    class Meta:
        verbose_name = _("SLA timer")
        verbose_name_plural = _("SLA timers")
        ordering = ("-created_at",)
        indexes: ClassVar[list[models.Index]] = [
            models.Index(fields=("tenant", "is_breached", "is_stopped"), name="sla_timer_brchd_stpd"),
            models.Index(fields=("tenant", "target_at"), name="sla_timer_tenant_target"),
            models.Index(fields=("content_type", "object_id"), name="sla_timer_content_object"),
            models.Index(fields=("warning_at", "warning_sent"), name="sla_timer_warning"),
        ]
        constraints = (
            models.UniqueConstraint(
                fields=("policy", "content_type", "object_id"),
                name="sla_timer_unique_policy_entity",
            ),
        )

    def __str__(self) -> str:
        return f"SLA Timer: {self.policy.name} | {self.content_type}:{self.object_id}"


class SLABreach(UUIDModel, TimeStampedModel):
    """Records SLA breach and warning events for audit trail."""

    timer = models.ForeignKey(
        SLATimer,
        on_delete=models.CASCADE,
        related_name="breaches",
        verbose_name=_("SLA timer"),
    )
    policy = models.ForeignKey(
        SLAPolicy,
        on_delete=models.PROTECT,
        related_name="breaches",
        verbose_name=_("SLA policy"),
    )
    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="sla_breaches",
        verbose_name=_("tenant"),
    )

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

    status = models.CharField(
        _("status"),
        max_length=16,
        choices=SLABreachStatus.choices,
        db_index=True,
    )
    occurred_at = models.DateTimeField(_("occurred at"), default=None, db_index=True)
    notes = models.TextField(_("notes"), blank=True, default="")

    class Meta:
        verbose_name = _("SLA breach")
        verbose_name_plural = _("SLA breaches")
        ordering = ("-occurred_at",)
        indexes: ClassVar[list[models.Index]] = [
            models.Index(fields=("tenant", "status"), name="sla_breach_tenant_status"),
            models.Index(fields=("tenant", "occurred_at"), name="sla_breach_tenant_occurred"),
        ]

    def __str__(self) -> str:
        return f"SLA Breach: {self.get_status_display()} | {self.policy.name}"
