"""DMS audit bounded context — models (Phase 12).

Design
------
All three entities are **immutable write-once records**.  No update or delete
path exists.

DocumentAuditLog
    Tracks every significant lifecycle event on a document.

AccessLog
    Lower-level log of every access event (view, download, preview, share-link).

SecurityEvent
    Security-relevant incidents: permission denials, unauthorized access,
    bulk-download detection, policy violations.

Key design decisions
--------------------
* ``UUIDModel`` provides the ``public_id`` UUID field used for all external
  (API) references.  The integer ``id`` pk is for DB-internal joins only.
* These models do NOT inherit from ``TenantScopedModel`` because:
  - Audit records are immutable and must never be filtered by soft-delete.
  - ``organization_node`` is optional (some events are tenant-level).
  - We use an explicit ``tenant`` FK for clarity.
* ``occurred_at`` (auto_now_add) is the sole timestamp — there is no
  ``updated_at`` because these records are never updated.

SIEM readiness
    All three share: tenant scope, optional actor FK, occurred_at (indexed),
    optional document FK, and a flexible JSON ``metadata`` field.
"""

from __future__ import annotations

from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _

from simorgh.core.models import UUIDModel


# ---------------------------------------------------------------------------
# Choices
# ---------------------------------------------------------------------------

class DocumentAction(models.TextChoices):
    CREATED = "created", _("Created")
    PUBLISHED = "published", _("Published")
    ARCHIVED = "archived", _("Archived")
    DELETED = "deleted", _("Deleted")
    RESTORED = "restored", _("Restored")
    VERSION_ADDED = "version_added", _("Version Added")
    VERSION_ROLLED_BACK = "version_rolled_back", _("Version Rolled Back")
    METADATA_UPDATED = "metadata_updated", _("Metadata Updated")
    TYPE_CHANGED = "type_changed", _("Type Changed")
    MOVED = "moved", _("Moved")
    RENAMED = "renamed", _("Renamed")
    LOCKED = "locked", _("Locked")
    UNLOCKED = "unlocked", _("Unlocked")
    HOLD_PLACED = "hold_placed", _("Legal Hold Placed")
    HOLD_RELEASED = "hold_released", _("Legal Hold Released")
    OCR_SUBMITTED = "ocr_submitted", _("OCR Submitted")
    OCR_COMPLETED = "ocr_completed", _("OCR Completed")
    CLASSIFIED = "classified", _("AI Classified")
    SHARE_LINK_CREATED = "share_link_created", _("Share Link Created")
    SHARE_LINK_REVOKED = "share_link_revoked", _("Share Link Revoked")


class AccessType(models.TextChoices):
    VIEWED = "viewed", _("Viewed")
    DOWNLOADED = "downloaded", _("Downloaded")
    PREVIEWED = "previewed", _("Previewed")
    SHARE_LINK = "share_link", _("Accessed via Share Link")


class SecurityEventType(models.TextChoices):
    UNAUTHORIZED_ACCESS = "unauthorized_access", _("Unauthorized Access Attempt")
    PERMISSION_DENIED = "permission_denied", _("Permission Denied")
    LEGAL_HOLD_CHECK = "legal_hold_check", _("Legal Hold Consulted")
    POLICY_VIOLATION = "policy_violation", _("Policy Violation")
    BULK_DOWNLOAD = "bulk_download", _("Bulk Download Detected")
    SUSPICIOUS_ACTIVITY = "suspicious_activity", _("Suspicious Activity")


class SecurityEventSeverity(models.TextChoices):
    INFO = "info", _("Info")
    WARNING = "warning", _("Warning")
    CRITICAL = "critical", _("Critical")


# ---------------------------------------------------------------------------
# DocumentAuditLog
# ---------------------------------------------------------------------------

