"""HR signals:
- post_save User → auto-create Employee stub (so request.user always has .employee)
- pre_save/post_save User → re-index face in Rekognition when the avatar changes
- post_save Employee → bridge to Contact (canonical Person table)
"""

from django.conf import settings
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.utils import timezone

from apps.core.background import run_in_background

from .models import Employee
from .services.face_enrollment import sync_face_from_avatar


def _generate_employee_id(user) -> str:
    suffix = str(user.id).replace("-", "")[-8:].upper()
    return f"EMP-{suffix}"


def ensure_employee_stub(user):
    """Create the Employee stub for a user, if they have an org membership and
    don't already have one. Safe to call multiple times.

    Split out from the post_save signal below because organization is
    required on Employee, but org membership is sometimes attached to a new
    user *after* their first save (e.g. create_superuser creates the org
    itself only after create_user() returns) — callers on those paths must
    call this again once membership exists.
    """
    if user.organization_role == "donor":
        return
    if Employee.objects.filter(user=user).exists():
        return
    organization = user.organizations.first()
    if organization is None:
        return
    Employee.objects.create(
        organization=organization,
        user=user,
        employee_id=_generate_employee_id(user),
        position="Unassigned",
        department="Unassigned",
        hire_date=timezone.now().date(),
    )


@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def ensure_employee_for_user(sender, instance, created, **kwargs):
    if not created:
        return
    ensure_employee_stub(instance)


@receiver(pre_save, sender=settings.AUTH_USER_MODEL)
def stash_previous_avatar(sender, instance, **kwargs):
    """Record the stored avatar so post_save can tell whether it changed.

    Avatar writes reach the DB through four different mechanisms (DRF
    serializer save, raw save(update_fields=...), FieldFile.save() in the photo
    sync command, and the admin form), so post_save on User is the only choke
    point that catches them all — but it needs the old value to avoid
    re-indexing on every unrelated user save.
    """
    if instance.pk is None:
        instance._previous_avatar = None
        return
    previous = sender.objects.filter(pk=instance.pk).values_list("avatar", flat=True).first()
    instance._previous_avatar = previous or ""


@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def sync_face_on_avatar_change(sender, instance, created, **kwargs):
    if not getattr(settings, "REKOGNITION_AVATAR_ENROLLMENT", True):
        return
    previous = getattr(instance, "_previous_avatar", None)
    current = instance.avatar.name or ""
    if not created and previous == current:
        return
    if created and not current:
        return
    run_in_background(sync_face_from_avatar, instance)


@receiver(post_save, sender=Employee)
def ensure_contact_for_employee(sender, instance, created, **kwargs):
    from apps.contacts.models import Contact

    user = instance.user
    if not user:
        return

    contact = Contact.objects.filter(user=user).first()
    if contact is None:
        contact = Contact.objects.filter(email__iexact=user.email).first() if user.email else None
        if contact is not None and contact.user_id is None:
            contact.user = user
            contact.save(update_fields=["user"])

    if contact is None:
        organization = user.organizations.first()
        if organization is None:
            # User has no org membership yet (e.g. mid-creation in
            # create_superuser, before its org/membership is attached).
            # Contact requires organization — skip for now; nothing else
            # currently retries this, but Employee.save() re-fires this
            # signal on any future update to the same instance.
            return
        full_name = user.get_full_name() or user.username or user.email
        Contact.objects.create(
            organization=organization,
            user=user,
            name=full_name,
            email=user.email or "",
            phone=getattr(user, "phone", "") or "",
            type="individual",
            position=instance.position or "",
        )
