# Utils
import logging

# Django
from auditlog.models import LogEntry
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.fields.reverse_related import ManyToOneRel

# Utils
from {{project_slug}}.utils.serializers import LogEntrySerializer

logger = logging.getLogger(__name__)


def get_logs(
    obj, exclude={}, nested_fields=[], many_to_one_fields=[]
) -> [LogEntrySerializer]:
    """Get log entries for a particular object"""
    try:
        logs = get_object_logs(obj)

        for foreign_key in get_foreign_keys(obj, exclude):
            logs = logs.union(get_foreign_key_logs(obj, foreign_key, nested_fields))

        for many_to_one_keys in get_related_objects(obj, many_to_one_fields):
            logs = logs.union(get_many_to_one_logs(obj, many_to_one_keys))

        return LogEntrySerializer(logs.order_by("timestamp"), many=True).data
    except Exception as e:
        logger.error(f"Error in get_logs: {str(e)}")
        return []


def get_related_objects(obj, many_to_one_fields):
    return [
        field
        for field in obj._meta.get_fields()
        if isinstance(field, ManyToOneRel) and field.name in many_to_one_fields
    ]


def get_object_logs(obj):
    content_type = ContentType.objects.get_for_model(obj.__class__).pk
    return LogEntry.objects.filter(object_id=obj.pk, content_type=content_type)


def get_foreign_keys(obj, exclude):
    return [
        field
        for field in obj._meta.get_fields()
        if (
            field.is_relation
            and not field.many_to_many
            and not field.one_to_many
            and field.name not in exclude
        )
    ]


def get_foreign_key_logs(obj, foreign_key, nested_fields):
    logs = LogEntry.objects.none()
    try:
        foreign_obj = getattr(obj, foreign_key.name)
        if foreign_obj:
            logs = logs.union(get_related_logs(foreign_obj))

            if nested_fields.get(foreign_key.name):
                nested_obj = getattr(foreign_obj, nested_fields.get(foreign_key.name))
                if nested_obj:
                    logs = logs.union(get_related_logs(nested_obj))
    except ObjectDoesNotExist:
        logger.warning(f"Related object not found for {foreign_key.name}")
    except Exception as e:
        logger.error(f"Error processing foreign key {foreign_key.name}: {str(e)}")

    return logs


def get_many_to_one_logs(obj, foreign_key):
    logs = LogEntry.objects.none()
    try:
        foreign_objs = getattr(obj, foreign_key.name).all()
        for foreign_obj in foreign_objs:
            logs = logs.union(get_related_logs(foreign_obj))
    except ObjectDoesNotExist:
        logger.warning(f"Related object not found for {foreign_key.name}")
    except Exception as e:
        logger.error(f"Error processing foreign key {foreign_key.name}: {str(e)}")

    return logs


def get_related_logs(related_obj):
    content_type = ContentType.objects.get_for_model(related_obj.__class__).pk
    return LogEntry.objects.filter(
        object_id=related_obj.pk,
        content_type=content_type,
        action=1,
    )
