"""
Declare and configure the models for the courses application
"""
from django.apps import apps
from django.conf import settings
from django.db import models
from django.db.models import Prefetch
from django.utils import translation
from django.utils.translation import ugettext_lazy as _

from cms.api import Page
from cms.extensions.extension_pool import extension_pool
from cms.models import Title
from cms.models.pluginmodel import CMSPlugin

from ...core.models import BasePageExtension, PagePluginMixin


class Category(BasePageExtension):
    """
    The category page extension represents and records a thematic in the catalog.

    This model should be used to record structured data about the thematic whereas the
    associated page object is where we record the less structured information to display on the
    page that presents the thematic.
    """

    ROOT_REVERSE_ID = "categories"
    TEMPLATE_DETAIL = "courses/cms/category_detail.html"

    class Meta:
        verbose_name = _("category")
        ordering = ["-pk"]

    def __str__(self):
        """Human representation of a category"""
        ancestors = self.extended_object.get_ancestor_pages().filter(
            category__isnull=False
        )
        return "{ancestors:s}{title:s}".format(
            ancestors="{:s} / ".format(" / ".join([a.get_title() for a in ancestors]))
            if ancestors
            else "",
            title=self.extended_object.get_title(),
        )

    def get_courses(self, language=None):
        """
        Return a query to get the courses related to this category ie for which a plugin for
        this category is linked to the course page on the "course_categories" placeholder.
        """
        page = (
            self.extended_object
            if self.extended_object.publisher_is_draft
            else self.draft_extension.extended_object
        )
        language = language or translation.get_language()
        bfs = "extended_object__placeholders__cmsplugin__courses_categorypluginmodel__page"
        filter_dict = {
            "extended_object__node__parent__cms_pages__course__isnull": True,
            "extended_object__publisher_is_draft": True,
            "extended_object__placeholders__slot": "course_categories",
            "extended_object__placeholders__cmsplugin__language": language,
            bfs: page,
            "{:s}__publisher_is_draft".format(bfs): True,
        }
        course_model = apps.get_model(app_label="courses", model_name="course")
        # pylint: disable=no-member
        return (
            course_model.objects.filter(**filter_dict)
            .select_related("extended_object")
            .prefetch_related(
                Prefetch(
                    "extended_object__title_set",
                    to_attr="prefetched_titles",
                    queryset=Title.objects.filter(language=language),
                )
            )
            .distinct()
        )


def get_category_limit_choices_to():
    """Return a query limiting the categories proposed when creating a CategoryPlugin."""
    limit_choices_to = {
        "publisher_is_draft": True,  # plugins work with draft instances
        "category__isnull": False,  # limit to pages linked to a category object
        "node__parent__cms_pages__category__isnull": False,  # exclude meta categories
    }
    # Limit to leaf categories only if active in settings (False by default)
    if getattr(settings, "LIMIT_PLUGIN_CATEGORIES_TO_LEAF", False):
        limit_choices_to["node__numchild"] = 0

    return limit_choices_to


class CategoryPluginModel(PagePluginMixin, CMSPlugin):
    """
    Category plugin model handles the relation between CategoryPlugin
    and their Category instance

    We only propose to link category objects:
    - are not at the root of the category tree (this position defines a meta category which
      is used to define a filter bank on the search page e.g. Subjects, Levels).
    - that are a leaf in the category tree (can be deactivated with setting
      `LIMIT_PLUGIN_CATEGORIES_TO_LEAF`),
    """

    page = models.ForeignKey(
        Page,
        related_name="category_plugins",
        limit_choices_to=get_category_limit_choices_to,
        on_delete=models.CASCADE,
    )

    class Meta:
        verbose_name = _("category plugin model")

    def __str__(self):
        """Human representation of a page plugin"""
        return "{model:s}: {id:d}".format(
            model=self._meta.verbose_name.title(), id=self.id
        )


extension_pool.register(Category)
