"""Diff + persistence helpers behind `simorgh.core.audit.record_event`."""

from __future__ import annotations

from collections.abc import Iterable, Mapping
from typing import Any

from django.db import models

REDACTED = "***"
DEFAULT_REDACT_FIELDS: frozenset[str] = frozenset(
    {
        "password",
        "password_hash",
        "secret",
        "token",
        "api_key",
        "private_key",
    }
)


def serialize_instance(
    instance: models.Model,
    *,
    exclude: Iterable[str] = (),
    redact: Iterable[str] = DEFAULT_REDACT_FIELDS,
) -> dict[str, Any]:
    """Cheap snapshot of a model instance for audit `before`/`after` payloads.

    Only includes concrete, non-relational fields by default. FKs are kept
    as their `<name>_id` form. Datetimes/UUIDs are stringified.
    """
    excluded = set(exclude)
    redacted = set(redact)
    out: dict[str, Any] = {}
    for field in instance._meta.concrete_fields:
        name = field.attname  # gives FK as `<name>_id`
        if name in excluded:
            continue
        value = getattr(instance, name, None)
        if name in redacted or field.name in redacted:
            value = REDACTED if value not in (None, "") else value
        elif value is not None and not isinstance(value, str | int | float | bool | dict | list):
            value = str(value)
        out[name] = value
    return out


def diff_dicts(
    before: Mapping[str, Any] | None,
    after: Mapping[str, Any] | None,
) -> dict[str, dict[str, Any]]:
    """Return ``{field: {"before": ..., "after": ...}}`` for changed keys only."""
    before = before or {}
    after = after or {}
    keys = set(before) | set(after)
    changed: dict[str, dict[str, Any]] = {}
    for k in keys:
        b = before.get(k)
        a = after.get(k)
        if b != a:
            changed[k] = {"before": b, "after": a}
    return changed
