"""Helpdesk domain models.

Model inventory
---------------
Queue            — logical grouping / inbox; one *default* per tenant allowed.
Category         — two-level tree; scoped to tenant/org and optionally to a queue.
Tag              — flat, tenant-scoped label for free-form classification.
SLAPolicy        — configurable first-response / resolution targets (hours).
Ticket           — the core entity; drives the helpdesk.ticket workflow FSM.
TicketTag        — M2M through table (explicit for extra metadata readiness).
TicketReply      — both public replies and internal notes (``is_internal``).
SLATimer         — per-ticket SLA breach tracking (two breach types).
Automation       — trigger → conditions → actions rules evaluated on events.

Design decisions
----------------
* Every business entity extends ``TenantScopedModel`` (tenant + org_node FKs)
  so the scoped-query engine applies automatically.
* ``UUIDModel`` exposes ``public_id`` for external/portal-facing identifiers
  without leaking sequential PKs.
* ``SoftDeleteModel`` is used on entities that must survive deletion for audit
  purposes (Ticket, TicketReply). Hard-delete via admin only.
* Attachments are NOT stored here; use ``platform_core.Attachment`` with a
  GenericFK pointing at ``Ticket`` or ``TicketReply``.
* Comments / Activity feed: use ``platform_core.Comment`` / ``Activity``.
* SLA timers are written/updated by the helpdesk.services layer; never
  computed on the fly to stay DB-friendly.
* Custom fields: ``Ticket.custom_fields`` (JSON) is the extensibility seam
  until a dedicated custom-field engine is wired.
"""

from __future__ import annotations

from typing import ClassVar

from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _

from simorgh.core.models import (
    ScopedSoftDeleteManager,
    SoftDeleteManager,
    SoftDeleteModel,
    TenantScopedModel,
    TimeStampedModel,
    UUIDModel,
)

# ---------------------------------------------------------------------------
# Enumerations
# ---------------------------------------------------------------------------

class TicketStatus(models.TextChoices):
    NEW = "new", _("New")
    OPEN = "open", _("Open")
    PENDING = "pending", _("Pending")
    ON_HOLD = "on_hold", _("On Hold")
    SOLVED = "solved", _("Solved")
    CLOSED = "closed", _("Closed")


class TicketPriority(models.TextChoices):
    LOW = "low", _("Low")
    NORMAL = "normal", _("Normal")
    HIGH = "high", _("High")
    URGENT = "urgent", _("Urgent")
    CRITICAL = "critical", _("Critical")


class TicketChannel(models.TextChoices):
    WEB = "web", _("Web")
    EMAIL = "email", _("Email")
    PORTAL = "portal", _("Portal")
    API = "api", _("API")
    PHONE = "phone", _("Phone")


class SLABreachType(models.TextChoices):
    FIRST_RESPONSE = "first_response", _("First Response")
    RESOLUTION = "resolution", _("Resolution")


class AutomationTrigger(models.TextChoices):
    TICKET_CREATED = "ticket_created", _("Ticket Created")
    TICKET_UPDATED = "ticket_updated", _("Ticket Updated")
    TICKET_STATUS_CHANGED = "ticket_status_changed", _("Status Changed")
    REPLY_ADDED = "reply_added", _("Reply Added")
    TIME_ELAPSED = "time_elapsed", _("Time Elapsed")


class ReplyChannel(models.TextChoices):
    WEB = "web", _("Web")
    EMAIL = "email", _("Email")
    PORTAL = "portal", _("Portal")
    API = "api", _("API")


# ---------------------------------------------------------------------------
# Queue
# ---------------------------------------------------------------------------

