"""Correspondence domain models.

Entities:
- Letter: the central entity for all correspondence types (incoming, outgoing,
  internal memo, announcement, notice).
- LetterTemplate: reusable content/structure templates.
- NumberingTemplate: customisable registration-number patterns.
- LetterheadTemplate: reusable letterhead/header templates.
- Category: hierarchical subject/unit categorisation.
- LetterAttachment: scans and file attachments on letters.
- LetterRecipient: multiple recipients for outgoing letters.
- LetterReference: cross-letter links (reply, forward, related).
"""

from __future__ import annotations

from typing import ClassVar

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 (
    AuditedModel,
    OrderedModel,
    SoftDeleteModel,
    TenantScopedModel,
    TimeStampedModel,
    UUIDModel,
    VersionedModel,
)

# ═════════════════════════════════════════════════════════════════════════════
# Enumerations
# ═════════════════════════════════════════════════════════════════════════════


class LetterType(models.TextChoices):
    INCOMING = "incoming", _("correspondence.letter_type.incoming")
    OUTGOING = "outgoing", _("correspondence.letter_type.outgoing")
    INTERNAL_MEMO = "internal_memo", _("correspondence.letter_type.internal_memo")
    ANNOUNCEMENT = "announcement", _("correspondence.letter_type.announcement")
    NOTICE = "notice", _("correspondence.letter_type.notice")


class LetterStatus(models.TextChoices):
    DRAFT = "draft", _("correspondence.letter_status.draft")
    SENT = "sent", _("correspondence.letter_status.sent")
    RECEIVED = "received", _("correspondence.letter_status.received")
    IN_PROGRESS = "in_progress", _("correspondence.letter_status.in_progress")
    COMPLETED = "completed", _("correspondence.letter_status.completed")
    ARCHIVED = "archived", _("correspondence.letter_status.archived")
    CANCELLED = "cancelled", _("correspondence.letter_status.cancelled")


class LetterPriority(models.TextChoices):
    LOW = "low", _("correspondence.letter_priority.low")
    NORMAL = "normal", _("correspondence.letter_priority.normal")
    HIGH = "high", _("correspondence.letter_priority.high")
    URGENT = "urgent", _("correspondence.letter_priority.urgent")


class LetterSecurity(models.TextChoices):
    NORMAL = "normal", _("correspondence.letter_security.normal")
    CONFIDENTIAL = "confidential", _("correspondence.letter_security.confidential")
    SECRET = "secret", _("correspondence.letter_security.secret")
    TOP_SECRET = "top_secret", _("correspondence.letter_security.top_secret")


class RecipientType(models.TextChoices):
    TO = "to", _("correspondence.recipient_type.to")
    CC = "cc", _("correspondence.recipient_type.cc")
    BCC = "bcc", _("correspondence.recipient_type.bcc")


class RecipientStatus(models.TextChoices):
    PENDING = "pending", _("correspondence.recipient_status.pending")
    SENT = "sent", _("correspondence.recipient_status.sent")
    DELIVERED = "delivered", _("correspondence.recipient_status.delivered")
    READ = "read", _("correspondence.recipient_status.read")
    FAILED = "failed", _("correspondence.recipient_status.failed")


class ReferenceType(models.TextChoices):
    REPLY = "reply", _("correspondence.reference_type.reply")
    FORWARD = "forward", _("correspondence.reference_type.forward")
    RELATED = "related", _("correspondence.reference_type.related")


class AttachmentType(models.TextChoices):
    SCAN = "scan", _("correspondence.attachment_type.scan")
    ATTACHMENT = "attachment", _("correspondence.attachment_type.attachment")


class CategoryType(models.TextChoices):
    SUBJECT = "subject", _("correspondence.category_type.subject")
    UNIT = "unit", _("correspondence.category_type.unit")


# ═════════════════════════════════════════════════════════════════════════════
# Category
# ═════════════════════════════════════════════════════════════════════════════