class DocumentAuditLog(UUIDModel):
    """Immutable record of a lifecycle event on a DMS document."""

    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="+",
        verbose_name=_("tenant"),
    )
    organization_node = models.ForeignKey(
        "organizations.OrganizationNode",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name=_("organization node"),
    )
    document = models.ForeignKey(
        "dms.Document",
        on_delete=models.PROTECT,
        related_name="audit_logs",
        verbose_name=_("document"),
    )
    version_ref = models.ForeignKey(
        "dms.DocumentVersion",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="audit_logs",
        verbose_name=_("version reference"),
    )
    actor = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name=_("actor"),
    )
    action = models.CharField(
        _("action"),
        max_length=50,
        choices=DocumentAction.choices,
        db_index=True,
    )
    before_state = models.JSONField(_("before state"), default=dict, blank=True)
    after_state = models.JSONField(_("after state"), default=dict, blank=True)
    metadata = models.JSONField(_("metadata"), default=dict, blank=True)
    ip_address = models.GenericIPAddressField(_("IP address"), null=True, blank=True)
    user_agent = models.CharField(_("user agent"), max_length=500, blank=True, default="")
    occurred_at = models.DateTimeField(_("occurred at"), auto_now_add=True, db_index=True)

    class Meta:
        verbose_name = _("document audit log")
        verbose_name_plural = _("document audit logs")
        ordering = ("-occurred_at",)
        indexes = [
            models.Index(fields=["document_id", "action"], name="dms_audit_doc_action_idx"),
            models.Index(fields=["tenant_id", "occurred_at"], name="dms_audit_tenant_time_idx"),
        ]

    def __str__(self) -> str:
        return f"AuditLog doc={self.document_id} action={self.action} at={self.occurred_at}"


# ---------------------------------------------------------------------------
# AccessLog
# ---------------------------------------------------------------------------

class AccessLog(UUIDModel):
    """Immutable record of a document access event."""

    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="+",
        verbose_name=_("tenant"),
    )
    organization_node = models.ForeignKey(
        "organizations.OrganizationNode",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name=_("organization node"),
    )
    document = models.ForeignKey(
        "dms.Document",
        on_delete=models.PROTECT,
        related_name="access_logs",
        verbose_name=_("document"),
    )
    version_ref = models.ForeignKey(
        "dms.DocumentVersion",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="access_logs",
        verbose_name=_("version"),
    )
    actor = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name=_("actor"),
    )
    access_type = models.CharField(
        _("access type"),
        max_length=30,
        choices=AccessType.choices,
        db_index=True,
    )
    ip_address = models.GenericIPAddressField(_("IP address"), null=True, blank=True)
    user_agent = models.CharField(_("user agent"), max_length=500, blank=True, default="")
    metadata = models.JSONField(_("metadata"), default=dict, blank=True)
    occurred_at = models.DateTimeField(_("occurred at"), auto_now_add=True, db_index=True)

    class Meta:
        verbose_name = _("access log")
        verbose_name_plural = _("access logs")
        ordering = ("-occurred_at",)
        indexes = [
            models.Index(fields=["document_id", "access_type"], name="dms_access_doc_type_idx"),
            models.Index(fields=["tenant_id", "occurred_at"], name="dms_access_tenant_time_idx"),
        ]

    def __str__(self) -> str:
        return f"AccessLog doc={self.document_id} type={self.access_type} at={self.occurred_at}"


# ---------------------------------------------------------------------------
# SecurityEvent
# ---------------------------------------------------------------------------

class SecurityEvent(UUIDModel):
    """Immutable record of a security-relevant incident."""

    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="+",
        verbose_name=_("tenant"),
    )
    document = models.ForeignKey(
        "dms.Document",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="security_events",
        verbose_name=_("document"),
    )
    actor = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name=_("actor"),
    )
    event_type = models.CharField(
        _("event type"),
        max_length=50,
        choices=SecurityEventType.choices,
        db_index=True,
    )
    severity = models.CharField(
        _("severity"),
        max_length=20,
        choices=SecurityEventSeverity.choices,
        default=SecurityEventSeverity.WARNING,
        db_index=True,
    )
    description = models.TextField(_("description"), blank=True, default="")
    ip_address = models.GenericIPAddressField(_("IP address"), null=True, blank=True)
    metadata = models.JSONField(_("metadata"), default=dict, blank=True)
    occurred_at = models.DateTimeField(_("occurred at"), auto_now_add=True, db_index=True)

    class Meta:
        verbose_name = _("security event")
        verbose_name_plural = _("security events")
        ordering = ("-occurred_at",)
        indexes = [
            models.Index(
                fields=["tenant_id", "event_type", "severity"],
                name="dms_sec_tenant_type_sev_idx",
            ),
            models.Index(fields=["tenant_id", "occurred_at"], name="dms_sec_tenant_time_idx"),
        ]

    def __str__(self) -> str:
        return f"SecurityEvent type={self.event_type} sev={self.severity} at={self.occurred_at}"
