import json
from django.db.models.signals import pre_save, post_save, post_delete, pre_delete
from django.dispatch import receiver
from django.utils import timezone
from django.core.serializers.json import DjangoJSONEncoder
from .models import Client, ClientLog, User, Company
from .utils import serialize_instance, build_changes, serialize_company_with_related
from django.http import JsonResponse
from django.db.models import Value
from django.db.models.functions import Concat
from users.middleware import get_current_user
from django.db import models


# --- Helpers ---
def normalize_value(value):
    if value in [None, "", "None"]:
        return None
    if hasattr(value, "_meta") and hasattr(value, "pk"):
        if value._meta.model_name == "user":
            return value.username
        return value.pk
    if hasattr(value, "name"):
        return value.name or None
    return value

# def build_changes(old_data, new_data):
#     changes = {}
#     for field in new_data.keys():
#         old_val = normalize_value(old_data.get(field)) if old_data else None
#         new_val = normalize_value(new_data.get(field))
#         if old_val != new_val:
#             changes[field] = {"old": old_val, "new": new_val}
#     return changes

# def build_changes(old_data, new_data):
#     """Compare two dicts and return only real changes. Handles None for new_data (deletion)."""
#     changes = {}
#     changes_1 = {}

#     # Ensure both are dicts
#     old_data = old_data or {}
#     new_data = new_data or {}

#     # Combine keys from both old and new
#     all_keys = set(old_data.keys()).union(new_data.keys())

#     for field in all_keys:
#         old_val = normalize_value(old_data.get(field))
#         new_val = normalize_value(new_data.get(field))

#         if old_val != new_val:
#             changes[field] = {"old": old_val, "new": new_val}

#     return changes

# def build_changes(old_data, new_data):
#     """Compare two dicts and return only real changes. Handles None for new_data (deletion)."""
#     changes = {}
#     fk_fields = ["country_code","residential_country", "residential_state", "residential_city", "ref_preferred_airline","ref_airline","account_concerned_person_country_code","company_country", "company_state", "company_city","travel_concerned_person_country_code"]
    
#     old_data = old_data or {}
#     new_data = new_data or {}
#     all_keys = set(old_data.keys()).union(new_data.keys())

#     for field in all_keys:
#         # Use display value for FK fields
#         if field in fk_fields and f"{field}_name" in old_data and f"{field}_name" in new_data:
#             old_val = normalize_value(old_data.get(f"{field}_name"))
#             new_val = normalize_value(new_data.get(f"{field}_name"))
#         else:
#             old_val = normalize_value(old_data.get(field))
#             new_val = normalize_value(new_data.get(field))

#         if old_val != new_val:
#             changes[field] = {"old": old_val, "new": new_val}

#     return changes

def build_changes(old_data, new_data):
    """Compare two dicts and return changes, including updated_by/update_by only if other changes exist."""
    changes = {}
    temp_changes = {}  # Temporary storage for non-updated_by changes
    fk_fields = ["country_code", "residential_country", "residential_state", "residential_city", 
                 "ref_preferred_airline", "ref_airline", "account_concerned_person_country_code", 
                 "company_country", "company_state", "company_city", "travel_concerned_person_country_code"]
    updated_by_fields = ["updated_by", "update_by"]  # Fields to handle with usernames

    old_data = old_data or {}
    new_data = new_data or {}
    all_keys = set(old_data.keys()).union(new_data.keys())

    # First, process non-updated_by fields to detect changes
    for field in all_keys:
        if field in updated_by_fields:
            continue  # Skip updated_by/update_by for now
        elif field in fk_fields and f"{field}_name" in old_data and f"{field}_name" in new_data:
            # Use display value for FK fields
            old_val = normalize_value(old_data.get(f"{field}_name"))
            new_val = normalize_value(new_data.get(f"{field}_name"))
            if old_val != new_val:
                temp_changes[field] = {"old": old_val, "new": new_val}
        else:
            # Use regular field values
            old_val = normalize_value(old_data.get(field))
            new_val = normalize_value(new_data.get(field))
            if old_val != new_val:
                temp_changes[field] = {"old": old_val, "new": new_val}

    # If there are changes, include updated_by/update_by (prioritizing updated_by)
    if temp_changes:
        changes = temp_changes  # Add non-updated_by changes
        # Check for updated_by first, then update_by
        selected_field = "updated_by" if "updated_by" in all_keys else "update_by" if "update_by" in all_keys else None
        if selected_field:
            old_val = get_username_from_id(old_data.get(selected_field)) if old_data.get(selected_field) else None
            new_val = get_username_from_id(new_data.get(selected_field)) if new_data.get(selected_field) else None
            changes[selected_field] = {"old": old_val, "new": new_val}

    return changes

def get_username_from_id(user_id):
    """Fetch username from User model by ID or username."""
    if not user_id:
        return None

    try:
        # Try fetching by ID if user_id is numeric
        if isinstance(user_id, (int, str)) and str(user_id).isdigit():
            user = User.objects.get(id=int(user_id))
            return user.username
    except User.DoesNotExist:
        pass  # Proceed to try by username
    except Exception as e:
        print(f"Error fetching user by ID {user_id}: {e}")
        # Continue to try by username

    try:
        # Try fetching by username
        user = User.objects.get(username=user_id)
        return user.username
    except User.DoesNotExist:
        print(f"No user found for ID or username: {user_id}")
        return None
    except Exception as e:
        print(f"Error fetching user by username {user_id}: {e}")
        return None
    
def normalize_value(value):
    """Helper function to normalize values."""
    if value is None:
        return None
    return str(value).strip()

# --- Pre-save ---
# @receiver(pre_save, sender=Client)
# def cache_old_client_data(sender, instance, **kwargs):
#     if instance.pk:
#         try:
#             old_instance = sender.objects.get(pk=instance.pk)
#             instance._old_data = serialize_instance(old_instance, exclude_fields=["id"])
#             instance._old_status = old_instance.client_status
#         except sender.DoesNotExist:
#             instance._old_data = None
#             instance._old_status = None
#     else:
#         instance._old_data = None
#         instance._old_status = None

@receiver(pre_save, sender=Client)
def cache_old_client_data(sender, instance, **kwargs):
    if instance.pk and not kwargs.get('raw', False):  # Only for updates, skip for fixtures
        try:
            old_instance = sender.objects.get(pk=instance.pk)
            instance._old_data = serialize_instance(old_instance, exclude_fields=["id"])
            instance._old_status = old_instance.client_status
            print(f"Set _old_data and _old_status for client {instance.pk}")
        except sender.DoesNotExist:
            print(f"Client {instance.pk} not found in pre_save, setting _old_data to None")
            instance._old_data = None
            instance._old_status = None
    else:
        # Explicitly clear _old_data and _old_status for new clients
        if hasattr(instance, '_old_data') or hasattr(instance, '_old_status'):
            print(f"Unexpected _old_data or _old_status found during creation for client {instance.pk}")
            if hasattr(instance, '_old_data'):
                delattr(instance, '_old_data')
            if hasattr(instance, '_old_status'):
                delattr(instance, '_old_status')
        instance._old_data = None
        instance._old_status = None
        print(f"Cleared _old_data and _old_status for new client (pk={instance.pk})")

# --- Post-save: create/update/status logging ---


# @receiver(post_save, sender=Client)
# def log_client_activity(sender, instance, created, **kwargs):
#     client_name = f"{instance.client_first_name or ''} {instance.client_last_name or ''}".strip()

#     # --- CREATED ---
#     if created:
#         new_data = serialize_instance(instance, exclude_fields=["client_id"])
#         new_data["client_name"] = client_name

#         ClientLog.objects.create(
#             ref_client_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="CLIENT CREATED",
#             changed_data=json.loads(json.dumps(build_changes(None, new_data), cls=DjangoJSONEncoder)),
#             performed_by=instance.created_by,
#             performed_at=timezone.now(),
#         )
#         return  # stop here: no update/status logs

#     # --- STATUS CHANGE ---
#     old_status = getattr(instance, "_old_status", None)
#     new_status = instance.client_status
#     status_changed = old_status != new_status

