from django.apps import apps
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.deprecation import MiddlewareMixin
from django_tenants.utils import get_public_schema_name, tenant_context


class TenantHTTPMiddleware(MiddlewareMixin):
    """
    Middleware for resolving tenant in standard Django HTTP requests.
    Lazily sets tenant or public tenant.
    """

    def __init__(self, get_response=None):
        super().__init__(get_response)
        self.get_response = get_response
        self._tenant_model = None
        self._resolver = None

    @property
    def tenant_model(self):
        if self._tenant_model is None:
            # Try to get the model path from settings
            model_path = getattr(settings, "CHANNELS_MULTITENANT_TENANT_MODEL", None)
            if model_path is None:
                raise ImproperlyConfigured(
                    "You must define CHANNELS_MULTITENANT_TENANT_MODEL in your Django settings!"
                )

            # If model_path is a string like "app_label.ModelName"
            if isinstance(model_path, str):
                try:
                    app_label, model_name = model_path.split(".")
                    self._tenant_model = apps.get_model(app_label, model_name)
                except (ValueError, LookupError) as e:
                    raise ImproperlyConfigured(
                        f"Invalid CHANNELS_MULTITENANT_TENANT_MODEL setting: {model_path}. Error: {e}"
                    )
            else:
                # If someone passed the actual model class instead of string
                self._tenant_model = model_path

        return self._tenant_model

    @property
    def resolver(self):
        if self._resolver is None:
            resolver_class = getattr(
                settings, "CHANNELS_MULTITENANT_RESOLVER_CLASS", None
            )
            resolver_args = getattr(settings, "CHANNELS_MULTITENANT_RESOLVER_ARGS", {})
            if resolver_class is None:
                from .resolvers import FlexibleSubdomainTenantResolver

                resolver_class = FlexibleSubdomainTenantResolver
                resolver_args.setdefault(
                    "base_domain", getattr(settings, "BASE_DOMAIN", "example.com")
                )
            self._resolver = resolver_class(**resolver_args)
        return self._resolver

    def process_request(self, request):
        host = request.get_host().split(":")[0]
        base_domain = getattr(settings, "BASE_DOMAIN", "example.com")

        tenant_name = self.resolver.resolve(host) if self.resolver else None
        tenant = None

        if tenant_name:
            try:
                tenant = self.tenant_model.objects.get(schema_name=tenant_name)
            except self.tenant_model.DoesNotExist:
                pass

        # If host is exactly the base domain, use public tenant object
        if host == base_domain:
            tenant = self.tenant_model.objects.get(schema_name=get_public_schema_name())

        # Fallback to public schema
        if tenant:
            request.tenant = tenant
            request.tenant_context = tenant_context(tenant)
            request.tenant_context.__enter__()
        else:
            # Should rarely happen, but just in case
            request.tenant = None
            request.tenant_context = tenant_context(get_public_schema_name())
            request.tenant_context.__enter__()

    def process_response(self, request, response):
        if hasattr(request, "tenant_context"):
            request.tenant_context.__exit__(None, None, None)
        return response
