import warnings

from django.core.exceptions import ImproperlyConfigured
from django.utils.module_loading import autodiscover_modules, import_string
from django.utils.translation import gettext as _

from cms.app_base import CMSApp
from cms.exceptions import AppAlreadyRegistered
from cms.utils.conf import get_cms_setting


class ApphookPool:

    def __init__(self):
        self.apphooks = []
        self.apps = {}
        self.discovered = False

    def clear(self):
        # TODO: remove this method, it's Python, we don't need it.
        self.apphooks = []
        self.apps = {}
        self.discovered = False

    def register(self, app=None, discovering_apps=False):
        # allow use as a decorator
        if app is None:
            return lambda app: self.register(app, discovering_apps)

        if self.apphooks and not discovering_apps:
            return app

        if app.__name__ in self.apps:
            raise AppAlreadyRegistered(
                'A CMS application %r is already registered' % app.__name__
            )

        if not issubclass(app, CMSApp):
            raise ImproperlyConfigured(
                'CMS application must inherit from cms.app_base.CMSApp, but %r does not' % app.__name__
            )

        if not hasattr(app, 'menus') and hasattr(app, 'menu'):
            warnings.warn(
                "You define a 'menu' attribute on CMS application %r, but the 'menus' attribute is empty, "
                "did you make a typo?" % app.__name__
            )

        self.apps[app.__name__] = app()
        return app

    def discover_apps(self):
        self.apphooks = get_cms_setting('APPHOOKS')

        if self.apphooks:
            for path in self.apphooks:
                cls = import_string(path)
                try:
                    self.register(cls, discovering_apps=True)
                except AppAlreadyRegistered:
                    pass

        else:
            autodiscover_modules('cms_apps')

        self.discovered = True

    def get_apphooks(self):
        hooks = []

        if not self.discovered:
            self.discover_apps()

        for app_name in self.apps:
            app = self.apps[app_name]

            if app.get_urls():
                hooks.append((app_name, app.name))

        # Unfortunately, we lose the ordering since we now have a list of
        # tuples. Let's reorder by app_name:
        hooks = sorted(hooks, key=lambda hook: hook[1])

        return hooks

    def get_apphook(self, app_name):
        if not self.discovered:
            self.discover_apps()

        try:
            return self.apps[app_name]
        except KeyError:
            # deprecated: return apphooks registered in db with urlconf name
            # instead of apphook class name
            for app in self.apps.values():
                if app_name in app.get_urls():
                    return app

        warnings.warn(_('No registered apphook "%r" found') % app_name)
        return None


apphook_pool = ApphookPool()