class Queue(UUIDModel, TenantScopedModel, SoftDeleteModel):
    """A logical inbox / team grouping for tickets.

    One tenant may have multiple queues (e.g. Tier-1 Support, Billing,
    Technical) but only one ``is_default`` queue, enforced at the service
    layer rather than via DB constraint to allow tenant-level migration
    without downtime.
    """

    name = models.CharField(_("name"), max_length=128)
    description = models.TextField(_("description"), blank=True)
    email_address = models.EmailField(
        _("email address"),
        blank=True,
        help_text=_("Inbound email address stub for future email channel."),
    )
    is_default = models.BooleanField(_("is default"), default=False, db_index=True)
    is_active = models.BooleanField(_("is active"), default=True, db_index=True)
    sort_order = models.PositiveIntegerField(_("sort order"), default=0)
    default_sla_policy = models.ForeignKey(
        "helpdesk.SLAPolicy",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="queues_using_as_default",
        verbose_name=_("default SLA policy"),
    )
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name=_("created by"),
    )

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta:
        verbose_name = _("queue")
        verbose_name_plural = _("queues")
        ordering = ("sort_order", "name")
        indexes: ClassVar = [
            models.Index(fields=("tenant", "is_active", "sort_order")),
        ]

    def __str__(self) -> str:
        return self.name


# ---------------------------------------------------------------------------
# Category
# ---------------------------------------------------------------------------

class Category(UUIDModel, TenantScopedModel, SoftDeleteModel):
    """Two-level hierarchy: parent → child categories.

    Optionally scoped to a single queue. When ``queue`` is None the category
    is available across all queues in the tenant.
    """

    queue = models.ForeignKey(
        Queue,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="categories",
        verbose_name=_("queue"),
    )
    parent = models.ForeignKey(
        "self",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="children",
        verbose_name=_("parent"),
    )
    name = models.CharField(_("name"), max_length=128)
    description = models.TextField(_("description"), blank=True)
    is_active = models.BooleanField(_("is active"), default=True, db_index=True)
    sort_order = models.PositiveIntegerField(_("sort order"), default=0)

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta:
        verbose_name = _("category")
        verbose_name_plural = _("categories")
        ordering = ("sort_order", "name")
        indexes: ClassVar = [
            models.Index(fields=("tenant", "queue", "is_active")),
        ]

    def __str__(self) -> str:
        return self.name


# ---------------------------------------------------------------------------
# Tag
# ---------------------------------------------------------------------------

class Tag(UUIDModel, TimeStampedModel, SoftDeleteModel):
    """Flat label scoped to a tenant.

    Tags are intentionally NOT org-node-scoped so they can be shared across
    the entire tenant. They do not extend ``TenantScopedModel`` (which adds
    org_node) — just a direct FK to tenant.
    """

    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="helpdesk_tags",
        verbose_name=_("tenant"),
    )
    name = models.CharField(_("name"), max_length=64)
    color = models.CharField(
        _("color"),
        max_length=7,
        blank=True,
        default="#6B7280",
        help_text=_("Hex color, e.g. #3B82F6"),
    )
    is_active = models.BooleanField(_("is active"), default=True, db_index=True)

    objects: ClassVar[SoftDeleteManager] = SoftDeleteManager()

    class Meta:
        verbose_name = _("tag")
        verbose_name_plural = _("tags")
        ordering = ("name",)
        constraints: ClassVar = [
            models.UniqueConstraint(
                fields=("tenant", "name"),
                name="helpdesk_tag_unique_per_tenant",
            )
        ]

    def __str__(self) -> str:
        return self.name


# ---------------------------------------------------------------------------
# SLA Policy
# ---------------------------------------------------------------------------

