# audit/signals.py
from django.db import models
from django.db.models.signals import pre_save, post_save, post_delete, pre_delete
from django.dispatch import receiver
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.contenttypes.models import ContentType

from account.models import User, UserAuditLog
from account.middleware import get_current_user
from account.audit import audit_event, model_snapshot

# from api.models.notifications.notifications import Notification

import logging

logger = logging.getLogger(__name__)


# Helper function to get staff users
def get_staff_users():
    return User.objects.filter(is_staff=True)


@receiver(pre_save, sender=User)
def track_user_changes(sender, instance, **kwargs):
    """
    Stores the previous state of the User instance before it's saved.
    This is used to detect changes in the post_save signal.
    """
    if not instance.pk:
        # New user being created; no changes to track
        instance._pre_save_instance = None
        return

    try:
        previous = sender.objects.get(pk=instance.pk)
        instance._pre_save_instance = previous
    except sender.DoesNotExist:
        instance._pre_save_instance = None


@receiver(post_save, sender=User)
def log_user_changes(sender, instance, created, **kwargs):
    """
    Logs changes to the User instance. Does not log creation events.
    """
    if created:
        # Skip logging for user creation
        return

    previous = getattr(instance, "_pre_save_instance", None)
    if not previous:
        return

    changed_fields = {}
    # List of fields to monitor; adjust as needed
    monitored_fields = [
        "first_name",
        "last_name",
        "email",
        "phone_number",
        "username",
        "address",
        "shop_name",
        "shop_address",
        "is_seller",
        "verified",
        "gender",
        "tazkira_number",
        "is_mobile_verified",
        "is_email_verified",
        "description",
        "work_license_copy",
        "image",
        "tazkira_copy",
        "allowed_tabs",
        "system_role",
        "action_permissions",
        "is_staff",
        "is_superuser",
        # Add other fields you want to monitor
    ]

    for field in monitored_fields:
        try:
            field_obj = sender._meta.get_field(field)
        except models.FieldDoesNotExist:
            continue  # Skip if the field does not exist

        old_value = getattr(previous, field)
        new_value = getattr(instance, field)

        if isinstance(field_obj, (models.FileField, models.ImageField)):
            old_value = old_value.name if old_value else None
            new_value = new_value.name if new_value else None
        else:
            if isinstance(old_value, models.Model):
                old_value = str(old_value)
            if isinstance(new_value, models.Model):
                new_value = str(new_value)

        if old_value != new_value:
            changed_fields[field] = {"old": old_value, "new": new_value}

    permission_fields = {"allowed_tabs", "system_role", "action_permissions", "is_staff", "is_superuser"}
    is_permission_change = bool(permission_fields.intersection(changed_fields))
    if changed_fields and ((instance.is_seller and instance.verified) or is_permission_change):
        # Get the acting user from middleware
        acting_user = get_current_user()
        changed_by = (
            acting_user if acting_user else instance
        )  # Fallback to the user themselves

        # Log the changes
        UserAuditLog.objects.create(
            user=instance,
            changed_fields=changed_fields,
            changed_by=changed_by,
            action="update",
        )
        if is_permission_change:
            audit_event(
                "PERMISSION_CHANGE", instance, actor=changed_by, target_user=instance,
                before={key: values["old"] for key, values in changed_fields.items()},
                after={key: values["new"] for key, values in changed_fields.items()},
                reference=instance.username or str(instance.pk),
            )
        logger.info(
            f"UserAuditLog created for {instance.username} by {changed_by.username if changed_by else 'Unknown'}"
        )


@receiver(pre_delete, sender=User)
def log_user_deletion(sender, instance, **kwargs):
    """
    Logs the deletion of a User instance before it is removed from the database.
    """
    if instance.is_seller and instance.verified:
        # Get the acting user from middleware (if available)
        acting_user = get_current_user()
        changed_by = (
            acting_user if acting_user else instance
        )  # Fallback to the user themselves

        # Log the deletion before the user is deleted
        UserAuditLog.objects.create(
            user=instance,
            changed_fields={"__deleted__": "User account deleted."},
            changed_by=changed_by,
            action="delete",
        )
        logger.info(
            f"UserAuditLog (delete) created for {instance.username} by {changed_by.username if changed_by else 'Unknown'}."
        )


