"""Resolve the tenant for an incoming request.

Resolution strategies (first hit wins):
  1. `X-Tenant` header (explicit; useful for API clients and tests).
  2. Subdomain (`{slug}.example.com`) — production default.
  3. Authenticated user's primary membership (fallback for single-tenant users).

The strategy chain is configurable so deployments that don't use subdomains
(typical cPanel single-domain setup) can drop that link without code changes.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Protocol

from django.conf import settings

from simorgh.apps.tenants.models import Tenant, TenantStatus

if TYPE_CHECKING:
    from django.http import HttpRequest


class TenantResolverStrategy(Protocol):
    def __call__(self, request: HttpRequest) -> Tenant | None: ...


def resolve_from_header(request: HttpRequest) -> Tenant | None:
    slug = request.headers.get("X-Tenant") or request.META.get("HTTP_X_TENANT")
    if not slug:
        return None
    tenant = Tenant.objects.filter(slug=slug, status=TenantStatus.ACTIVE).first()
    if tenant is None:
        return None
    # Verify the authenticated user is an active member of the requested tenant.
    # Unauthenticated requests (e.g. login endpoint) are allowed through; the
    # individual views are responsible for requiring authentication.
    user = getattr(request, "user", None)
    if user is not None and user.is_authenticated:
        from simorgh.apps.memberships.models import Membership, MembershipStatus
        is_member = Membership.objects.filter(
            tenant=tenant,
            users=user,
            status=MembershipStatus.ACTIVE,
        ).exists()
        if not is_member and not user.is_superuser:
            return None
    return tenant


def resolve_from_subdomain(request: HttpRequest) -> Tenant | None:
    host = request.get_host().split(":")[0]
    parts = host.split(".")
    base_parts = int(getattr(settings, "TENANT_BASE_DOMAIN_PARTS", 2))
    if len(parts) <= base_parts:
        return None
    slug = parts[0]
    if slug in {"www", "admin", "api"}:
        return None
    return Tenant.objects.filter(slug=slug, status=TenantStatus.ACTIVE).first()


def resolve_from_session(request: HttpRequest) -> Tenant | None:
    """Return the tenant the user explicitly selected via /auth/switch-tenant/.

    The slug is stored in ``request.session["active_tenant_slug"]`` by the
    switch-tenant view, so subsequent requests in the same browser session
    automatically carry the right tenant without needing the X-Tenant header.
    """
    slug = getattr(request, "session", {}).get("active_tenant_slug")
    if not slug:
        return None
    return Tenant.objects.filter(slug=slug, status=TenantStatus.ACTIVE).first()


def resolve_from_membership(request: HttpRequest) -> Tenant | None:
    user = getattr(request, "user", None)
    if not user or not user.is_authenticated:
        return None
    # Avoid a hard import cycle at module load: memberships imports tenants.
    from simorgh.apps.memberships.models import Membership

    membership = (
        Membership.objects.filter(users=user, tenant__status=TenantStatus.ACTIVE)
        .select_related("tenant")
        .order_by("created_at")
        .first()
    )
    return membership.tenant if membership else None


DEFAULT_STRATEGIES: tuple[TenantResolverStrategy, ...] = (
    resolve_from_header,
    resolve_from_subdomain,
    resolve_from_session,      # explicit selection persisted in the browser session
    resolve_from_membership,   # fallback: auto-resolve for single-tenant users
)


def resolve_tenant(
    request: HttpRequest,
    strategies: tuple[TenantResolverStrategy, ...] | None = None,
) -> Tenant | None:
    for strategy in strategies or DEFAULT_STRATEGIES:
        tenant = strategy(request)
        if tenant is not None:
            return tenant
    return None