class SLAPolicy(UUIDModel, TenantScopedModel):
    """Service Level Agreement targets applied to tickets.

    Hours are calendar hours by default; ``business_hours_only`` is a
    configuration flag (actual business calendar implementation deferred to
    app_settings / future phase).
    """

    name = models.CharField(_("name"), max_length=128)
    description = models.TextField(_("description"), blank=True)
    first_response_hours = models.PositiveIntegerField(
        _("first response hours"),
        default=8,
        help_text=_("Target hours to first public reply."),
    )
    next_response_hours = models.PositiveIntegerField(
        _("next response hours"),
        default=24,
        help_text=_("Target hours between subsequent replies."),
    )
    resolution_hours = models.PositiveIntegerField(
        _("resolution hours"),
        default=72,
        help_text=_("Target hours to resolution."),
    )
    business_hours_only = models.BooleanField(
        _("business hours only"),
        default=False,
        help_text=_("When True, pause timers outside configured business hours."),
    )
    is_active = models.BooleanField(_("is active"), default=True, db_index=True)

    class Meta:
        verbose_name = _("SLA policy")
        verbose_name_plural = _("SLA policies")
        ordering = ("name",)
        indexes: ClassVar = [
            models.Index(fields=("tenant", "is_active")),
        ]

    def __str__(self) -> str:
        return self.name


# ---------------------------------------------------------------------------
# Ticket
# ---------------------------------------------------------------------------

class Ticket(UUIDModel, TenantScopedModel, SoftDeleteModel):
    """Core support-desk entity.

    Lifecycle is driven by the ``helpdesk.ticket`` workflow FSM registered in
    ``simorgh.apps.helpdesk.workflow``. Direct ``status`` writes are allowed
    only via the service layer (``TicketService.transition``) which fires the
    FSM and emits domain events.

    Fields kept deliberately flat to avoid EAV. ``custom_fields`` (JSON) is
    the extensibility seam for tenant-specific metadata until a proper
    custom-field engine is wired.
    """

    queue = models.ForeignKey(
        Queue,
        on_delete=models.PROTECT,
        related_name="tickets",
        verbose_name=_("queue"),
    )
    category = models.ForeignKey(
        Category,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="tickets",
        verbose_name=_("category"),
    )
    subject = models.CharField(_("subject"), max_length=512)
    description = models.TextField(
        _("description"),
        blank=True,
        help_text=_("Initial message body from the requester."),
    )
    status = models.CharField(
        _("status"),
        max_length=16,
        choices=TicketStatus.choices,
        default=TicketStatus.NEW,
        db_index=True,
    )
    priority = models.CharField(
        _("priority"),
        max_length=16,
        choices=TicketPriority.choices,
        default=TicketPriority.NORMAL,
        db_index=True,
    )
    channel = models.CharField(
        _("channel"),
        max_length=16,
        choices=TicketChannel.choices,
        default=TicketChannel.WEB,
        db_index=True,
    )

    # ---------- Requester --------------------------------------------------
    # For authenticated users the ``requester`` FK is set.
    # For portal/email, name+email are stored directly.
    requester = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="helpdesk_tickets_as_requester",
        verbose_name=_("requester"),
    )
    requester_email = models.EmailField(_("requester email"), blank=True)
    requester_name = models.CharField(_("requester name"), max_length=256, blank=True)

    # ---------- Assignment -------------------------------------------------
    assigned_to = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="helpdesk_assigned_tickets",
        verbose_name=_("assigned to"),
    )
    assigned_at = models.DateTimeField(_("assigned at"), null=True, blank=True)

    # ---------- Escalation -------------------------------------------------
    escalated_to = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="helpdesk_escalated_tickets",
        verbose_name=_("escalated to"),
    )
    escalated_at = models.DateTimeField(_("escalated at"), null=True, blank=True)
    escalation_reason = models.TextField(_("escalation reason"), blank=True)

    # ---------- SLA --------------------------------------------------------
    sla_policy = models.ForeignKey(
        SLAPolicy,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="tickets",
        verbose_name=_("SLA policy"),
    )
    first_response_due_at = models.DateTimeField(
        _("first response due at"), null=True, blank=True, db_index=True
    )
    resolution_due_at = models.DateTimeField(
        _("resolution due at"), null=True, blank=True, db_index=True
    )
    first_responded_at = models.DateTimeField(
        _("first responded at"), null=True, blank=True
    )
    resolved_at = models.DateTimeField(_("resolved at"), null=True, blank=True)
    closed_at = models.DateTimeField(_("closed at"), null=True, blank=True)

    # ---------- CSAT -------------------------------------------------------
    satisfaction_rating = models.PositiveSmallIntegerField(
        _("satisfaction rating"),
        null=True,
        blank=True,
        help_text=_("1-5 star rating from the requester."),
    )
    satisfaction_comment = models.TextField(_("satisfaction comment"), blank=True)

    # ---------- Meta -------------------------------------------------------
    tags = models.ManyToManyField(
        Tag,
        through="TicketTag",
        related_name="tickets",
        blank=True,
        verbose_name=_("tags"),
    )
    custom_fields = models.JSONField(_("custom fields"), default=dict, blank=True)
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name=_("created by"),
    )
    reference_number = models.CharField(
        _("reference number"),
        max_length=64,
        blank=True,
        default="",
        db_index=True,
        help_text=_("Auto-generated reference number, e.g. TK-0001."),
    )

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta:
        verbose_name = _("ticket")
        verbose_name_plural = _("tickets")
        ordering = ("-created_at",)
        indexes: ClassVar = [
            models.Index(fields=("tenant", "status", "-created_at")),
            models.Index(fields=("tenant", "queue", "status")),
            models.Index(fields=("tenant", "assigned_to", "status")),
            models.Index(fields=("tenant", "priority", "status")),
            models.Index(fields=("tenant", "first_response_due_at")),
            models.Index(fields=("tenant", "resolution_due_at")),
        ]

    def __str__(self) -> str:
        return f"#{self.pk} {self.subject[:60]}"

    @property
    def requester_display(self) -> str:
        if self.requester_id:
            return (
                getattr(self.requester, "get_full_name", lambda: "")()
                or getattr(self.requester, "email", "")
            )
        return self.requester_name or self.requester_email or "—"


