import logging

from django import template

from haystack.query import SearchQuerySet
from haystack.utils.app_loading import haystack_get_model

register = template.Library()


class MoreLikeThisNode(template.Node):
    def __init__(self, model, varname, for_types=None, limit=None):
        self.model = template.Variable(model)
        self.varname = varname
        self.for_types = for_types
        self.limit = limit

        if self.limit is not None:
            self.limit = int(self.limit)

    def render(self, context):
        try:
            model_instance = self.model.resolve(context)
            sqs = SearchQuerySet()

            if self.for_types is not None:
                intermediate = template.Variable(self.for_types)
                for_types = intermediate.resolve(context).split(",")
                search_models = []

                for model in for_types:
                    model_class = haystack_get_model(*model.split("."))

                    if model_class:
                        search_models.append(model_class)

                sqs = sqs.models(*search_models)

            sqs = sqs.more_like_this(model_instance)

            if self.limit is not None:
                sqs = sqs[: self.limit]

            context[self.varname] = sqs
        except Exception:
            logging.exception(
                "Unhandled exception rendering %r",
                self,
                level=logging.WARNING,
            )

        return ""


@register.tag
def more_like_this(parser, token):
    """
    Fetches similar items from the search index to find content that is similar
    to the provided model's content.

    Syntax::

        {% more_like_this model_instance as varname [for app_label.model_name,app_label.model_name,...] [limit n] %}

    Example::

        # Pull a full SearchQuerySet (lazy loaded) of similar content.
        {% more_like_this entry as related_content %}

        # Pull just the top 5 similar pieces of content.
        {% more_like_this entry as related_content limit 5  %}

        # Pull just the top 5 similar entries or comments.
        {% more_like_this entry as related_content for "blog.entry,comments.comment" limit 5  %}
    """
    bits = token.split_contents()

    if len(bits) not in (4, 6, 8):
        raise template.TemplateSyntaxError(
            "'%s' tag requires either 3, 5 or 7 arguments." % bits[0]
        )

    model = bits[1]

    if bits[2] != "as":
        raise template.TemplateSyntaxError(
            "'%s' tag's second argument should be 'as'." % bits[0]
        )

    varname = bits[3]
    limit = None
    for_types = None

    if len(bits) == 6:
        if bits[4] != "limit" and bits[4] != "for":
            raise template.TemplateSyntaxError(
                "'%s' tag's fourth argument should be either 'limit' or 'for'."
                % bits[0]
            )

        if bits[4] == "limit":
            limit = bits[5]
        else:
            for_types = bits[5]

    if len(bits) == 8:
        if bits[4] != "for":
            raise template.TemplateSyntaxError(
                "'%s' tag's fourth argument should be 'for'." % bits[0]
            )

        for_types = bits[5]

        if bits[6] != "limit":
            raise template.TemplateSyntaxError(
                "'%s' tag's sixth argument should be 'limit'." % bits[0]
            )

        limit = bits[7]

    return MoreLikeThisNode(model, varname, for_types, limit)
