"""Signals for the resources application.

Based off https://simonwillison.net/2017/Oct/5/django-postgresql-faceted-search/
"""


from django.dispatch import receiver
from django.db.models.signals import (
    post_save,
    # m2m_changed,
)
from django.db.models import Value
from django.contrib.postgres.search import SearchVector
from django.db import transaction
from resources.models import (
    Resource,
)


SEARCH_INDEX_UPDATE_MODELS = (
    Resource,
)


@receiver(post_save)
def on_save(sender, **kwargs):
    """Trigger functions after model save."""
    if issubclass(sender, SEARCH_INDEX_UPDATE_MODELS):
        transaction.on_commit(make_updater(kwargs['instance']))


# TODO: Update on M2M relationships
# @receiver(m2m_changed)
# def on_m2m_changed(sender, **kwargs):
#     instance = kwargs['instance']
#     model = kwargs['model']
#     if model is Tag:
#         transaction.on_commit(make_updater(instance))
#     elif isinstance(instance, Tag):
#         for obj in model.objects.filter(pk__in=kwargs['pk_set']):
#             transaction.on_commit(make_updater(obj))


def make_updater(instance):
    """Return function for updating search index of resource."""
    components = instance.index_contents()
    pk = instance.pk

    def on_commit():
        search_vector_list = []
        for weight, text in components.items():
            search_vector_list.append(
                SearchVector(Value(text), weight=weight)
            )
        search_vectors = search_vector_list[0]
        for search_vector in search_vector_list[1:]:
            search_vectors += search_vector

        instance.__class__.objects.filter(pk=pk).update(
            search_vector=search_vectors
        )

    return on_commit

"""Signals for the resources application.

Based off https://simonwillison.net/2017/Oct/5/django-postgresql-faceted-search/
"""


from django.dispatch import receiver
from django.db.models.signals import (
    post_save,
    m2m_changed,
)
from django.db.models import Value
from django.contrib.postgres.search import SearchVector
from django.db import transaction
from resources.models import (
    Resource,
)


SEARCH_INDEX_UPDATE_MODELS = (
    Resource,
)


@receiver(post_save)
def on_save(sender, **kwargs):
    """Trigger functions after any model save."""
    if issubclass(sender, SEARCH_INDEX_UPDATE_MODELS):
        transaction.on_commit(make_updater(kwargs['instance']))


@receiver(m2m_changed)
def on_m2m_changed(sender, **kwargs):
    """Update search indexes on m2m relationship changes.

    When a many to many (M2M) relationship is changed, the
    search index should be updated for models that require is.
    This only applies to the text search, filter searches are always
    up to date.

    If an object changes, any items pointing to that object also
    updates their index as the text may have changed it uses
    for indexing.
    """
    instance = kwargs['instance']
    model = kwargs['model']
    if issubclass(model, SEARCH_INDEX_UPDATE_MODELS):
        transaction.on_commit(make_updater(instance))
    elif isinstance(instance, SEARCH_INDEX_UPDATE_MODELS):
        for obj in model.objects.filter(pk__in=kwargs['pk_set']):
            transaction.on_commit(make_updater(obj))


def make_updater(instance):
    """Return function for updating search index of resource."""
    components = instance.index_contents()
    pk = instance.pk

    def on_commit():
        search_vector_list = []
        for weight, text in components.items():
            search_vector_list.append(
                SearchVector(Value(text), weight=weight)
            )
        search_vectors = search_vector_list[0]
        for search_vector in search_vector_list[1:]:
            search_vectors += search_vector

        instance.__class__.objects.filter(pk=pk).update(
            search_vector=search_vectors
        )

    return on_commit

Update search indexes on M2M changes