# ---------------------------------------------------------------------------
# TicketTag (explicit M2M)
# ---------------------------------------------------------------------------

class TicketTag(TimeStampedModel):
    ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, related_name="ticket_tags")
    tag = models.ForeignKey(Tag, on_delete=models.CASCADE, related_name="ticket_tags")
    added_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
    )

    class Meta:
        verbose_name = _("ticket tag")
        verbose_name_plural = _("ticket tags")
        constraints: ClassVar = [
            models.UniqueConstraint(
                fields=("ticket", "tag"),
                name="helpdesk_ticket_tag_unique",
            )
        ]


# ---------------------------------------------------------------------------
# TicketReply  (public replies + internal notes unified)
# ---------------------------------------------------------------------------

class TicketReply(UUIDModel, TimeStampedModel, SoftDeleteModel):
    """A public reply or internal note on a ticket.

    Does NOT extend ``TenantScopedModel`` — tenancy is resolved through the
    ticket FK to keep the DB schema tight and avoid redundant FKs. The
    service layer guards tenant isolation at creation time.
    ``is_internal=True`` → visible to agents only (not to the requester portal).
    """

    ticket = models.ForeignKey(
        Ticket,
        on_delete=models.CASCADE,
        related_name="replies",
        verbose_name=_("ticket"),
    )
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="helpdesk_replies",
        verbose_name=_("author"),
    )
    # For portal / email submissions without an account.
    author_email = models.EmailField(_("author email"), blank=True)
    author_name = models.CharField(_("author name"), max_length=256, blank=True)

    body = models.TextField(_("body"))
    html_body = models.TextField(
        _("HTML body"),
        blank=True,
        help_text=_("Sanitized HTML version; generated from ``body`` when blank."),
    )
    is_internal = models.BooleanField(
        _("is internal"),
        default=False,
        db_index=True,
        help_text=_("True = internal note visible to agents only."),
    )
    channel = models.CharField(
        _("channel"),
        max_length=16,
        choices=ReplyChannel.choices,
        default=ReplyChannel.WEB,
    )

    objects: ClassVar[SoftDeleteManager] = SoftDeleteManager()

    class Meta:
        verbose_name = _("ticket reply")
        verbose_name_plural = _("ticket replies")
        ordering = ("created_at",)
        indexes: ClassVar = [
            models.Index(fields=("ticket", "is_internal", "created_at")),
        ]

    def __str__(self) -> str:
        kind = "note" if self.is_internal else "reply"
        return f"{kind}#{self.pk} on ticket#{self.ticket_id}"

    @property
    def author_display(self) -> str:
        if self.author_id:
            return (
                getattr(self.author, "get_full_name", lambda: "")()
                or getattr(self.author, "email", "")
            )
        return self.author_name or self.author_email or "—"