class Category(UUIDModel, TenantScopedModel, SoftDeleteModel, OrderedModel):
    """Hierarchical categorisation for correspondence (subject-based or unit-based)."""

    name = models.CharField(_("name"), max_length=255)
    parent = models.ForeignKey(
        "self",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="children",
        verbose_name=_("parent category"),
    )
    category_type = models.CharField(
        _("category type"),
        max_length=20,
        choices=CategoryType.choices,
        default=CategoryType.SUBJECT,
    )
    description = models.TextField(_("description"), blank=True, default="")
    is_active = models.BooleanField(_("active"), default=True, db_index=True)

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta:
        verbose_name = _("correspondence category")
        verbose_name_plural = _("correspondence categories")

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


# ═════════════════════════════════════════════════════════════════════════════
# NumberingTemplate
# ═════════════════════════════════════════════════════════════════════════════


class NumberingTemplate(UUIDModel, TenantScopedModel, SoftDeleteModel, AuditedModel):
    """Customisable registration-number patterns.

    Pattern placeholders:
      {year}        — current year (4 digits)
      {year_short}  — current year (2 digits)
      {month}       — current month (2 digits)
      {day}         — current day (2 digits)
      {counter}     — per-template auto-increment counter
      {org_code}    — organization node code
      {prefix}      — static prefix
    """

    name = models.CharField(_("name"), max_length=255)
    pattern = models.CharField(
        _("pattern"),
        max_length=255,
        default="{year}/{counter:04d}",
        help_text=_("correspondence.numbering.pattern_help"),
    )
    prefix = models.CharField(_("prefix"), max_length=50, blank=True, default="")
    counter = models.PositiveIntegerField(_("counter"), default=0)
    letter_type = models.CharField(
        _("letter type"),
        max_length=20,
        choices=LetterType.choices,
        default=LetterType.OUTGOING,
    )
    is_default = models.BooleanField(_("default"), default=False, db_index=True)

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta:
        verbose_name = _("numbering template")
        verbose_name_plural = _("numbering templates")
        constraints: ClassVar[list] = [
            models.UniqueConstraint(
                fields=["tenant", "name"],
                condition=models.Q(is_deleted=False),
                name="uq_numbering_template_tenant_name",
            ),
        ]

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


# ═════════════════════════════════════════════════════════════════════════════
# LetterheadTemplate
# ═════════════════════════════════════════════════════════════════════════════


class LetterheadTemplate(UUIDModel, TenantScopedModel, SoftDeleteModel, AuditedModel):
    """Reusable letterhead/header templates for outgoing letters."""

    name = models.CharField(_("name"), max_length=255)
    header_html = models.TextField(_("header HTML"), blank=True, default="")
    footer_html = models.TextField(_("footer HTML"), blank=True, default="")
    css_styles = models.TextField(_("CSS styles"), blank=True, default="")
    is_default = models.BooleanField(_("default"), default=False, db_index=True)
    is_active = models.BooleanField(_("active"), default=True, db_index=True)

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta:
        verbose_name = _("letterhead template")
        verbose_name_plural = _("letterhead templates")

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


# ═════════════════════════════════════════════════════════════════════════════
# LetterTemplate
# ═════════════════════════════════════════════════════════════════════════════


class LetterTemplate(UUIDModel, TenantScopedModel, SoftDeleteModel, AuditedModel):
    """Reusable content/structure templates for all letter types."""

    name = models.CharField(_("name"), max_length=255)
    letter_type = models.CharField(
        _("letter type"),
        max_length=20,
        choices=LetterType.choices,
        default=LetterType.OUTGOING,
    )
    subject_template = models.CharField(
        _("subject template"), max_length=500, blank=True, default=""
    )
    body_template = models.TextField(_("body template"), blank=True, default="")
    letterhead = models.ForeignKey(
        LetterheadTemplate,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="templates",
        verbose_name=_("letterhead"),
    )
    numbering_template = models.ForeignKey(
        NumberingTemplate,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="templates",
        verbose_name=_("numbering template"),
    )
    is_default = models.BooleanField(_("default"), default=False, db_index=True)
    is_active = models.BooleanField(_("active"), default=True, db_index=True)

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta:
        verbose_name = _("letter template")
        verbose_name_plural = _("letter templates")

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


