from django.db.models.signals import pre_save, post_save, post_delete
from django.dispatch import receiver
from django.forms.models import model_to_dict

from .models import PatientProfile, Encounter, VitalSign, Diagnosis, Prescription, LabResult, AuditEvent
from .utils import get_actor, get_client_meta

TRACKED_MODELS = (PatientProfile, Encounter, VitalSign, Diagnosis, Prescription, LabResult)

def _diff(old, new):
    changes = {}
    for k, v in new.items():
        if k in old and old[k] != v:
            changes[k] = {"from": old[k], "to": v}
    return changes

@receiver(pre_save)
def capture_pre_save(sender, instance, **kwargs):
    if sender not in TRACKED_MODELS:
        return
    if not instance.pk:
        instance.__pre_save_snapshot__ = None
        return
    try:
        old = sender.objects.get(pk=instance.pk)
        instance.__pre_save_snapshot__ = model_to_dict(old)
    except sender.DoesNotExist:
        instance.__pre_save_snapshot__ = None

@receiver(post_save)
def audit_post_save(sender, instance, created, **kwargs):
    if sender not in TRACKED_MODELS:
        return
    actor = get_actor()
    meta = get_client_meta()
    model_label = f"{sender._meta.app_label}.{sender.__name__}"
    if created:
        action = "create"
        changes = {"created": model_to_dict(instance)}
    else:
        old = getattr(instance, "__pre_save_snapshot__", None) or {}
        new = model_to_dict(instance)
        changes = _diff(old, new)
        if not changes:
            return
        action = "update"
    AuditEvent.objects.create(
        actor=actor,
        action=action,
        model_label=model_label,
        object_id=str(instance.pk),
        object_repr=str(instance)[:250],
        changes=changes,
        ip_address=meta.get("ip"),
        user_agent=meta.get("ua"),
    )

@receiver(post_delete)
def audit_post_delete(sender, instance, **kwargs):
    if sender not in TRACKED_MODELS:
        return
    actor = get_actor()
    meta = get_client_meta()
    model_label = f"{sender._meta.app_label}.{sender.__name__}"
    AuditEvent.objects.create(
        actor=actor,
        action="delete",
        model_label=model_label,
        object_id=str(instance.pk),
        object_repr=str(instance)[:250],
        changes={},
        ip_address=meta.get("ip"),
        user_agent=meta.get("ua"),
    )


from django.contrib.auth.signals import user_logged_in, user_logged_out

@receiver(user_logged_in)
def audit_login(sender, request, user, **kwargs):
    meta = get_client_meta()
    AuditEvent.objects.create(
        actor=user,
        action="login",
        model_label="auth.User",
        object_id=str(user.pk),
        object_repr=str(user)[:250],
        changes={},
        ip_address=meta.get("ip"),
        user_agent=meta.get("ua"),
    )

@receiver(user_logged_out)
def audit_logout(sender, request, user, **kwargs):
    meta = get_client_meta()
    AuditEvent.objects.create(
        actor=user if user and getattr(user,"is_authenticated",False) else None,
        action="logout",
        model_label="auth.User",
        object_id=str(getattr(user,"pk","")) if user else "",
        object_repr=str(user)[:250] if user else "",
        changes={},
        ip_address=meta.get("ip"),
        user_agent=meta.get("ua"),
    )