#     # --- UPDATED (only if NOT a pure status change) ---
#     if not status_changed:  # skip UPDATED log when status changed
#         old_data = getattr(instance, "_old_data", None)
#         if old_data:
#             old_data_filtered = {
#                 k: v for k, v in old_data.items()
#                 if k not in ["client_status", "created_at", "updated_at"]
#             }
#             new_data_filtered = serialize_instance(
#                 instance, exclude_fields=["id", "client_status", "created_at", "updated_at"]
#             )


#             changes = build_changes(old_data_filtered, new_data_filtered)
          
#             log_data = {
#                 "description": changes,
#                 "client_name": client_name
#             }
#             if changes:
#                 ClientLog.objects.create(
#                     ref_client_id=instance,
#                     ref_table_name=instance._meta.db_table,
#                     ref_id=instance.pk,
#                     action_type="CLIENT UPDATED",
#                     changed_data=json.loads(json.dumps(log_data ,cls=DjangoJSONEncoder)),
#                     performed_by=instance.updated_by,
#                     performed_at=timezone.now(),
#                 )

#     # --- STATUS UPDATE ---
#     if status_changed:
#         ClientLog.objects.create(
#             ref_client_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="CLIENT - STATUS UPDATED",
#             changed_data=json.loads(json.dumps({
#                 "client_name": {
#                     "old": f"{instance.client_first_name} {instance.client_last_name}",
#                     "new": client_name
#                 },
#                 "client_status": {"old": old_status, "new": new_status}
#             }, cls=DjangoJSONEncoder)),
#             performed_by=instance.updated_by,
#             performed_at=timezone.now(),
#         )


# -------------------------------------------------------------------------------------------------------------

# @receiver(post_save, sender=Client)
# def log_client_activity(sender, instance, created, **kwargs):
#     client_name = f"{instance.client_first_name or ''} {instance.client_last_name or ''}".strip()
#     residential_city = instance.residential_city
#     residential_state = instance.residential_state

#     # --- CREATED ---
#     if created:
#         new_data = serialize_instance(instance, exclude_fields=["client_id"])
#         new_data["client_name"] = client_name
#         new_data["residential_city"] = residential_city
#         new_data["residential_state"] = residential_state

#         ClientLog.objects.create(
#             ref_client_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="CLIENT CREATED",
#             changed_data=json.loads(json.dumps(build_changes(None, new_data), cls=DjangoJSONEncoder)),
#             performed_by=instance.created_by,
#             performed_at=timezone.now(),
#         )
#         return  # stop here: no update/status logs

#     # --- STATUS CHANGE ---
#     old_status = getattr(instance, "_old_status", None)
#     new_status = instance.client_status
#     status_changed = old_status != new_status

#     # --- UPDATED (only if NOT a pure status change) ---
#     if not status_changed:  # skip UPDATED log when status changed
#         old_data = getattr(instance, "_old_data", None)
#         if old_data:
#             old_data_filtered = {
#                 k: v for k, v in old_data.items()
#                 if k not in ["client_status", "created_at", "updated_at","updated_by"]
#             }
#             new_data_filtered = serialize_instance(
#                 instance, exclude_fields=["id", "client_status", "created_at", "updated_at","updated_by"]
#             )


#             changes = build_changes(old_data_filtered, new_data_filtered)
          
#             log_data = {
#                 "description": changes,
#                 "client_name": client_name
#             }
#             if changes:
#                 ClientLog.objects.create(
#                     ref_client_id=instance,
#                     ref_table_name=instance._meta.db_table,
#                     ref_id=instance.pk,
#                     action_type="CLIENT UPDATED",
#                     changed_data=json.loads(json.dumps(log_data ,cls=DjangoJSONEncoder)),
#                     performed_by=instance.updated_by,
#                     performed_at=timezone.now(),
#                 )

#     # --- STATUS UPDATE ---
#     if status_changed:
#         ClientLog.objects.create(
#             ref_client_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="CLIENT - STATUS UPDATED",
#             changed_data=json.loads(json.dumps({
#                 "client_name": {
#                     "old": f"{instance.client_first_name} {instance.client_last_name}",
#                     "new": client_name
#                 },
#                 "client_status": {"old": old_status, "new": new_status}
#             }, cls=DjangoJSONEncoder)),
#             performed_by=instance.updated_by,
#             performed_at=timezone.now(),
#         )

# @receiver(post_save, sender=Client)
# def log_client_activity(sender, instance, created, **kwargs):
#     print(f"Signal triggered: client_id={instance.pk}, created={created}, _old_data={hasattr(instance, '_old_data')}")

#     # Extract common data
#     client_name = f"{instance.client_first_name or ''} {instance.client_last_name or ''}".strip()
#     residential_city = instance.residential_city
#     residential_state = instance.residential_state

#     # --- CREATED ---
#     if created:
#         print(f"Creating log for new client: {instance.pk}")
#         new_data = serialize_instance(instance, exclude_fields=["client_id"])
#         new_data["client_name"] = client_name
#         new_data["residential_city"] = residential_city
#         new_data["residential_state"] = residential_state

#         ClientLog.objects.create(
#             ref_client_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="CLIENT CREATED",
#             changed_data=json.loads(json.dumps(build_changes(None, new_data), cls=DjangoJSONEncoder)),
#             performed_by=instance.created_by,
#             performed_at=timezone.now(),
#         )
#         print(f"Created CLIENT CREATED log for client: {instance.pk}")
#         return  # Explicitly stop execution to prevent update/status logs

#     # --- Only proceed with update/status logic if NOT created ---
#     if not created:
#         print(f"Processing update for client: {instance.pk}")
#         # --- STATUS CHANGE ---
#         old_status = getattr(instance, "_old_status", None)
#         new_status = instance.client_status
#         status_changed = old_status != new_status

#         # --- UPDATED (only if NOT a pure status change) ---
#         if not status_changed:
#             old_data = getattr(instance, "_old_data", None)
#             if old_data:
#                 print(f"Old data found for client {instance.pk}: {old_data}")
#                 old_data_filtered = {
#                     k: v for k, v in old_data.items()
#                     if k not in ["client_status", "created_at", "updated_at", "updated_by"]
#                 }
#                 new_data_filtered = serialize_instance(
#                     instance, exclude_fields=["id", "client_status", "created_at", "updated_at", "updated_by"]
#                 )

#                 changes = build_changes(old_data_filtered, new_data_filtered)
                
#                 log_data = {
#                     "description": changes,
#                     "client_name": client_name
#                 }
#                 if changes:
#                     print(f"Creating CLIENT UPDATED log for client {instance.pk}: {changes}")
#                     ClientLog.objects.create(
#                         ref_client_id=instance,
#                         ref_table_name=instance._meta.db_table,
#                         ref_id=instance.pk,
#                         action_type="CLIENT UPDATED",
#                         changed_data=json.loads(json.dumps(log_data, cls=DjangoJSONEncoder)),
#                         performed_by=instance.updated_by,
#                         performed_at=timezone.now(),
#                     )
#                 else:
#                     print(f"No changes detected for client {instance.pk}")
#             else:
#                 print(f"No old_data found for client {instance.pk}, skipping update log")

#         # --- STATUS UPDATE ---
#         if status_changed:
#             print(f"Creating CLIENT - STATUS UPDATED log for client {instance.pk}")
#             ClientLog.objects.create(
#                 ref_client_id=instance,
#                 ref_table_name=instance._meta.db_table,
#                 ref_id=instance.pk,
#                 action_type="CLIENT - STATUS UPDATED",
#                 changed_data=json.loads(json.dumps({
#                     "client_name": {
#                         "old": f"{instance.client_first_name} {instance.client_last_name}",
#                         "new": client_name
#                     },
#                     "client_status": {"old": old_status, "new": new_status}
#                 }, cls=DjangoJSONEncoder)),
#                 performed_by=instance.updated_by,
#                 performed_at=timezone.now(),
#             )
#     else:
#         print(f"Skipping update/status logic for client {instance.pk} as it was just created")

# @receiver(post_save, sender=Client, dispatch_uid="log_client_activity")
# def log_client_activity(sender, instance, created, **kwargs):
#     print(f"Signal triggered: client_id={instance.pk}, created={created}, _old_data={hasattr(instance, '_old_data')}, _old_status={hasattr(instance, '_old_status')}")

