from django.dispatch import receiver
from django.db.models.signals import post_save, m2m_changed
from django.db.models import Value, TextField
from django.contrib.postgres.search import SearchVector
from django.db import transaction
from blog.models import BaseModel, Tag
import operator
from functools import reduce


@receiver(post_save)
def on_save(sender, **kwargs):
    if not issubclass(sender, BaseModel):
        return
    transaction.on_commit(make_updater(kwargs["instance"]))


@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):
    components = instance.index_components()
    pk = instance.pk

    def on_commit():
        search_vectors = []
        for weight, text in list(components.items()):
            search_vectors.append(
                SearchVector(Value(text, output_field=TextField()), weight=weight)
            )
        instance.__class__.objects.filter(pk=pk).update(
            search_document=reduce(operator.add, search_vectors)
        )

    return on_commit