"""Auto-audit mixin for `TenantScopedModel` subclasses.

Captures the loaded row on `from_db`, then writes an audit entry on save/
delete via `simorgh.core.audit.record_event`. Apps opt in per-model — no
global save signal — so noisy bulk operations stay quiet by default.
"""

from __future__ import annotations

from typing import ClassVar

from django.db import models

from simorgh.apps.audit.diff import serialize_instance
from simorgh.core.audit import record_event


class AuditedModel(models.Model):
    """Mix in alongside `TenantScopedModel` to get create/update/delete audit."""

    audit_exclude: ClassVar[tuple[str, ...]] = ("updated_at",)

    class Meta:
        abstract = True

    @classmethod
    def from_db(cls, db, field_names, values):
        instance = super().from_db(db, field_names, values)
        instance._audit_loaded_state = serialize_instance(instance, exclude=cls.audit_exclude)
        return instance

    def save(self, *args, **kwargs):  # noqa: DJ012 — `from_db` override is the entry point
        is_create = self.pk is None
        before = getattr(self, "_audit_loaded_state", None)
        super().save(*args, **kwargs)
        after = serialize_instance(self, exclude=self.audit_exclude)
        record_event(
            f"{self._meta.label_lower}.{'create' if is_create else 'update'}",
            resource_type=self._meta.label_lower,
            resource_id=self._resource_id(),
            before=None if is_create else before,
            after=after,
            tenant_id=getattr(self, "tenant_id", None),
            organization_node_id=getattr(self, "organization_node_id", None),
        )
        self._audit_loaded_state = after

    def delete(self, *args, **kwargs):
        before = serialize_instance(self, exclude=self.audit_exclude)
        rid = self._resource_id()
        tenant_id = getattr(self, "tenant_id", None)
        node_id = getattr(self, "organization_node_id", None)
        result = super().delete(*args, **kwargs)
        record_event(
            f"{self._meta.label_lower}.delete",
            resource_type=self._meta.label_lower,
            resource_id=rid,
            before=before,
            after=None,
            tenant_id=tenant_id,
            organization_node_id=node_id,
        )
        return result

    def _resource_id(self) -> str:
        return str(self.pk) if self.pk is not None else ""