#     # Extract common data
#     client_name = f"{instance.client_first_name or ''} {instance.client_last_name or ''}".strip()
#     residential_city = instance.residential_city
#     residential_state = instance.residential_state

#     # --- CREATED ---
#     if created:
#         print(f"Creating log for new client: {instance.pk}")
#         new_data = serialize_instance(instance, exclude_fields=["client_id"])
#         new_data["client_name"] = client_name
#         new_data["residential_city"] = residential_city
#         new_data["residential_state"] = residential_state

#         ClientLog.objects.create(
#             ref_client_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="CLIENT CREATED",
#             changed_data=json.loads(json.dumps(build_changes(None, new_data), cls=DjangoJSONEncoder)),
#             performed_by=instance.created_by,
#             performed_at=timezone.now(),
#         )
#         print(f"Created CLIENT CREATED log for client: {instance.pk}")
#         # Clear _old_data to prevent misuse
#         if hasattr(instance, '_old_data'):
#             print(f"_old_data unexpectedly set during creation for client {instance.pk}")
#             delattr(instance, '_old_data')
#         if hasattr(instance, '_old_status'):
#             print(f"_old_status unexpectedly set during creation for client {instance.pk}")
#             delattr(instance, '_old_status')
#         return  # Stop execution

#     # --- Only proceed with update/status logic for existing instances ---
#     if not created and instance.pk:
#         print(f"Processing update for client: {instance.pk}")
#         # --- STATUS CHANGE ---
#         old_status = getattr(instance, "_old_status", None)
#         new_status = instance.client_status
#         status_changed = old_status != new_status

#         # --- UPDATED (only if NOT a pure status change) ---
#         if not status_changed:
#             old_data = getattr(instance, "_old_data", None)
#             if old_data:
#                 print(f"Old data found for client {instance.pk}: {old_data}")
#                 old_data_filtered = {
#                     k: v for k, v in old_data.items()
#                     if k not in ["client_status", "created_at", "updated_at", "updated_by"]
#                 }
#                 new_data_filtered = serialize_instance(
#                     instance, exclude_fields=["id", "client_status", "created_at", "updated_at", "updated_by"]
#                 )

#                 changes = build_changes(old_data_filtered, new_data_filtered)
                
#                 log_data = {
#                     "description": changes,
#                     "client_name": client_name
#                 }
#                 if changes:
#                     print(f"Creating CLIENT UPDATED log for client {instance.pk}: {changes}")
#                     ClientLog.objects.create(
#                         ref_client_id=instance,
#                         ref_table_name=instance._meta.db_table,
#                         ref_id=instance.pk,
#                         action_type="CLIENT UPDATED",
#                         changed_data=json.loads(json.dumps(log_data, cls=DjangoJSONEncoder)),
#                         performed_by=instance.updated_by,
#                         performed_at=timezone.now(),
#                     )
#                 else:
#                     print(f"No changes detected for client {instance.pk}")
#             else:
#                 print(f"No old_data found for client {instance.pk}, skipping update log")

#         # --- STATUS UPDATE ---
#         if status_changed:
#             print(f"Creating CLIENT - STATUS UPDATED log for client {instance.pk}")
#             ClientLog.objects.create(
#                 ref_client_id=instance,
#                 ref_table_name=instance._meta.db_table,
#                 ref_id=instance.pk,
#                 action_type="CLIENT - STATUS UPDATED",
#                 changed_data=json.loads(json.dumps({
#                     "client_name": {
#                         "old": f"{instance.client_first_name} {instance.client_last_name}",
#                         "new": client_name
#                     },
#                     "client_status": {"old": old_status, "new": new_status}
#                 }, cls=DjangoJSONEncoder)),
#                 performed_by=instance.updated_by,
#                 performed_at=timezone.now(),
#             )
#     else:
#         print(f"Skipping update/status logic for client {instance.pk}: created={created}, pk_exists={bool(instance.pk)}")

# @receiver(post_save, sender=Client, dispatch_uid="log_client_activity")
# def log_client_activity(sender, instance, created, **kwargs):
#     print(f"Signal triggered: client_id={instance.pk}, created={created}, _old_data={hasattr(instance, '_old_data')}, _old_status={hasattr(instance, '_old_status')}")

#     # Extract common data
#     client_name = f"{instance.client_first_name or ''} {instance.client_last_name or ''}".strip()
#     aadhaar_card_file = instance. aadhaar_card_file
#     print(" aadhaar_card_file:", aadhaar_card_file)
    

#     # --- CREATED ---
#     if created:
#         print(f"Creating log for new client: {instance.pk}")
#         new_data = serialize_instance(instance, exclude_fields=["client_id","residential_city","residential_state","aadhaar_card_file"])
#         residential_city = instance.residential_city
#         residential_state = instance.residential_state
      
#         new_data["client_name"] = client_name
#         new_data["aadhaar_card_file"] = aadhaar_card_file
#         new_data["residential_city"] = residential_city
#         new_data["residential_state"] = residential_state

#         try:
#             ClientLog.objects.create(
#                 ref_client_id=instance,
#                 ref_table_name=instance._meta.db_table,
#                 ref_id=instance.pk,
#                 action_type="CLIENT CREATED",
#                 changed_data=json.loads(json.dumps(build_changes(None, new_data), cls=DjangoJSONEncoder)),
#                 performed_by=instance.created_by,
#                 performed_at=timezone.now(),
#             )
#             print(f"Created CLIENT CREATED log for client, we are in client: {instance.pk}")
#         except Exception as e:
#             print(f"Error creating CLIENT CREATED log for client {instance.pk}: {e}")

#         # Clear _old_data to prevent misuse
#         if hasattr(instance, '_old_data'):
#             print(f"_old_data unexpectedly set during creation for client {instance.pk}")
#             delattr(instance, '_old_data')
#         if hasattr(instance, '_old_status'):
#             print(f"_old_status unexpectedly set during creation for client {instance.pk}")
#             delattr(instance, '_old_status')
#         return  # Stop execution

#     # --- Only proceed with update/status logic for existing instances ---
#     # if not created and instance.pk:
#     #     print(f"Processing update for client: {instance.pk}")
#         # --- STATUS CHANGE ---
#     old_status = getattr(instance, "_old_status", None)
#     new_status = instance.client_status
#     status_changed = old_status != new_status

#     # --- UPDATED (only if NOT a pure status change) ---
#     if not status_changed:
#         print("We are in in if not status change")
#         old_data = getattr(instance, "_old_data", None)
#         if old_data:
#             print(f"Old data found for client {instance.pk}: {old_data}")
#             old_data_filtered = {
#                 k: v for k, v in old_data.items()
#                 if k not in ["client_status", "created_at", "updated_at", "updated_by"]
#             }
#             new_data_filtered = serialize_instance(
#                 instance, exclude_fields=["id", "client_status", "created_at", "updated_at", "updated_by"]
#             )

#             changes = build_changes(old_data_filtered, new_data_filtered)
            
#             log_data = {
#                 "description": changes,
#                 "client_name": client_name
#             }
#             if changes:
#                 print("We are in in client update")
#                 print(f"Creating CLIENT UPDATED log for client {instance.pk}: {changes}")
#                 try:
#                     ClientLog.objects.create(
#                         ref_client_id=instance,
#                         ref_table_name=instance._meta.db_table,
#                         ref_id=instance.pk,
#                         action_type="CLIENT UPDATED",
#                         changed_data=json.loads(json.dumps(log_data, cls=DjangoJSONEncoder)),
#                         performed_by=instance.updated_by,
#                         performed_at=timezone.now(),
#                     )
#                 except Exception as e:
#                     print(f"Error creating CLIENT UPDATED log for client {instance.pk}: {e}")
#             else:
#                 print(f"No changes detected for client {instance.pk}")
#         else:
#             print(f"No old_data found for client {instance.pk}, skipping update log")

