"""
Signal handlers for automatic search vector updates.

These signals ensure search vectors stay synchronized with model changes,
eliminating the need for manual re-indexing.

Signal handlers are only active when using PostgreSQL with PostGIS backend.
"""
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.conf import settings
from django.contrib.postgres.search import SearchVector

from apps.entities.models import Company, RideModel, Park, Ride

# Only register signals if using PostgreSQL with PostGIS
_using_postgis = 'postgis' in settings.DATABASES['default']['ENGINE']


if _using_postgis:
    
    # ==========================================
    # Company Signals
    # ==========================================
    
    @receiver(post_save, sender=Company)
    def update_company_search_vector(sender, instance, created, **kwargs):
        """
        Update search vector when company is created or updated.
        
        Search vector includes:
        - name (weight A)
        - description (weight B)
        """
        # Update the company's own search vector
        Company.objects.filter(pk=instance.pk).update(
            search_vector=(
                SearchVector('name', weight='A', config='english') +
                SearchVector('description', weight='B', config='english')
            )
        )
    
    
    @receiver(pre_save, sender=Company)
    def check_company_name_change(sender, instance, **kwargs):
        """
        Track if company name is changing to trigger cascading updates.
        
        Stores the old name on the instance for use in post_save signal.
        """
        if instance.pk:
            try:
                old_instance = Company.objects.get(pk=instance.pk)
                instance._old_name = old_instance.name
            except Company.DoesNotExist:
                instance._old_name = None
        else:
            instance._old_name = None
    
    
    @receiver(post_save, sender=Company)
    def cascade_company_name_updates(sender, instance, created, **kwargs):
        """
        When company name changes, update search vectors for related objects.
        
        Updates:
        - All RideModels from this manufacturer
        - All Rides from this manufacturer
        """
        # Skip if this is a new company or name hasn't changed
        if created or not hasattr(instance, '_old_name'):
            return
        
        old_name = getattr(instance, '_old_name', None)
        if old_name == instance.name:
            return
        
        # Update all RideModels from this manufacturer
        ride_models = RideModel.objects.filter(manufacturer=instance)
        for ride_model in ride_models:
            RideModel.objects.filter(pk=ride_model.pk).update(
                search_vector=(
                    SearchVector('name', weight='A', config='english') +
                    SearchVector('manufacturer__name', weight='A', config='english') +
                    SearchVector('description', weight='B', config='english')
                )
            )
        
        # Update all Rides from this manufacturer
        rides = Ride.objects.filter(manufacturer=instance)
        for ride in rides:
            Ride.objects.filter(pk=ride.pk).update(
                search_vector=(
                    SearchVector('name', weight='A', config='english') +
                    SearchVector('park__name', weight='A', config='english') +
                    SearchVector('manufacturer__name', weight='B', config='english') +
                    SearchVector('description', weight='B', config='english')
                )
            )
    
    
    # ==========================================
    # Park Signals
    # ==========================================
    
    @receiver(post_save, sender=Park)
    def update_park_search_vector(sender, instance, created, **kwargs):
        """
        Update search vector when park is created or updated.
        
        Search vector includes:
        - name (weight A)
        - description (weight B)
        """
        # Update the park's own search vector
        Park.objects.filter(pk=instance.pk).update(
            search_vector=(
                SearchVector('name', weight='A', config='english') +
                SearchVector('description', weight='B', config='english')
            )
        )
    
    
    @receiver(pre_save, sender=Park)
    def check_park_name_change(sender, instance, **kwargs):
        """
        Track if park name is changing to trigger cascading updates.
        
        Stores the old name on the instance for use in post_save signal.
        """
        if instance.pk:
            try:
                old_instance = Park.objects.get(pk=instance.pk)
                instance._old_name = old_instance.name
            except Park.DoesNotExist:
                instance._old_name = None
        else:
            instance._old_name = None
    
    
    @receiver(post_save, sender=Park)
    def cascade_park_name_updates(sender, instance, created, **kwargs):
        """
        When park name changes, update search vectors for related rides.
        
        Updates:
        - All Rides in this park
        """
        # Skip if this is a new park or name hasn't changed
        if created or not hasattr(instance, '_old_name'):
            return
        
        old_name = getattr(instance, '_old_name', None)
        if old_name == instance.name:
            return
        
        # Update all Rides in this park
        rides = Ride.objects.filter(park=instance)
        for ride in rides:
            Ride.objects.filter(pk=ride.pk).update(
                search_vector=(
                    SearchVector('name', weight='A', config='english') +
                    SearchVector('park__name', weight='A', config='english') +
                    SearchVector('manufacturer__name', weight='B', config='english') +
                    SearchVector('description', weight='B', config='english')
                )
            )
    
    
    # ==========================================
    # RideModel Signals
    # ==========================================
    
    @receiver(post_save, sender=RideModel)
    def update_ride_model_search_vector(sender, instance, created, **kwargs):
        """
        Update search vector when ride model is created or updated.
        
        Search vector includes:
        - name (weight A)
        - manufacturer__name (weight A)
        - description (weight B)
        """
        RideModel.objects.filter(pk=instance.pk).update(
            search_vector=(
                SearchVector('name', weight='A', config='english') +
                SearchVector('manufacturer__name', weight='A', config='english') +
                SearchVector('description', weight='B', config='english')
            )
        )
    
    
    @receiver(pre_save, sender=RideModel)
    def check_ride_model_manufacturer_change(sender, instance, **kwargs):
        """
        Track if ride model manufacturer is changing.
        
        Stores the old manufacturer on the instance for use in post_save signal.
        """
        if instance.pk:
            try:
                old_instance = RideModel.objects.get(pk=instance.pk)
                instance._old_manufacturer = old_instance.manufacturer
            except RideModel.DoesNotExist:
                instance._old_manufacturer = None
        else:
            instance._old_manufacturer = None
    
    
    # ==========================================
    # Ride Signals
    # ==========================================
    
    @receiver(post_save, sender=Ride)
    def update_ride_search_vector(sender, instance, created, **kwargs):
        """
        Update search vector when ride is created or updated.
        
        Search vector includes:
        - name (weight A)
        - park__name (weight A)
        - manufacturer__name (weight B)
        - description (weight B)
        """
        Ride.objects.filter(pk=instance.pk).update(
            search_vector=(
                SearchVector('name', weight='A', config='english') +
                SearchVector('park__name', weight='A', config='english') +
                SearchVector('manufacturer__name', weight='B', config='english') +
                SearchVector('description', weight='B', config='english')
            )
        )
    
    
    @receiver(pre_save, sender=Ride)
    def check_ride_relationships_change(sender, instance, **kwargs):
        """
        Track if ride park or manufacturer are changing.
        
        Stores old values on the instance for use in post_save signal.
        """
        if instance.pk:
            try:
                old_instance = Ride.objects.get(pk=instance.pk)
                instance._old_park = old_instance.park
                instance._old_manufacturer = old_instance.manufacturer
            except Ride.DoesNotExist:
                instance._old_park = None
                instance._old_manufacturer = None
        else:
            instance._old_park = None
            instance._old_manufacturer = None