# ═════════════════════════════════════════════════════════════════════════════
# Letter — the central entity
# ═════════════════════════════════════════════════════════════════════════════


class Letter(UUIDModel, TenantScopedModel, SoftDeleteModel, VersionedModel, AuditedModel):
    """Central correspondence entity covering all letter types.

    Incoming letters  → sender_external filled, sender_id optional
    Outgoing letters  → sender_id filled, recipients via LetterRecipient
    Internal memos    → sender_id filled, internal recipients
    Announcements     → broadcast, no specific recipients
    Notices           → formal notices with specific recipients
    """

    letter_type = models.CharField(
        _("letter type"),
        max_length=20,
        choices=LetterType.choices,
        db_index=True,
    )
    status = models.CharField(
        _("status"),
        max_length=20,
        choices=LetterStatus.choices,
        default=LetterStatus.DRAFT,
        db_index=True,
    )
    priority = models.CharField(
        _("priority"),
        max_length=20,
        choices=LetterPriority.choices,
        default=LetterPriority.NORMAL,
    )
    security = models.CharField(
        _("security level"),
        max_length=20,
        choices=LetterSecurity.choices,
        default=LetterSecurity.NORMAL,
    )

    # ── Registration ──────────────────────────────────────────────────────
    registration_number = models.CharField(
        _("registration number"),
        max_length=100,
        blank=True,
        default="",
        db_index=True,
    )
    registration_date = models.DateField(
        _("registration date"),
        null=True,
        blank=True,
        db_index=True,
    )

    # ── Content ───────────────────────────────────────────────────────────
    subject = models.CharField(_("subject"), max_length=500)
    body = models.TextField(_("body"), blank=True, default="")
    summary = models.TextField(_("summary"), blank=True, default="")

    # ── Sender / Source ───────────────────────────────────────────────────
    sender = models.ForeignKey(
        "accounts.User",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="sent_letters",
        verbose_name=_("sender"),
    )
    sender_external = models.CharField(
        _("external sender"), max_length=500, blank=True, default=""
    )
    sender_organization = models.CharField(
        _("sender organization"), max_length=500, blank=True, default=""
    )

    # ── Categorisation ────────────────────────────────────────────────────
    category = models.ForeignKey(
        Category,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="letters",
        verbose_name=_("category"),
    )
    template = models.ForeignKey(
        LetterTemplate,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="letters",
        verbose_name=_("template"),
    )
    numbering_template = models.ForeignKey(
        NumberingTemplate,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="letters",
        verbose_name=_("numbering template"),
    )
    letterhead = models.ForeignKey(
        LetterheadTemplate,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="letters",
        verbose_name=_("letterhead"),
    )

    # ── Dates ─────────────────────────────────────────────────────────────
    letter_date = models.DateField(
        _("letter date"), null=True, blank=True, db_index=True
    )
    received_date = models.DateField(
        _("received date"), null=True, blank=True, db_index=True
    )
    sent_date = models.DateField(
        _("sent date"), null=True, blank=True, db_index=True
    )
    due_date = models.DateField(
        _("due date"), null=True, blank=True, db_index=True
    )

    # ── References ────────────────────────────────────────────────────────
    parent = models.ForeignKey(
        "self",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="replies",
        verbose_name=_("parent letter"),
    )
    external_reference = models.CharField(
        _("external reference"), max_length=255, blank=True, default=""
    )

    # ── Metadata ──────────────────────────────────────────────────────────
    metadata = models.JSONField(_("metadata"), default=dict, blank=True)

    objects: ClassVar[ScopedSoftDeleteManager] = ScopedSoftDeleteManager()

    class Meta:
        verbose_name = _("letter")
        verbose_name_plural = _("letters")
        indexes: ClassVar[list] = [
            models.Index(fields=["letter_type", "status"]),
            models.Index(fields=["registration_date"]),
        ]

    def __str__(self) -> str:
        ref = self.registration_number or f"#{self.pk}"
        return f"{ref} — {self.subject}"