#     # --- STATUS UPDATE ---
#     if status_changed:
#         print(f"Creating CLIENT - STATUS UPDATED log for client {instance.pk}")
#         try:
#             ClientLog.objects.create(
#                 ref_client_id=instance,
#                 ref_table_name=instance._meta.db_table,
#                 ref_id=instance.pk,
#                 action_type="CLIENT - STATUS UPDATED",
#                 changed_data=json.loads(json.dumps({
#                     "client_name": {
#                         "old": f"{instance.client_first_name} {instance.client_last_name}",
#                         "new": client_name
#                     },
#                     "client_status": {"old": old_status, "new": new_status}
#                 }, cls=DjangoJSONEncoder)),
#                 performed_by=instance.updated_by,
#                 performed_at=timezone.now(),
#             )
#         except Exception as e:
#             print(f"Error creating CLIENT - STATUS UPDATED log for client {instance.pk}: {e}")
#     # else:
#     #     print(f"Skipping update/status logic for client {instance.pk}: created={created}, pk_exists={bool(instance.pk)}")

from users.utils import serialize_client_with_related

# @receiver(post_save, sender=Client, dispatch_uid="log_client_activity")
# def log_client_activity(sender, instance, created, **kwargs):
#     print(f"Signal triggered: client_id={instance.pk}, created={created}, _old_data={hasattr(instance, '_old_data')}, _old_status={hasattr(instance, '_old_status')}")
    
#     if getattr(instance, '_is_creating', False):
#         print(f"Skipping logging for client {instance.pk} during creation.")
#         return  # Skip logging during initial creation

#     # Extract common data
#     client_name = f"{instance.client_first_name or ''} {instance.client_last_name or ''}".strip()
#     aadhaar_card_file = instance. aadhaar_card_file
#     print(" aadhaar_card_file:", aadhaar_card_file)
    
#     # --- Only proceed with update/status logic for existing instances ---
#     # if not created and instance.pk:
#     #     print(f"Processing update for client: {instance.pk}")
#         # --- STATUS CHANGE ---
#     old_status = getattr(instance, "_old_status", None)
#     new_status = instance.client_status
#     status_changed = old_status != new_status

#     # --- UPDATED (only if NOT a pure status change) ---
#     if not created and not status_changed:
#         print("We are in in if not status change")
#         old_data = getattr(instance, "_old_data", None)
#         if old_data:
#             print(f"Old data found for client {instance.pk}: {old_data}")
#             old_data_filtered = {
#                 k: v for k, v in old_data.items()
#                 if k not in ["client_status", "created_at", "updated_at", "updated_by"]
#             }
#             new_data_filtered = serialize_client_with_related(instance)
#             new_data_filtered = {
#                 k: v for k, v in new_data_filtered.items()
#                 if k not in ["client_status", "created_at", "updated_at", "updated_by"]
#             }
#             # new_data_filtered = serialize_instance(
#             #     instance, exclude_fields=["id", "client_status", "created_at", "updated_at", "updated_by"]
#             # )

#             changes = build_changes(old_data_filtered, new_data_filtered)
            
#             log_data = {
#                 "description": changes,
#                 "client_name": client_name
#             }
#             if not created and changes:
#                 print("We are in in client update")
#                 print(f"Creating CLIENT UPDATED log for client {instance.pk}: {changes}")
#                 try:
#                     ClientLog.objects.create(
#                         ref_client_id=instance,
#                         ref_table_name=instance._meta.db_table,
#                         ref_id=instance.pk,
#                         action_type="CLIENT UPDATED",
#                         changed_data=json.loads(json.dumps(log_data, cls=DjangoJSONEncoder)),
#                         performed_by=instance.updated_by,
#                         performed_at=timezone.now(),
#                         # created_by=instance.created_by,
#                     )
#                 except Exception as e:
#                     print(f"Error creating CLIENT UPDATED log for client {instance.pk}: {e}")
#             else:
#                 print(f"No changes detected for client {instance.pk}")
#         else:
#             print(f"No old_data found for client {instance.pk}, skipping update log")

#     # --- STATUS UPDATE ---
#     if not created and status_changed:
#         print(f"Creating CLIENT - STATUS UPDATED log for client {instance.pk}")
#         try:
#             ClientLog.objects.create(
#                 ref_client_id=instance,
#                 ref_table_name=instance._meta.db_table,
#                 ref_id=instance.pk,
#                 action_type="CLIENT - STATUS UPDATED",
#                 changed_data=json.loads(json.dumps({
#                     "client_name": {
#                         "old": f"{instance.client_first_name} {instance.client_last_name}",
#                         "new": client_name
#                     },
#                     "client_status": {"old": old_status, "new": new_status}
#                 }, cls=DjangoJSONEncoder)),
#                 performed_by=instance.updated_by,
#                 performed_at=timezone.now(),
#             )
#         except Exception as e:
#             print(f"Error creating CLIENT - STATUS UPDATED log for client {instance.pk}: {e}")
#     # else:
#     #     print(f"Skipping update/status logic for client {instance.pk}: created={created}, pk_exists={bool(instance.pk)}")

# @receiver(post_save, sender=Client, dispatch_uid="log_client_activity")
# def log_client_activity(sender, instance, created, **kwargs):
#     print(f"Signal triggered: client_id={instance.pk}, created={created}, _old_data={hasattr(instance, '_old_data')}, _old_status={hasattr(instance, '_old_status')}")
    
#     if getattr(instance, '_is_creating', False):
#         print(f"Skipping logging for client {instance.pk} during creation.")
#         return

#     client_name = f"{instance.client_first_name or ''} {instance.client_last_name or ''}".strip()
#     print("aadhaar_card_file:", instance.aadhaar_card_file)
    
#     old_status = getattr(instance, "_old_status", None)
#     new_status = instance.client_status
#     status_changed = old_status != new_status

#     if not created and not status_changed:
#         print(f"Processing update for client: {instance.pk}")
#         old_data = getattr(instance, "_old_data", None)
#         if old_data:
#             print(f"Old data found for client {instance.pk}: {old_data}")
#             old_data_filtered = {
#                 k: v for k, v in old_data.items()
#                 if k not in ["client_status", "created_at", "updated_at", "updated_by"]
#             }
#             new_data_filtered = serialize_client_with_related(instance)
#             new_data_filtered = {
#                 k: v for k, v in new_data_filtered.items()
#                 if k not in ["client_status", "created_at", "updated_at", "updated_by"]
#             }

#             print(f"Old data filtered: {old_data_filtered}")
#             print(f"New data filtered: {new_data_filtered}")

#             changes = build_changes(old_data_filtered, new_data_filtered)
#             print(f"Computed changes: {changes}")

#             if not changes:
#                 print(f"No changes detected for client {instance.pk}, skipping update log")
#                 return

#             log_data = {
#                 "description": changes,
#                 "client_name": client_name
#             }
#             print(f"Creating CLIENT UPDATED log for client {instance.pk}: {changes}")
#             try:
#                 ClientLog.objects.create(
#                     ref_client_id=instance,
#                     ref_table_name=instance._meta.db_table,
#                     ref_id=instance.pk,
#                     action_type="CLIENT UPDATED",
#                     changed_data=json.loads(json.dumps(log_data, cls=DjangoJSONEncoder)),
#                     performed_by=instance.updated_by,
#                     performed_at=timezone.now(),
#                 )
#             except Exception as e:
#                 print(f"Error creating CLIENT UPDATED log for client {instance.pk}: {e}")
#         else:
#             print(f"No old_data found for client {instance.pk}, skipping update log")

#     if not created and status_changed:
#         print(f"Creating CLIENT - STATUS UPDATED log for client {instance.pk}")
#         try:
#             ClientLog.objects.create(
#                 ref_client_id=instance,
#                 ref_table_name=instance._meta.db_table,
#                 ref_id=instance.pk,
#                 action_type="CLIENT - STATUS UPDATED",
#                 changed_data=json.loads(json.dumps({
#                     "client_name": {
#                         "old": f"{instance.client_first_name} {instance.client_last_name}",
#                         "new": client_name
#                     },
#                     "client_status": {"old": old_status, "new": new_status}
#                 }, cls=DjangoJSONEncoder)),
#                 performed_by=instance.updated_by,
#                 performed_at=timezone.now(),
#             )
#         except Exception as e:
#             print(f"Error creating CLIENT - STATUS UPDATED log for client {instance.pk}: {e}")
            
