from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.utils.encoding import force_text
from django.utils.module_loading import import_string

from channels.routing import ProtocolTypeRouter, URLRouter

from ..utils import remove_www, get_domain_model
from ..volatile import VolatileTenant
from .auth import TenantAuthMiddlewareStack


class TenantProtocolRouter:
    """
    ProtocolRouter that handles multi-tenancy.
    """

    TENANT_NOT_FOUND_EXCEPTION = Http404

    def __init__(self):
        self.root_ws_urlconf = settings.TENANTS["default"].get("WS_URLCONF")
        if self.root_ws_urlconf is None:
            raise ImproperlyConfigured(
                "TENANTS['default'] must contain a 'WS_URLCONF' key in order to use TenantProtocolRouter."
            )

    def get_tenant_scope(self, scope):
        """
        Get tenant and websockets urlconf based on scope host.
        """
        hostname = force_text(dict(scope["headers"]).get(b"host", b""))
        hostname = remove_www(hostname.split(":")[0])

        tenant = None
        ws_urlconf = self.root_ws_urlconf

        # Checking for static tenants
        for schema, data in settings.TENANTS.items():
            if schema in ["public", "default"]:
                continue
            if hostname in data["DOMAINS"]:
                tenant = VolatileTenant.create(schema_name=schema, domain_url=hostname)
                if "WS_URLCONF" in data:
                    ws_urlconf = data["WS_URLCONF"]
                return tenant, import_string(ws_urlconf + ".urlpatterns")
        # Checking for dynamic tenants
        else:
            DomainModel = get_domain_model()
            try:
                domain = DomainModel.objects.select_related("tenant").get(domain=hostname)
                tenant = domain.tenant
            except DomainModel.DoesNotExist:
                raise self.TENANT_NOT_FOUND_EXCEPTION("No tenant for hostname '%s'" % hostname)
            tenant.domain_url = hostname
            ws_urlconf = settings.TENANTS["default"]["WS_URLCONF"]
            return tenant, import_string(ws_urlconf + ".urlpatterns")

    def get_protocol_type_router(self, ws_urlconf):
        """
        Subclasses can override this to include more protocols.
        """
        return ProtocolTypeRouter({"websocket": TenantAuthMiddlewareStack(URLRouter(ws_urlconf))})

    def __call__(self, scope):
        tenant, ws_urlconf = self.get_tenant_scope(scope)
        scope.update({"tenant": tenant})
        return self.get_protocol_type_router(ws_urlconf)(scope)