# ---------------------------------------------------------------------------
# SLA Timer
# ---------------------------------------------------------------------------

class SLATimer(TimeStampedModel, SoftDeleteModel):
    """Tracks an active SLA breach deadline for one ticket + breach type.

    One row per (ticket, breach_type). Managed exclusively by
    ``TicketService``; never written directly from views.
    """

    ticket = models.ForeignKey(
        Ticket,
        on_delete=models.CASCADE,
        related_name="sla_timers",
        verbose_name=_("ticket"),
    )
    policy = models.ForeignKey(
        SLAPolicy,
        on_delete=models.PROTECT,
        related_name="timers",
        verbose_name=_("policy"),
    )
    breach_type = models.CharField(
        _("breach type"),
        max_length=32,
        choices=SLABreachType.choices,
        db_index=True,
    )
    started_at = models.DateTimeField(_("started at"), auto_now_add=True)
    due_at = models.DateTimeField(_("due at"), db_index=True)

    # Pause support (for on-hold tickets, business-hours calculation).
    is_paused = models.BooleanField(_("is paused"), default=False, db_index=True)
    paused_at = models.DateTimeField(_("paused at"), null=True, blank=True)
    paused_seconds = models.PositiveBigIntegerField(
        _("paused seconds"),
        default=0,
        help_text=_("Cumulative seconds spent paused; subtracted from effective due_at."),
    )

    breached_at = models.DateTimeField(_("breached at"), null=True, blank=True)
    is_resolved = models.BooleanField(_("is resolved"), default=False, db_index=True)
    resolved_at = models.DateTimeField(_("resolved at"), null=True, blank=True)

    objects: ClassVar[SoftDeleteManager] = SoftDeleteManager()

    class Meta:
        verbose_name = _("SLA timer")
        verbose_name_plural = _("SLA timers")
        constraints: ClassVar = [
            models.UniqueConstraint(
                fields=("ticket", "breach_type"),
                name="helpdesk_sla_timer_unique",
            )
        ]
        indexes: ClassVar = [
            models.Index(fields=("due_at", "is_paused", "is_resolved")),
        ]

    def __str__(self) -> str:
        return f"SLA({self.breach_type}) ticket#{self.ticket_id} due={self.due_at}"


# ---------------------------------------------------------------------------
# Automation
# ---------------------------------------------------------------------------