# @receiver(pre_save, sender=Client)
# def capture_old_data(sender, instance, **kwargs):
#     if instance.pk:  # Only for updates, not creations
#         try:
#             old_instance = sender.objects.get(pk=instance.pk)
#             instance._old_data = serialize_client_with_related(old_instance)
#             instance._old_status = old_instance.client_status
#         except sender.DoesNotExist:
#             instance._old_data = {}
#             instance._old_status = None
#     else:
#         instance._old_data = {}
#         instance._old_status = None
#         instance._is_creating = True  # Set flag for creation
        
@receiver(post_save, sender=Client, dispatch_uid="log_client_activity")
def log_client_activity(sender, instance, created, **kwargs):
    print(f"Signal triggered: client_id={instance.pk}, created={created}, _old_data={hasattr(instance, '_old_data')}, _old_status={hasattr(instance, '_old_status')}")
    
    if getattr(instance, '_is_creating', False):
        print(f"Skipping logging for client {instance.pk} during creation.")
        return

    new_name = f"{instance.client_first_name or ''} {instance.client_last_name or ''}".strip()
    
    old_data = getattr(instance, "_old_data", None)
    old_status = getattr(instance, "_old_status", None)
    new_status = instance.client_status
    status_changed = old_status != new_status

    old_first = old_data.get('client_first_name', '') if old_data else ''
    old_last = old_data.get('client_last_name', '') if old_data else ''
    old_name = f"{old_first} {old_last}".strip()

    # if not created and not status_changed:
    #     print(f"Processing update for client: {instance.pk}")
    #     if old_data:
    #         print(f"Old data found for client {instance.pk}: {old_data}")
    #         old_data_filtered = {
    #             k: v for k, v in old_data.items()
    #             if k not in ["client_status", "created_at", "updated_at", "updated_by"]
    #         }
    #         new_data = serialize_client_with_related(instance)
    #         new_data_filtered = {
    #             k: v for k, v in new_data.items()
    #             if k not in ["client_status", "created_at", "updated_at", "updated_by"]
    #         }

    #         print(f"Old data filtered: {old_data_filtered}")
    #         print(f"New data filtered: {new_data_filtered}")

    #         changes = build_changes(old_data_filtered, new_data_filtered)
    #         print(f"Computed changes: {changes}")

    #         if not changes:
    #             print(f"No changes detected for client {instance.pk}, skipping update log")
    #             return

    #         log_data = {
    #             "description": changes,
    #             "client_name": new_name  # For update logs, use new name as reference
    #         }
    #         print(f"Creating CLIENT UPDATED log for client {instance.pk}: {changes}")
    #         try:
    #             ClientLog.objects.create(
    #                 ref_client_id=instance,
    #                 ref_table_name=instance._meta.db_table,
    #                 ref_id=instance.pk,
    #                 action_type="CLIENT UPDATED",
    #                 changed_data=json.loads(json.dumps(log_data, cls=DjangoJSONEncoder)),
    #                 performed_by=instance.updated_by,
    #                 performed_at=timezone.now(),
    #             )
    #         except Exception as e:
    #             print(f"Error creating CLIENT UPDATED log for client {instance.pk}: {e}")
    #     else:
    #         print(f"No old_data found for client {instance.pk}, skipping update log")

    if not created and status_changed:
        
        old_updated_by_username = get_username_from_id(old_data.get("updated_by"))
        new_updated_by_username = get_username_from_id(instance.updated_by)
        
        log_data = {"client_status" : {
            "old": old_status,
            "new": new_status
            },
            "updated_by" : {
            "old": old_updated_by_username,
            "new": new_updated_by_username
            }
        }
        
        
        try:
            ClientLog.objects.create(
                ref_client_id=instance,
                ref_table_name=instance._meta.db_table,
                ref_id=instance.pk,
                action_type="CLIENT - STATUS UPDATED",
                changed_data=json.loads(json.dumps(log_data, cls=DjangoJSONEncoder)),
                performed_by=instance.updated_by,
                performed_at=timezone.now(),
            )
        except Exception as e:
            print(f"Error creating CLIENT - STATUS UPDATED log for client {instance.pk}: {e}")
            
# @receiver(post_save, sender='users.Client', dispatch_uid="log_client_activity")
# def log_client_activity(sender, instance, created, **kwargs):

#     # Skip logging if this is a file-only update
#     if getattr(instance, '_is_file_update', False):
#         print(f"Skipping log for file-only update: client_id={instance.pk}")
#         return

#     print(f"Signal triggered: client_id={instance.pk}, created={created}, "
#                  f"_old_data={hasattr(instance, '_old_data')}, _old_status={hasattr(instance, '_old_status')}")

#     # Extract common data
#     client_name = f"{instance.client_first_name or ''} {instance.client_last_name or ''}".strip()
#     residential_city = instance.residential_city
#     residential_state = instance.residential_state

#     # --- CREATED ---
#     if created:
#         print(f"Creating log for new client: {instance.pk}")
#         new_data = serialize_instance(instance, exclude_fields=["client_id"])
#         new_data["client_name"] = client_name
#         new_data["residential_city"] = residential_city
#         new_data["residential_state"] = residential_state

#         try:
#             ClientLog.objects.create(
#                 ref_client_id=instance,
#                 ref_table_name=instance._meta.db_table,
#                 ref_id=instance.pk,
#                 action_type="CLIENT CREATED",
#                 changed_data=json.loads(json.dumps(build_changes(None, new_data), cls=DjangoJSONEncoder)),
#                 performed_by=instance.created_by,
#                 performed_at=timezone.now(),
#             )
#             print(f"Created CLIENT CREATED log for client: {instance.pk}")
#         except Exception as e:
#             print(f"Error creating CLIENT CREATED log for client {instance.pk}: {e}")

#         # Clear _old_data to prevent misuse
#         if hasattr(instance, '_old_data'):
#             print(f"_old_data unexpectedly set during creation for client {instance.pk}")
#             delattr(instance, '_old_data')
#         if hasattr(instance, '_old_status'):
#             print(f"_old_status unexpectedly set during creation for client {instance.pk}")
#             delattr(instance, '_old_status')
#         return  # Stop execution

#     # --- Only proceed with update/status logic for existing instances ---
#     if not created and instance.pk:
#         print(f"Processing update for client: {instance.pk}")
#         # --- STATUS CHANGE ---
#         old_status = getattr(instance, "_old_status", None)
#         new_status = instance.client_status
#         status_changed = old_status != new_status

#         # --- UPDATED (only if NOT a pure status change) ---
#         if not status_changed:
#             old_data = getattr(instance, "_old_data", None)
#             if old_data:
#                 print(f"Old data found for client {instance.pk}: {old_data}")
#                 old_data_filtered = {
#                     k: v for k, v in old_data.items()
#                     if k not in ["client_id", "client_status", "created_at", "updated_at", "updated_by"]
#                 }
#                 new_data_filtered = serialize_instance(
#                     instance, exclude_fields=["client_id", "client_status", "created_at", "updated_at", "updated_by"]
#                 )

#                 changes = build_changes(old_data_filtered, new_data_filtered)
                
#                 log_data = {
#                     "description": changes,
#                     "client_name": client_name
#                 }
#                 if changes:
#                     print(f"Creating CLIENT UPDATED log for client {instance.pk}: {changes}")
#                     try:
#                         ClientLog.objects.create(
#                             ref_client_id=instance,
#                             ref_table_name=instance._meta.db_table,
#                             ref_id=instance.pk,
#                             action_type="CLIENT UPDATED",
#                             changed_data=json.loads(json.dumps(log_data, cls=DjangoJSONEncoder)),
#                             performed_by=instance.updated_by,
#                             performed_at=timezone.now(),
#                         )
#                     except Exception as e:
#                         print(f"Error creating CLIENT UPDATED log for client {instance.pk}: {e}")
#                 else:
#                     print(f"No changes detected for client {instance.pk}")
#             else:
#                 print(f"No old_data found for client {instance.pk}, skipping update log")

