"""Immutable, structured business audit helpers."""
from datetime import date, datetime
from decimal import Decimal
from uuid import UUID

from django.db import models

from account.middleware import get_current_request, get_current_user
from account.models import UserAuditLog


SENSITIVE_FIELDS = {"password", "otp_secret", "mobile_otp"}
REFERENCE_FIELDS = (
    "sale_number", "purchase_number", "return_number", "payment_number",
    "transfer_number", "adjustment_number", "journal_number", "reference",
    "code", "name",
)


def _json_value(value):
    if isinstance(value, (datetime, date)):
        return value.isoformat()
    if isinstance(value, (Decimal, UUID)):
        return str(value)
    if isinstance(value, (bytes, bytearray)):
        return "<binary>"
    if value is None or isinstance(value, (str, int, float, bool)):
        return value
    return str(value)


def model_snapshot(instance):
    if not instance:
        return {}
    result = {}
    for field in instance._meta.concrete_fields:
        if field.name in SENSITIVE_FIELDS:
            continue
        key = field.attname if isinstance(field, models.ForeignKey) else field.name
        result[key] = _json_value(getattr(instance, key, None))
    return result


def object_reference(instance):
    for field in REFERENCE_FIELDS:
        value = getattr(instance, field, None)
        if value not in (None, ""):
            return str(value)
    return str(instance.pk or "")


def object_branch_id(instance):
    if getattr(instance, "shop_id", None):
        return instance.shop_id
    warehouse = getattr(instance, "warehouse", None)
    if warehouse:
        return warehouse.shop_id
    account = getattr(instance, "from_account", None) or getattr(instance, "account", None)
    if account:
        return account.shop_id
    return None


def request_ip(request=None):
    request = request or get_current_request()
    if not request:
        return None
    forwarded = request.META.get("HTTP_X_FORWARDED_FOR", "")
    return forwarded.split(",")[0].strip() if forwarded else request.META.get("REMOTE_ADDR")


def audit_event(action, instance=None, *, actor=None, before=None, after=None,
                reference=None, branch_id=None, target_user=None, request=None):
    actor = actor or get_current_user()
    after = model_snapshot(instance) if after is None and instance is not None else (after or {})
    return UserAuditLog.objects.create(
        user=target_user,
        changed_by=actor,
        action=action,
        changed_fields={},
        model_name=instance._meta.label if instance is not None else "",
        object_id=str(instance.pk) if instance is not None and instance.pk is not None else "",
        reference=reference if reference is not None else (object_reference(instance) if instance else ""),
        branch_id=branch_id if branch_id is not None else (object_branch_id(instance) if instance else None),
        before_data=before or {},
        after_data=after,
        ip_address=request_ip(request),
    )
