import copy
import sys

from django.apps import apps as django_apps
from django.utils.module_loading import import_module, module_has_submodule


class AlreadyRegistered(Exception):
    pass


class RegistryNotLoaded(Exception):
    pass


class SiteDataframeHandlerNotFound(Exception):
    pass


__all__ = ["site_dataframe_handlers"]


class Site:
    def __init__(self):
        self.registry = {}
        self.model_reference_registry = {}
        self.loaded = False

    def register(self, handler_cls=None, app_labels=None, models=None):
        app_labels = app_labels or []
        models = models or []
        name = handler_cls.__name__
        if name in self.registry:
            raise AlreadyRegistered(
                f"Dataframe handler class already been registered. " f"Got {name}"
            )
        self.registry.update({name: handler_cls})
        for app_label in app_labels:
            self.model_reference_registry.update({app_label: name})
        for model in models:
            self.model_reference_registry.update({model: name})
        self.loaded = True

    def get_for(self, key=None):
        if not self.loaded:
            raise RegistryNotLoaded("site_dataframe_handlers not loaded")
        handler_cls = None
        try:
            handler_cls = self.registry[key]
        except KeyError:
            pass
        try:
            handler_cls = self.registry[self.model_reference_registry.get(key)]
        except KeyError:
            pass
        try:
            handler_cls = self.registry[self.model_reference_registry.get(key.split(".")[0])]
        except KeyError:
            pass
        if not handler_cls:
            keys = list(self.registry.keys()) + list(self.model_reference_registry.keys())
            raise SiteDataframeHandlerNotFound(
                f"Cannot find dataframe hander. Got {key}. " f"Expected one of {keys}."
            )
        return handler_cls

    def autodiscover(self, module_name=None):
        """Autodiscovers classes in the site_dataframe_handlers.py file of any
        INSTALLED_APP.
        """
        module_name = module_name or "site_dataframe_handlers"
        sys.stdout.write(f" * checking for {module_name} ...\n")
        for app in django_apps.app_configs:
            try:
                mod = import_module(app)
                try:
                    before_import_registry = copy.copy(site_dataframe_handlers.registry)
                    import_module(f"{app}.{module_name}")
                    sys.stdout.write(
                        f" * registered dataframe handler from application '{app}'\n"
                    )
                except (ImportError, ModuleNotFoundError) as e:
                    if f"No module named '{app}.{module_name}'" not in str(e):
                        site_dataframe_handlers.registry = before_import_registry
                        if module_has_submodule(mod, module_name):
                            raise
            except ModuleNotFoundError:
                pass


site_dataframe_handlers = Site()