class Automation(UUIDModel, TenantScopedModel, SoftDeleteModel):
    """Declarative event → condition → action rule engine entry.

    ``conditions`` and ``actions`` are JSON arrays whose shape is documented
    in ``helpdesk/automations.py``. The platform automation executor
    evaluates rules cheaply on each trigger event and fires Python callables
    registered as action handlers.

    Example conditions::

        [{"field": "priority", "op": "eq", "value": "critical"}]

    Example actions::

        [{"type": "assign_to_user", "user_id": 42},
         {"type": "add_tag", "tag": "vip"}]
    """

    name = models.CharField(_("name"), max_length=128)
    description = models.TextField(_("description"), blank=True)
    trigger = models.CharField(
        _("trigger"),
        max_length=32,
        choices=AutomationTrigger.choices,
        default=AutomationTrigger.TICKET_CREATED,
        db_index=True,
    )
    conditions = models.JSONField(
        _("conditions"),
        default=list,
        blank=True,
        help_text=_("List of condition objects evaluated with implicit AND logic."),
    )
    actions = models.JSONField(
        _("actions"),
        default=list,
        blank=True,
        help_text=_("Ordered list of action objects to execute when conditions pass."),
    )
    is_active = models.BooleanField(_("is active"), default=True, db_index=True)
    sort_order = models.PositiveIntegerField(
        _("sort order"),
        default=0,
        help_text=_("Lower values run first."),
    )
    run_count = models.PositiveBigIntegerField(_("run count"), default=0)
    last_run_at = models.DateTimeField(_("last run at"), null=True, blank=True)

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta:
        verbose_name = _("automation")
        verbose_name_plural = _("automations")
        ordering = ("sort_order", "name")
        indexes: ClassVar = [
            models.Index(fields=("tenant", "is_active", "sort_order")),
        ]

    def __str__(self) -> str:
        return self.name  # Automation __str__


# ---------------------------------------------------------------------------
# Agent Team
# ---------------------------------------------------------------------------

class AgentTeamRole(models.TextChoices):
    LEAD   = "lead",   _("Lead")
    MEMBER = "member", _("Member")


class AgentTeam(UUIDModel, TenantScopedModel, SoftDeleteModel):
    """A named group of support agents that can be assigned categories.

    Teams allow supervisors to organise agents by specialty (e.g. Billing,
    Technical, VIP) and to bind one or more ticket categories to a team so
    that incoming tickets can be auto-routed.
    """

    name = models.CharField(_("name"), max_length=128)
    description = models.TextField(_("description"), blank=True)
    is_active = models.BooleanField(_("is active"), default=True, db_index=True)
    sort_order = models.PositiveIntegerField(_("sort order"), default=0)

    # Categories whose tickets should be handled by this team.
    categories = models.ManyToManyField(
        "helpdesk.Category",
        blank=True,
        related_name="agent_teams",
        verbose_name=_("categories"),
    )

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta:
        verbose_name = _("agent team")
        verbose_name_plural = _("agent teams")
        ordering = ("sort_order", "name")
        indexes: ClassVar = [
            models.Index(fields=("tenant", "is_active", "sort_order")),
        ]

    def __str__(self) -> str:
        return self.name  # AgentTeam __str__


class AgentTeamMember(TimeStampedModel, SoftDeleteModel):
    """Through table — a user's membership in an AgentTeam."""

    team = models.ForeignKey(
        AgentTeam,
        on_delete=models.CASCADE,
        related_name="memberships",
        verbose_name=_("team"),
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="helpdesk_team_memberships",
        verbose_name=_("user"),
    )
    role = models.CharField(
        _("role"),
        max_length=16,
        choices=AgentTeamRole.choices,
        default=AgentTeamRole.MEMBER,
        db_index=True,
    )
    added_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name=_("added by"),
    )

    objects: ClassVar[SoftDeleteManager] = SoftDeleteManager()

    class Meta:
        verbose_name = _("agent team member")
        verbose_name_plural = _("agent team members")
        ordering = ("role", "user__first_name")
        constraints: ClassVar = [
            models.UniqueConstraint(
                fields=("team", "user"),
                name="helpdesk_team_member_unique",
            )
        ]

    def __str__(self) -> str:
        return f"{self.user} → {self.team} ({self.role})"


# Re-export ConnectorConfig from connectors.py so Django's migration
# autodetector discovers it (models must be importable from models.py).
from simorgh.apps.helpdesk.connectors import ConnectorConfig  # noqa: E402, F401

# Re-export AI models for Django migration discovery.
from simorgh.apps.helpdesk.ai.models import AITicketSuggestion, AIActionLog  # noqa: E402, F401
