from importlib import import_module

try:
    from django.core.urlresolvers import reverse
except ImportError:  # Django 1.11
    from django.urls import reverse

from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _

from jet.dashboard import modules
from jet.dashboard.models import UserDashboardModule
from jet.ordered_set import OrderedSet
from jet.utils import context_to_dict, get_admin_site_name

try:
    from django.template.context_processors import csrf
except ImportError:
    from django.core.context_processors import csrf


class Dashboard(object):
    """
    Base dashboard class. All custom dashboards should inherit it.
    """

    #: Number of columns in which widgets can be placed
    columns = 2

    #: Dashboard Modules (widgets) that dashboard is filled with, when the user open it for the first time
    #:
    #: List of dashboard module **instances**
    children = None

    #: Dashboard Modules (widgets) that user can add to dashboard at any time
    # (not created when the user open dashboard for the first time)
    #:
    #: List of dashboard module **classes**
    available_children = None
    app_label = None
    context = None
    modules = None

    class Media:
        css = ()
        js = ()

    def __init__(self, context, **kwargs):
        for key in kwargs:
            if hasattr(self.__class__, key):
                setattr(self, key, kwargs[key])
        self.children = self.children or []
        self.available_children = self.available_children or []
        self.set_context(context)

    def set_context(self, context):
        self.context = context
        self.init_with_context(context)
        self.load_modules()

    def init_with_context(self, context):
        """
        Override this method to fill your custom **Dashboard** class with widgets.
        You should add your widgets to ``children`` and ``available_children`` attributes.

        Usage example:

        .. code-block:: python

            from django.utils.translation import ugettext_lazy as _
            from jet.dashboard import modules
            from jet.dashboard.dashboard import Dashboard, AppIndexDashboard


            class CustomIndexDashboard(Dashboard):
                columns = 3

                def init_with_context(self, context):
                    self.available_children.append(modules.LinkList)
                    self.children.append(modules.LinkList(
                        _('Support'),
                        children=[
                            {
                                'title': _('Django documentation'),
                                'url': 'http://docs.djangoproject.com/',
                                'external': True,
                            },
                            {
                                'title': _('Django "django-users" mailing list'),
                                'url': 'http://groups.google.com/group/django-users',
                                'external': True,
                            },
                            {
                                'title': _('Django irc channel'),
                                'url': 'irc://irc.freenode.net/django',
                                'external': True,
                            },
                        ],
                        column=0,
                        order=0
                    ))

        """
        pass

    def load_module(self, module_fullname):
        package, module_name = module_fullname.rsplit(".", 1)
        package = import_module(package)
        module = getattr(package, module_name)

        return module

    def create_initial_module_models(self, user):
        module_models = []

        i = 0

        for module in self.children:
            column = module.column if module.column is not None else i % self.columns
            order = module.order if module.order is not None else int(i / self.columns)

            module_models.append(
                UserDashboardModule.objects.create(
                    title=module.title,
                    app_label=self.app_label,
                    user=user.pk,
                    module=module.fullname(),
                    column=column,
                    order=order,
                    settings=module.dump_settings(),
                    children=module.dump_children(),
                )
            )
            i += 1

        return module_models

    def load_modules(self):
        module_models = UserDashboardModule.objects.filter(
            app_label=self.app_label, user=self.context["request"].user.pk
        ).all()

        if len(module_models) == 0:
            module_models = self.create_initial_module_models(
                self.context["request"].user
            )

        loaded_modules = []

        for module_model in module_models:
            module_cls = module_model.load_module()
            if module_cls is not None:
                module = module_cls(model=module_model, context=self.context)
                loaded_modules.append(module)

        self.modules = loaded_modules

    def render(self):
        context = context_to_dict(self.context)
        context.update(
            {
                "columns": range(self.columns),
                "modules": self.modules,
                "app_label": self.app_label,
            }
        )
        context.update(csrf(context["request"]))

        return render_to_string("jet.dashboard/dashboard.html", context)

    def render_tools(self):
        context = context_to_dict(self.context)
        context.update(
            {
                "children": self.children,
                "app_label": self.app_label,
                "available_children": self.available_children,
            }
        )
        context.update(csrf(context["request"]))

        return render_to_string("jet.dashboard/dashboard_tools.html", context)

    def media(self):
        unique_css = OrderedSet()
        unique_js = OrderedSet()

        for js in getattr(self.Media, "js", ()):
            unique_js.add(js)
        for css in getattr(self.Media, "css", ()):
            unique_css.add(css)

        for module in self.modules:
            for js in getattr(module.Media, "js", ()):
                unique_js.add(js)
            for css in getattr(module.Media, "css", ()):
                unique_css.add(css)

        class Media:
            css = list(unique_css)
            js = list(unique_js)

        return Media


class AppIndexDashboard(Dashboard):
    def get_app_content_types(self):
        return (self.app_label + ".*",)

    def models(self):
        return (self.app_label + ".*",)


class DefaultIndexDashboard(Dashboard):
    columns = 3

    def init_with_context(self, context):
        self.available_children.append(modules.LinkList)
        self.available_children.append(modules.Feed)

        site_name = get_admin_site_name(context)
        # append a link list module for "quick links"
        self.children.append(
            modules.LinkList(
                _("Quick links"),
                layout="inline",
                draggable=False,
                deletable=False,
                collapsible=False,
                children=[
                    [_("Return to site"), "/"],
                    [_("Change password"), reverse("%s:password_change" % site_name)],
                    [_("Log out"), reverse("%s:logout" % site_name)],
                ],
                column=0,
                order=0,
            )
        )

        # append an app list module for "Applications"
        self.children.append(
            modules.AppList(_("Applications"), exclude=("auth.*",), column=1, order=0)
        )

        # append an app list module for "Administration"
        self.children.append(
            modules.AppList(_("Administration"), models=("auth.*",), column=2, order=0)
        )

        # append a recent actions module
        self.children.append(
            modules.RecentActions(_("Recent Actions"), 10, column=0, order=1)
        )

        # append a feed module
        self.children.append(
            modules.Feed(
                _("Latest Django News"),
                feed_url="http://www.djangoproject.com/rss/weblog/",
                limit=5,
                column=1,
                order=1,
            )
        )

        # append another link list module for "support".
        self.children.append(
            modules.LinkList(
                _("Support"),
                children=[
                    {
                        "title": _("Django documentation"),
                        "url": "http://docs.djangoproject.com/",
                        "external": True,
                    },
                    {
                        "title": _('Django "django-users" mailing list'),
                        "url": "http://groups.google.com/group/django-users",
                        "external": True,
                    },
                    {
                        "title": _("Django irc channel"),
                        "url": "irc://irc.freenode.net/django",
                        "external": True,
                    },
                ],
                column=2,
                order=1,
            )
        )


class DefaultAppIndexDashboard(AppIndexDashboard):
    def init_with_context(self, context):
        self.available_children.append(modules.LinkList)

        self.children.append(
            modules.ModelList(
                title=_("Application models"), models=self.models(), column=0, order=0
            )
        )
        self.children.append(
            modules.RecentActions(
                include_list=self.get_app_content_types(), column=1, order=0
            )
        )


class DashboardUrls(object):
    _urls = []

    def get_urls(self):
        return self._urls

    def register_url(self, url):
        self._urls.append(url)

    def register_urls(self, urls):
        self._urls.extend(urls)


urls = DashboardUrls()