#         # --- STATUS UPDATE ---
#         if status_changed:
#             print(f"Creating CLIENT - STATUS UPDATED log for client {instance.pk}")
#             try:
#                 ClientLog.objects.create(
#                     ref_client_id=instance,
#                     ref_table_name=instance._meta.db_table,
#                     ref_id=instance.pk,
#                     action_type="CLIENT - STATUS UPDATED",
#                     changed_data=json.loads(json.dumps({
#                         "client_name": {
#                             "old": f"{instance.client_first_name} {instance.client_last_name}",
#                             "new": client_name
#                         },
#                         "client_status": {"old": old_status, "new": new_status}
#                     }, cls=DjangoJSONEncoder)),
#                     performed_by=instance.updated_by,
#                     performed_at=timezone.now(),
#                 )
#             except Exception as e:
#                 print(f"Error creating CLIENT - STATUS UPDATED log for client {instance.pk}: {e}")
#     else:
#         print(f"Skipping update/status logic for client {instance.pk}: created={created}, pk_exists={bool(instance.pk)}")
    
# def serialize_instance_client_log(instance, exclude_fields=None):
#     """Serialize model instance to dict, including ForeignKey display values."""
#     exclude_fields = exclude_fields or []
#     data = {}
#     for field in instance._meta.fields:
#         if field.name in exclude_fields:
#             continue
#         value = getattr(instance, field.name)
#         if isinstance(field, models.ForeignKey) and value is not None:
#             try:
#                 related_obj = getattr(instance, field.name)
#                 if hasattr(related_obj, 'name'):
#                     value = related_obj.name
#                 elif hasattr(related_obj, 'username'):
#                     value = related_obj.username
#                 else:
#                     value = str(related_obj)
#             except Exception:
#                 value = str(value)
#         data[field.name] = value
#     return data

# @receiver(post_save, sender='users.Client', dispatch_uid="log_client_activity")
# def log_client_activity(sender, instance, created, **kwargs):
#     from users.models import ClientLog  # Lazy import to avoid circular import

#     # Skip logging for file-only updates on existing clients
#     if getattr(instance, '_is_file_update', False) and not created:
#         print(f"Skipping log for file-only update: client_id={instance.pk}")
#         return

#     print(f"Signal triggered: client_id={instance.pk}, created={created}, "
#                  f"_old_data={hasattr(instance, '_old_data')}, _old_status={hasattr(instance, '_old_status')}")

#     # Extract common data
#     client_name = f"{instance.client_first_name or ''} {instance.client_last_name or ''}".strip()

#     # --- CREATED ---
#     if created:
#         print(f"Creating log for new client: {instance.pk}")
#         new_data = serialize_instance(instance, exclude_fields=["client_id"])
#         new_data["client_name"] = client_name

#         try:
#             ClientLog.objects.create(
#                 ref_client_id=instance,
#                 ref_table_name=instance._meta.db_table,
#                 ref_id=instance.pk,
#                 action_type="CLIENT CREATED",
#                 changed_data=json.loads(json.dumps({"new_data": new_data}, cls=DjangoJSONEncoder)),
#                 performed_by=instance.created_by,
#                 performed_at=timezone.now(),
#             )
#             print(f"Created CLIENT CREATED log for client: {instance.pk}")
#         except Exception as e:
#             print(f"Error creating CLIENT CREATED log for client {instance.pk}: {e}")

#         # Clear _old_data to prevent misuse
#         if hasattr(instance, '_old_data'):
#             print(f"_old_data unexpectedly set during creation for client {instance.pk}")
#             delattr(instance, '_old_data')
#         if hasattr(instance, '_old_status'):
#             print(f"_old_status unexpectedly set during creation for client {instance.pk}")
#             delattr(instance, '_old_status')
#         return  # Stop execution

#     # --- Only proceed with update/status logic for existing instances ---
#     if instance.pk:
#         print(f"Processing update for client: {instance.pk}")
#         # --- STATUS CHANGE ---
#         old_status = getattr(instance, "_old_status", None)
#         new_status = instance.client_status
#         status_changed = old_status != new_status

#         # --- UPDATED (log all changed fields, including ForeignKeys) ---
#         old_data = getattr(instance, "_old_data", None)
#         if old_data:
#             print(f"Old data found for client {instance.pk}")
#             new_data = serialize_instance(instance, exclude_fields=["client_id"])
#             new_data["client_name"] = client_name

#             changes = build_changes(old_data, new_data)
#             if changes:
#                 print(f"Creating CLIENT UPDATED log for client {instance.pk}: {changes}")
#                 try:
#                     ClientLog.objects.create(
#                         ref_client_id=instance,
#                         ref_table_name=instance._meta.db_table,
#                         ref_id=instance.pk,
#                         action_type="CLIENT UPDATED",
#                         changed_data=json.loads(json.dumps({"changes": changes, "client_name": client_name}, cls=DjangoJSONEncoder)),
#                         performed_by=instance.updated_by,
#                         performed_at=timezone.now(),
#                     )
#                     print(f"Created CLIENT UPDATED log for client: {instance.pk}")
#                 except Exception as e:
#                     print(f"Error creating CLIENT UPDATED log for client {instance.pk}: {e}")
#             else:
#                 print(f"No changes detected for client {instance.pk}")

#         # --- STATUS UPDATE ---
#         if status_changed:
#             print(f"Creating CLIENT - STATUS UPDATED log for client {instance.pk}")
#             try:
#                 ClientLog.objects.create(
#                     ref_client_id=instance,
#                     ref_table_name=instance._meta.db_table,
#                     ref_id=instance.pk,
#                     action_type="CLIENT - STATUS UPDATED",
#                     changed_data=json.loads(json.dumps({
#                         "client_name": client_name,
#                         "client_status": {"old": old_status, "new": new_status}
#                     }, cls=DjangoJSONEncoder)),
#                     performed_by=instance.updated_by,
#                     performed_at=timezone.now(),
#                 )
#                 print(f"Created CLIENT - STATUS UPDATED log for client: {instance.pk}")
#             except Exception as e:
#                 print(f"Error creating CLIENT - STATUS UPDATED log for client {instance.pk}: {e}")
#         else:
#             print(f"No status change for client {instance.pk}")
#     else:
#         print(f"Skipping update/status logic for client {instance.pk}: created={created}, pk_exists={bool(instance.pk)}")
        
            
@receiver(post_delete, sender=Client)
def log_client_delete(sender, instance, **kwargs):
    
    user = get_current_user()
    
    if not isinstance(user, User):
        user = None
    
    # old_data = serialize_instance(instance, exclude_fields=["id"])
    old_data = serialize_client_with_related(instance)
    old_data["client_name"] = f"{normalize_value(instance.client_first_name)} {normalize_value(instance.client_last_name)}".strip()
    old_data["created_by"] = normalize_value(instance.created_by)
    old_data["updated_by"] = normalize_value(instance.updated_by)
    
    old_data_filtered = {
            k: v for k, v in old_data.items()
            if k not in ["client_status", "created_at", "updated_at"]
        }

    performed_by = instance.updated_by or user or instance.created_by

    ClientLog.objects.create(
        ref_client_id=None,   # client is gone, keep logs detached
        ref_table_name=instance._meta.db_table,
        ref_id=instance.pk,
        action_type="CLIENT DELETED",
        changed_data=json.loads(json.dumps(build_changes(old_data_filtered, None), cls=DjangoJSONEncoder)),
        performed_by=performed_by,
        performed_at=timezone.now(),
    )


# --------- For Company ---------


@receiver(pre_save, sender=Company)
def cache_old_company(sender, instance, **kwargs):
    if instance.pk:
        try:
            old_instance = Company.objects.get(pk=instance.pk)
            instance._old_data = serialize_instance(old_instance)
            instance._old_status = old_instance.company_status
        except Company.DoesNotExist:
            instance._old_data = {}
            instance._old_status = None
    else:
        instance._old_data = {}
        instance._old_status = None



# @receiver(post_save, sender=Company)
# def log_company_activity(sender, instance, created, **kwargs):
#     company_name = instance.company_name
#     company_country_name = instance.company_country.name
#     company_state = instance.company_state.name
#     company_city = instance.company_city.name
#     # --- CREATED ---
#     if created:
#         new_data = serialize_instance(instance, exclude_fields=["id","account_concerned_person_country_code","travel_concerned_person_country_code"])
#         new_data["company_name"] = company_name
#         new_data["company_country_name"] = company_country_name
#         new_data["company_state"] = company_state
#         new_data["company_city"] = company_city