# ═════════════════════════════════════════════════════════════════════════════
# LetterAttachment
# ═════════════════════════════════════════════════════════════════════════════


class LetterAttachment(UUIDModel, TimeStampedModel, SoftDeleteModel, AuditedModel):
    """Scans and file attachments linked to a letter."""

    letter = models.ForeignKey(
        Letter,
        on_delete=models.CASCADE,
        related_name="attachments",
        verbose_name=_("letter"),
    )
    file = models.ForeignKey(
        "storage.FileMetadata",
        on_delete=models.PROTECT,
        related_name="letter_attachments",
        verbose_name=_("file"),
    )
    attachment_type = models.CharField(
        _("attachment type"),
        max_length=20,
        choices=AttachmentType.choices,
        default=AttachmentType.ATTACHMENT,
    )
    description = models.CharField(
        _("description"), max_length=500, blank=True, default=""
    )
    sort_order = models.PositiveIntegerField(_("sort order"), default=0, db_index=True)

    class Meta:
        verbose_name = _("letter attachment")
        verbose_name_plural = _("letter attachments")
        ordering = ["sort_order"]

    def __str__(self) -> str:
        return f"{self.attachment_type} — {self.description or self.file_id}"


# ═════════════════════════════════════════════════════════════════════════════
# LetterRecipient
# ═════════════════════════════════════════════════════════════════════════════


class LetterRecipient(UUIDModel, TimeStampedModel):
    """Recipients of outgoing letters, internal memos, announcements, notices."""

    letter = models.ForeignKey(
        Letter,
        on_delete=models.CASCADE,
        related_name="recipients",
        verbose_name=_("letter"),
    )
    recipient = models.ForeignKey(
        "accounts.User",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="received_letters",
        verbose_name=_("recipient"),
    )
    recipient_name = models.CharField(
        _("recipient name"), max_length=500, blank=True, default=""
    )
    recipient_organization = models.CharField(
        _("recipient organization"), max_length=500, blank=True, default=""
    )
    recipient_type = models.CharField(
        _("recipient type"),
        max_length=10,
        choices=RecipientType.choices,
        default=RecipientType.TO,
    )
    status = models.CharField(
        _("status"),
        max_length=20,
        choices=RecipientStatus.choices,
        default=RecipientStatus.PENDING,
    )
    sent_at = models.DateTimeField(_("sent at"), null=True, blank=True)
    delivered_at = models.DateTimeField(_("delivered at"), null=True, blank=True)
    read_at = models.DateTimeField(_("read at"), null=True, blank=True)

    class Meta:
        verbose_name = _("letter recipient")
        verbose_name_plural = _("letter recipients")

    def __str__(self) -> str:
        name = self.recipient_name or str(self.recipient)
        return f"{self.recipient_type} → {name}"


# ═════════════════════════════════════════════════════════════════════════════
# LetterReference
# ═════════════════════════════════════════════════════════════════════════════


class LetterReference(UUIDModel, TimeStampedModel):
    """Explicit links between related letters (reply chains, forwarding, etc.)."""

    from_letter = models.ForeignKey(
        Letter,
        on_delete=models.CASCADE,
        related_name="references_from",
        verbose_name=_("from letter"),
    )
    to_letter = models.ForeignKey(
        Letter,
        on_delete=models.CASCADE,
        related_name="references_to",
        verbose_name=_("to letter"),
    )
    reference_type = models.CharField(
        _("reference type"),
        max_length=20,
        choices=ReferenceType.choices,
        default=ReferenceType.RELATED,
    )
    note = models.CharField(_("note"), max_length=500, blank=True, default="")

    class Meta:
        verbose_name = _("letter reference")
        verbose_name_plural = _("letter references")
        constraints: ClassVar[list] = [
            models.UniqueConstraint(
                fields=["from_letter", "to_letter", "reference_type"],
                name="uq_letter_ref_from_to_type",
            ),
        ]

    def __str__(self) -> str:
        return f"{self.from_letter_id} → {self.reference_type} → {self.to_letter_id}"