# @receiver(post_save, sender=UserAuditLog)
# def create_user_audit_log_notification(sender, instance, created, **kwargs):
#     if created:
#         # Get only the staff users
#         staff_users = get_staff_users()
#         actor = instance.changed_by if instance.changed_by else instance.user
#         verb = f"{actor.username} {instance.get_action_display()} {instance.user.username}'s profile."
#         notification_type = 'audit_log'
#         target_content_type = ContentType.objects.get_for_model(instance.user)
#         target_object_id = instance.user.id
#         action_object_content_type = ContentType.objects.get_for_model(UserAuditLog)
#         action_object_object_id = instance.id

#         # Create a notification for each staff user
#         for staff in staff_users:
#             Notification.objects.create(
#                 recipient=staff,
#                 actor=actor,
#                 verb=verb,
#                 notification_type=notification_type,
#                 target_content_type=target_content_type,
#                 target_object_id=target_object_id,
#                 action_object_content_type=action_object_content_type,
#                 action_object_object_id=action_object_object_id,
#                 is_read=False,
#             )
#             logger.info(f"Created 'User Audit Log' notification for staff {staff.username} on UserAuditLog for {instance.user.username}.")


AUDITED_BUSINESS_MODELS = {
    "Sale", "Purchase", "SaleReturn", "PurchaseReturn", "CustomerPayment",
    "SupplierPayment", "StockAdjustment", "WarehouseTransfer", "AccountTransfer",
    "JournalEntry", "Expense", "ExchangeRate", "FinancialOpeningBalance",
}


def _document_status(instance):
    if hasattr(instance, "document_status"):
        return instance.document_status
    return getattr(instance, "status", None)


@receiver(pre_save)
def capture_business_state(sender, instance, raw=False, **kwargs):
    if raw or sender._meta.app_label != "shop_management" or sender.__name__ not in AUDITED_BUSINESS_MODELS:
        return
    if instance.pk:
        previous = sender.objects.filter(pk=instance.pk).first()
        instance._audit_before_snapshot = model_snapshot(previous)
        instance._audit_before_status = _document_status(previous)
    else:
        instance._audit_before_snapshot = {}
        instance._audit_before_status = None


@receiver(post_save)
def log_business_state(sender, instance, created, raw=False, **kwargs):
    if raw or sender._meta.app_label != "shop_management" or sender.__name__ not in AUDITED_BUSINESS_MODELS:
        return
    before = getattr(instance, "_audit_before_snapshot", {})
    after = model_snapshot(instance)
    if not created and before == after:
        return
    old_status = getattr(instance, "_audit_before_status", None)
    new_status = _document_status(instance)
    name = sender.__name__
    action = "CREATE" if created else "UPDATE"
    if name == "ExchangeRate":
        action = "EXCHANGE_RATE_CHANGE"
    elif old_status != new_status and new_status:
        if new_status == "VOID":
            action = "VOID"
        elif name in {"CustomerPayment", "SupplierPayment"} and new_status == "POSTED":
            action = "PAYMENT"
        elif name in {"SaleReturn", "PurchaseReturn"} and new_status == "POSTED":
            action = "RETURN"
        elif name == "StockAdjustment" and new_status == "POSTED":
            action = "ADJUSTMENT"
        elif name == "WarehouseTransfer" and new_status in {"DISPATCHED", "RECEIVED"}:
            action = "TRANSFER"
        elif new_status == "POSTED":
            action = "POST"
    actor = get_current_user() or getattr(instance, "posted_by", None) or getattr(instance, "voided_by", None) or getattr(instance, "created_by", None)
    audit_event(action, instance, actor=actor, before=before, after=after)