#         ClientLog.objects.create(
#             ref_company_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="COMPANY CREATED",
#             changed_data=json.loads(json.dumps(build_changes(None, new_data), cls=DjangoJSONEncoder)),
#             performed_by=instance.created_by,
#             performed_at=timezone.now(),
#         )
#         return  # stop here

#     # --- STATUS CHANGE ---
#     old_status = getattr(instance, "_old_status", None)
#     new_status = instance.company_status
#     status_changed = old_status != new_status



#     old_data = getattr(instance, "_old_data", {}) or {}
#     old_data_filtered = {k: v for k, v in old_data.items() if k not in ["id", "company_status", "created_at", "updated_at","is_active","updated_by"]}
#     new_data_filtered = serialize_instance(instance, exclude_fields=["id", "company_status", "created_at", "updated_at","is_active","updated_by"])
#     changes = build_changes(old_data_filtered, new_data_filtered)

#     # Log normal updates only if there are changes
#     if changes:
#         ClientLog.objects.create(
#             ref_company_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="COMPANY UPDATED",
#             changed_data=json.loads(json.dumps({
#                 "description": changes,
#                 "company_name": company_name
#             }, cls=DjangoJSONEncoder)),
#             performed_by=instance.updated_by,
#             performed_at=timezone.now(),
#         )

#     # Log status change separately
#     if status_changed:
#         ClientLog.objects.create(
#             ref_company_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="COMPANY - STATUS UPDATED",
#             changed_data=json.loads(json.dumps({
#                 "company_name": {
#                     "old": instance.company_name,
#                     "new": company_name
#                 },
#                 "company_status": {"old": old_status, "new": new_status}
#             }, cls=DjangoJSONEncoder)),
#             performed_by=instance.updated_by,
#             performed_at=timezone.now(),
#         )

# OG
# @receiver(post_save, sender=Company)
# def log_company_activity(sender, instance, created, **kwargs):
#     if getattr(instance, '_is_creating', False):
#         print(f"Skipping post_save logging for company {instance.pk} during creation.")
#         return

#     company_name = instance.company_name
#     company_country_name = instance.company_country.name
#     company_state = instance.company_state.name
#     company_city = instance.company_city.name
#     # --- CREATED ---
   
#     # --- STATUS CHANGE ---
#     old_status = getattr(instance, "_old_status", None)
#     new_status = instance.company_status
#     status_changed = old_status != new_status



#     old_data = getattr(instance, "_old_data", {}) or {}
#     old_data_filtered = {k: v for k, v in old_data.items() if k not in ["id", "company_status", "created_at", "updated_at","is_active","updated_by"]}
#     new_data_filtered = serialize_instance(instance, exclude_fields=["id", "company_status", "created_at", "updated_at","is_active","updated_by"])
#     changes = build_changes(old_data_filtered, new_data_filtered)

#     # Log normal updates only if there are changes
#     if not created and changes:
#         ClientLog.objects.create(
#             ref_company_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="COMPANY UPDATED",
#             changed_data=json.loads(json.dumps({
#                 "description": changes,
#                 "company_name": company_name
#             }, cls=DjangoJSONEncoder)),
#             performed_by=instance.updated_by,
#             performed_at=timezone.now(),
#         )

#     # Log status change separately
#     if not created and status_changed:
#         ClientLog.objects.create(
#             ref_company_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="COMPANY - STATUS UPDATED",
#             changed_data=json.loads(json.dumps({
#                 "company_name": {
#                     "old": instance.company_name,
#                     "new": company_name
#                 },
#                 "company_status": {"old": old_status, "new": new_status}
#             }, cls=DjangoJSONEncoder)),
#             performed_by=instance.updated_by,
#             performed_at=timezone.now(),
#         )


# @receiver(post_save, sender=Company)
# def log_company_activity(sender, instance, created, **kwargs):
#     if getattr(instance, '_is_creating', False):
#         print(f"Skipping post_save logging for company {instance.pk} during creation.")
#         return

#     company_name = instance.company_name
#     company_country_name = instance.company_country.name
#     company_state = instance.company_state.name
#     company_city = instance.company_city.name
#     # --- CREATED ---
   
#     # --- STATUS CHANGE ---
#     old_status = getattr(instance, "_old_status", None)
#     new_status = instance.company_status
#     status_changed = old_status != new_status



#     old_data = getattr(instance, "_old_data", {}) or {}
#     old_data = serialize_company_with_related(old_data)
#     new_data = serialize_company_with_related(instance)
#     old_data_filtered = {k: v for k, v in old_data.items() if k not in ["id", "company_status", "created_at", "updated_at","is_active","updated_by"]}
#     # new_data_filtered = serialize_instance(instance, exclude_fields=["id", "company_status", "created_at", "updated_at","is_active","updated_by"])
#     new_data_filtered = {
#             k: v for k, v in new_data.items()
#             if k not in ["id", "company_status", "created_at", "updated_at","is_active","updated_by"]
#         }
#     changes = build_changes(old_data_filtered, new_data_filtered)

#     # Log normal updates only if there are changes
#     if not created and changes:
#         ClientLog.objects.create(
#             ref_company_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="COMPANY UPDATED",
#             changed_data=json.loads(json.dumps({
#                 "description": changes,
#                 "company_name": company_name
#             }, cls=DjangoJSONEncoder)),
#             performed_by=instance.updated_by,
#             performed_at=timezone.now(),
#         )

#     # Log status change separately
#     if not created and status_changed:
#         ClientLog.objects.create(
#             ref_company_id=instance,
#             ref_table_name=instance._meta.db_table,
#             ref_id=instance.pk,
#             action_type="COMPANY - STATUS UPDATED",
#             changed_data=json.loads(json.dumps({
#                 "company_name": {
#                     "old": instance.company_name,
#                     "new": company_name
#                 },
#                 "company_status": {"old": old_status, "new": new_status}
#             }, cls=DjangoJSONEncoder)),
#             performed_by=instance.updated_by,
#             performed_at=timezone.now(),
#         )

@receiver(pre_save, sender=Company)
def capture_old_data(sender, instance, **kwargs):
    if instance.pk:  # Only for updates, not creations
        try:
            old_instance = sender.objects.get(pk=instance.pk)
            instance._old_data = serialize_company_with_related(old_instance)
            instance._old_status = old_instance.company_status
        except sender.DoesNotExist:
            instance._old_data = {}
            instance._old_status = None
    else:
        instance._old_data = {}
        instance._old_status = None
        instance._is_creating = True  # Set flag for creation

@receiver(post_save, sender=Company)
def log_company_activity(sender, instance, created, **kwargs):
    if getattr(instance, '_is_creating', False):
        print(f"Skipping post_save logging for company {instance.pk} during creation.")
        return

    company_name = instance.company_name
    company_country_name = instance.company_country.name
    company_state = instance.company_state.name
    company_city = instance.company_city.name

    # --- STATUS CHANGE ---
    old_status = getattr(instance, "_old_status", None)
    new_status = instance.company_status
    status_changed = old_status != new_status

    # Get old_data from instance (assumed to be set in pre_save)
    old_data = getattr(instance, "_old_data", {}) or {}
    
    # Serialize the current instance
    new_data = serialize_company_with_related(instance)
    
    # Filter out unwanted fields from old_data and new_data
    old_data_filtered = {k: v for k, v in old_data.items() if k not in ["id", "company_status", "created_at", "updated_at", "is_active", ]}
    new_data_filtered = {k: v for k, v in new_data.items() if k not in ["id", "company_status", "created_at", "updated_at", "is_active", ]}
    
    # Build changes between old and new data
    changes = build_changes(old_data_filtered, new_data_filtered)

    # Combine logs for updates and status changes
    if not created and (changes or status_changed):
        log_data = {
            "company_name": {
                "old": old_data.get("company_name", instance.company_name),
                "new": company_name
            }
        }
        
        # Include general changes if any
        if changes:
            log_data["description"] = changes
        
        # Include status change if any
        if status_changed:
            old_updated_by_username = get_username_from_id(old_data.get("updated_by"))
            new_updated_by_username = get_username_from_id(new_data.get("updated_by"))
            
            log_data["company_status"] = {
                "old": old_status,
                "new": new_status
            }
            log_data["updated_by"] = {
                "old": old_updated_by_username,
                "new": new_updated_by_username
            }
            
        # Determine action type based on changes
        action_type = "COMPANY UPDATED"
        if status_changed and not changes:
            action_type = "COMPANY - STATUS UPDATED"
        elif status_changed and changes:
            action_type = "COMPANY UPDATED AND STATUS CHANGED"

        ClientLog.objects.create(
            ref_company_id=instance,
            ref_table_name=instance._meta.db_table,
            ref_id=instance.pk,
            action_type=action_type,
            changed_data=json.loads(json.dumps(log_data, cls=DjangoJSONEncoder)),
            performed_by=instance.updated_by,
            performed_at=timezone.now(),
        )
        
