from typing import TYPE_CHECKING, Any

from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import QuerySet

if TYPE_CHECKING:
    pass


class DiffMixin:
    """Mixin to add diffing capabilities to models with pghistory"""

    def get_prev_record(self) -> Any | None:
        """Get the previous record for this instance"""
        try:
            # Use getattr to safely access objects manager and pghistory fields
            manager = getattr(type(self), "objects", None)
            if manager is None:
                return None

            pgh_created_at = getattr(self, "pgh_created_at", None)
            pgh_obj_id = getattr(self, "pgh_obj_id", None)

            if pgh_created_at is None or pgh_obj_id is None:
                return None

            return (
                manager.filter(
                    pgh_created_at__lt=pgh_created_at,
                    pgh_obj_id=pgh_obj_id,
                )
                .order_by("-pgh_created_at")
                .first()
            )
        except (AttributeError, TypeError):
            return None

    def diff_against_previous(self) -> dict:
        """Compare this record against the previous one"""
        prev_record = self.get_prev_record()
        if not prev_record:
            return {}

        skip_fields = {
            "pgh_id",
            "pgh_created_at",
            "pgh_label",
            "pgh_obj_id",
            "pgh_context_id",
            "_state",
            "created_at",
            "updated_at",
        }

        changes = {}
        for field, value in self.__dict__.items():
            # Skip internal fields and those we don't want to track
            if field.startswith("_") or field in skip_fields or field.endswith("_id"):
                continue

            try:
                old_value = getattr(prev_record, field)
                new_value = value
                if old_value != new_value:
                    changes[field] = {
                        "old": (str(old_value) if old_value is not None else "None"),
                        "new": (str(new_value) if new_value is not None else "None"),
                    }
            except AttributeError:
                continue

        return changes


class TrackedModel(models.Model):
    """Abstract base class for models that need history tracking"""

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

    def get_history(self) -> QuerySet:
        """Get all history records for this instance in chronological order"""
        try:
            # Use getattr to safely access pghistory events
            events = getattr(self, "events", None)
            if events is None:
                return self.__class__.objects.none()

            event_model = getattr(events, "model", None)
            if event_model:
                return event_model.objects.filter(pgh_obj_id=self.pk).order_by("-pgh_created_at")
        except (AttributeError, TypeError):
            pass
        return self.__class__.objects.none()


class HistoricalSlug(models.Model):
    """Track historical slugs for models"""

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey("content_type", "object_id")
    slug = models.SlugField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="historical_slugs",
    )

    class Meta:
        app_label = "core"
        unique_together = ("content_type", "slug")
        indexes = [
            models.Index(fields=["content_type", "object_id"]),
            models.Index(fields=["slug"]),
        ]

    def __str__(self) -> str:
        return f"{self.content_type} - {self.object_id} - {self.slug}"
