from django.contrib.contenttypes.models import ContentType
from django.core.serializers.json import DjangoJSONEncoder
from django.utils import timezone
import datetime
import json
import logging
from .models import AuditLog
from api_login.models import Usuario

logger = logging.getLogger(__name__)

def get_real_user(request):
    """
    Obtiene el usuario real a partir del request.
    
    Args:
        request: Objeto request de Django
        
    Returns:
        Instancia de Usuario o None
    """
    try:
        if not hasattr(request, 'user') or not request.user:
            return None
            
        # Si el usuario no está autenticado
        if not hasattr(request.user, 'is_authenticated') or not request.user.is_authenticated:
            return None
            
        # Obtener el ID del usuario del token JWT o del proxy
        user_id = None
        if hasattr(request.user, 'pk'):
            user_id = request.user.pk
        elif hasattr(request.user, 'id'):
            user_id = request.user.id
            
        if user_id:
            try:
                return Usuario.objects.get(pk=user_id)
            except Usuario.DoesNotExist:
                return None
        return None
    except Exception as e:
        logger.error(f"Error al obtener usuario real: {e}")
        return None

class AuditHelper:
    @staticmethod
    def log_action(user=None, action=None, instance=None, data_before=None, data_after=None, description="", request=None):
        """
        Registra una acción en el sistema de auditoría.
        
        Args:
            user: Usuario que realizó la acción (puede ser None para acciones anónimas)
            action: Tipo de acción (CREATE, UPDATE, DELETE, etc.)
            instance: Instancia del modelo afectado
            data_before: Datos antes del cambio (para UPDATE)
            data_after: Datos después del cambio (para UPDATE)
            description: Descripción adicional de la acción
            request: Objeto request para obtener la IP (opcional)
        
        Returns:
            Instancia del registro de auditoría creado o None en caso de error
        """
        try:
            # Validar la acción
            if action is None:
                logger.error("No se especificó la acción para el log de auditoría")
                return None
            
            # Obtener IP del request si está disponible
            ip_address = None
            if request:
                x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
                if x_forwarded_for:
                    ip_address = x_forwarded_for.split(',')[0].strip()
                else:
                    ip_address = request.META.get('REMOTE_ADDR')
            
            # Si se proporcionó el request pero no el usuario, intentar obtener el usuario real
            if user is None and request is not None:
                user = get_real_user(request)
                
            # Convertir diccionarios a JSON seguro con manejo especial para fechas
            safe_data_before = None
            if data_before is not None:
                try:
                    # Preprocesar los datos para asegurar que las fechas se manejan correctamente
                    processed_data_before = AuditHelper._preprocess_dates(data_before)
                    safe_data_before = json.loads(json.dumps(processed_data_before, cls=DjangoJSONEncoder))
                except Exception as e:
                    logger.error(f"Error al convertir data_before a JSON: {e}")
            
            safe_data_after = None
            if data_after is not None:
                try:
                    # Preprocesar los datos para asegurar que las fechas se manejan correctamente
                    processed_data_after = AuditHelper._preprocess_dates(data_after)
                    safe_data_after = json.loads(json.dumps(processed_data_after, cls=DjangoJSONEncoder))
                except Exception as e:
                    logger.error(f"Error al convertir data_after a JSON: {e}")
            
            # Crear el registro de auditoría sin el usuario inicialmente
            audit_log = AuditLog(
                action=action,
                ip_address=ip_address,
                data_before=safe_data_before,
                data_after=safe_data_after,
                description=description
            )
            
            # Asignar usuario solo si es una instancia de Usuario
            if user is not None and isinstance(user, Usuario):
                audit_log.user = user
            
            # Relacionar con el objeto afectado
            if instance:
                try:
                    content_type = ContentType.objects.get_for_model(instance.__class__)
                    audit_log.content_type = content_type
                    audit_log.object_id = instance.pk
                except Exception as e:
                    logger.error(f"Error al obtener ContentType: {e}")
            
            # Guardar el registro
            audit_log.save()
            return audit_log
            
        except Exception as e:
            logger.error(f"Error al registrar auditoría: {e}")
            import traceback
            logger.error(traceback.format_exc())
            return None
            
    @staticmethod
    def _preprocess_dates(data):
        """
        Convierte las fechas en strings a objetos datetime y los strings de fecha en formato ISO
        a un formato que pueda ser serializado correctamente.
        
        Args:
            data: Diccionario de datos o valor individual a procesar
            
        Returns:
            Datos preprocesados seguros para serialización JSON
        """
        if isinstance(data, dict):
            result = {}
            for key, value in data.items():
                result[key] = AuditHelper._preprocess_dates(value)
            return result
        elif isinstance(data, list):
            return [AuditHelper._preprocess_dates(item) for item in data]
        elif isinstance(data, str):
            # Intentar convertir strings que parecen fechas a formato ISO
            try:
                # Si parece una fecha en formato ISO
                if 'T' in data and ('+' in data or 'Z' in data):
                    # Ya es ISO, déjalo como está
                    return data
                # Si parece una fecha en formato YYYY-MM-DD
                if len(data) >= 10 and data[4] == '-' and data[7] == '-':
                    try:
                        # Intentar parsear como fecha
                        date_obj = datetime.datetime.strptime(data[:10], '%Y-%m-%d')
                        # Convertir a string ISO
                        return date_obj.isoformat()
                    except ValueError:
                        return data
            except:
                # Si falla, devolver el string original
                return data
            return data
        else:
            # Si es un datetime, se manejará con DjangoJSONEncoder
            return data