@receiver(post_delete, sender=Company)
def log_company_delete(sender, instance, **kwargs):

    user = get_current_user()
    
    if not isinstance(user, User):
        user = None

    # old_data = serialize_instance(instance, exclude_fields=["id"])
    old_data = serialize_company_with_related(instance)
    old_data["company_name"] = normalize_value(instance.company_name)
    old_data["created_by"] = normalize_value(instance.created_by)
    old_data["updated_by"] = normalize_value(instance.updated_by)

    performed_by = instance.updated_by or user or instance.created_by

    ClientLog.objects.create(
        ref_company_id=None,   # client is gone, keep logs detached
        ref_table_name=instance._meta.db_table,
        ref_id=instance.pk,
        action_type="COMPANY DELETED",
        changed_data=json.loads(json.dumps(build_changes(old_data, None), cls=DjangoJSONEncoder)),
        performed_by=performed_by,
        performed_at=timezone.now(),
    )


# --------------- For User Log ------------------
@receiver(pre_save, sender=User)
def store_old_data(sender, instance, **kwargs):
    if instance.pk:
        try:
            old_instance = User.objects.get(pk=instance.pk)
            instance._old_data = serialize_instance(old_instance)
            instance._old_status = old_instance.status
            instance._old_last_login = old_instance.last_login
            instance._old_password = old_instance.password
        except User.DoesNotExist:
            instance._old_data = {}
            instance._old_status = None
            instance._old_last_login = None
            # instance._old_password = old_instance.password

@receiver(post_save, sender=User)
def log_user_activity(sender, instance, created, **kwargs):
    username = instance.username

    # --- CREATED ---
    if created:
        new_data = serialize_instance(instance, exclude_fields=["id", "password","is_staff", "first_login", "last_login"])
        new_data["username"] = username

        ClientLog.objects.create(
            ref_user_id=instance,
            ref_table_name=instance._meta.db_table,
            ref_id=instance.pk,
            action_type="USER CREATED",
            changed_data=json.loads(json.dumps(build_changes(None, new_data), cls=DjangoJSONEncoder)),
            performed_by=instance.created_by,
            performed_at=timezone.now(),
        )
        return

    # --- STATUS CHANGE ---
    old_status = getattr(instance, "_old_status", None)
    new_status = instance.status
    status_changed = old_status != new_status

    # --- UPDATED (excluding pure status changes) ---
    if not status_changed:
        old_data = serialize_instance(instance, exclude_fields=["id", "password", "is_staff", "first_login", "last_login"])
        old_data = getattr(instance, "_old_data", None) or {}
        old_data_filtered = {
            k: v for k, v in old_data.items()
            if k not in ["id", "status", "created_at", "updated_at", "password", "first_login", "last_login"]
        }
        new_data_filtered = serialize_instance(
            instance, exclude_fields=["id", "status", "created_at", "updated_at", "password", "first_login", "last_login"]
        )

        # Force username into changed_data
        changes = build_changes(old_data_filtered, new_data_filtered)
        changes["username"] = {
            "old": old_data.get("username"),
            "new": instance.username
        }

        if changes:
            ClientLog.objects.create(
                ref_user_id=instance,
                ref_table_name=instance._meta.db_table,
                ref_id=instance.pk,
                action_type="USER UPDATED",
                changed_data=json.loads(json.dumps(changes, cls=DjangoJSONEncoder)),
                performed_by=instance.updated_by,
                performed_at=timezone.now(),
            )

    # --- STATUS UPDATE ---
    if status_changed:
        ClientLog.objects.create(
            ref_user_id=instance,
            ref_table_name=instance._meta.db_table,
            ref_id=instance.pk,
            action_type="USER - STATUS UPDATED",
            changed_data=json.loads(json.dumps({
                "username": {
                    # "old": old_data.get("username") if 'old_data' in locals() else username,
                    "old": instance.username,
                    "new": username
                },
                "status": {"old": old_status, "new": new_status}
            }, cls=DjangoJSONEncoder)),
            performed_by=instance.updated_by,
            performed_at=timezone.now(),
        )


'''
@receiver(post_save, sender=User)
def log_user_activity(sender, instance, created, **kwargs):
    username = instance.username
    # --- CREATED ---
    if created:
        new_data = serialize_instance(instance, exclude_fields=["id","password"])
        new_data["username"] = username

        ClientLog.objects.create(
            ref_user_id=instance,
            ref_table_name=instance._meta.db_table,
            ref_id=instance.pk,
            action_type="USER CREATED",
            changed_data=json.loads(json.dumps(build_changes(None, new_data), cls=DjangoJSONEncoder)),
            performed_by=instance.created_by,
            performed_at=timezone.now(),
        )
        print("Created by", instance.created_by)
        return  # stop here: no update/status logs

    # --- STATUS CHANGE ---
    old_status = getattr(instance, "_old_status", None)
    new_status = instance.status
    status_changed = old_status != new_status

    # --- UPDATED (only if NOT a pure status change) ---
    if not status_changed:  # skip UPDATED log when status changed
        old_data = getattr(instance, "_old_data", None)
        old_data_filtered = {
            k: v for k, v in old_data.items()
            if k not in ["id","status", "created_at", "updated_at", "password"]
        }
        new_data_filtered = serialize_instance(
            instance, exclude_fields=["id", "status", "created_at", "updated_at", "password"]
        )

        old_data_filtered["username"] = old_data.get("username")
        new_data_filtered["username"] = instance.username

        changes = build_changes(old_data_filtered, new_data_filtered)
          
        if changes:
            ClientLog.objects.create(
                ref_user_id=instance,
                ref_table_name=instance._meta.db_table,
                ref_id=instance.pk,
                action_type="USER UPDATED",
                changed_data=json.loads(json.dumps(changes,cls=DjangoJSONEncoder)),
                performed_by=instance.updated_by,
                performed_at=timezone.now(),
            )

    # --- STATUS UPDATE ---
    if status_changed:
        ClientLog.objects.create(
            ref_user_id=instance,
            ref_table_name=instance._meta.db_table,
            ref_id=instance.pk,
            action_type="USER - STATUS UPDATED",
            changed_data=json.loads(json.dumps({
                "username": username,  # always include current username
                "status": {"old": old_status, "new": new_status}
            }, cls=DjangoJSONEncoder)),
            performed_by=instance.updated_by,
            performed_at=timezone.now(),
        )
'''


@receiver(post_delete, sender=User)
def log_user_delete(sender, instance, **kwargs):
    old_data = serialize_instance(instance, exclude_fields=["id", "password", "is_staff", "first_login", "last_login"])
    old_data["username"] = normalize_value(instance.username)
    old_data["created_by"] = normalize_value(instance.created_by)
    old_data["updated_by"] = normalize_value(instance.updated_by)

    performed_by = instance.updated_by

    ClientLog.objects.create(
        ref_user_id=None,   # client is gone, keep logs detached
        ref_table_name=instance._meta.db_table,
        ref_id=instance.pk,
        action_type="USER DELETED",
        changed_data=json.loads(json.dumps(build_changes(old_data, None), cls=DjangoJSONEncoder)),
        performed_by=performed_by,
        performed_at=timezone.now(),
    )
