class BaseTenantResolver:
    """
    Base class for tenant resolvers.

    A tenant resolver extracts a tenant identifier from an incoming request.
    The identifier can be derived from different sources such as the host
    header, specific request headers, or query parameters.

    Subclasses must implement the `resolve()` method.
    """

    def resolve(self, scope) -> str | None:
        """
        Extract tenant identifier from the ASGI scope.

        Parameters
        ----------
        scope : dict
            ASGI scope containing request metadata, including headers.

        Returns
        -------
        str | None
            Resolved tenant identifier, or None if the tenant
            cannot be determined.
        """
        raise NotImplementedError("Subclasses must implement `resolve()`.")


class FlexibleSubdomainTenantResolver(BaseTenantResolver):
    """
    Resolve tenant from host supporting multiple separator formats.

    This resolver extracts the tenant identifier from the beginning
    of the host name while supporting multiple separator styles.

    Supported host formats
    ---------------------
    Dash separated:
        tenant1-app.example.com → tenant1

    Dot separated:
        tenant1.app.example.com → tenant1

    Base domain only:
        app.example.com → None

    Notes
    -----
    - Any port in the host will be automatically removed.
    - The host must end with the configured `base_domain`.
    """

    def __init__(self, base_domain: str, separators: tuple[str, ...] = ("-", ".")):
        self.base_domain = base_domain.lower()
        self.separators = separators

    def resolve(self, scope) -> str | None:
        """
        Extract tenant identifier from the host header.

        Parameters
        ----------
        scope : dict or str
            ASGI scope containing request headers or a host string.

        Returns
        -------
        str | None
            Tenant identifier if found, otherwise None.
        """
        # Support plain host string for backward compatibility
        if isinstance(scope, str):
            host = scope.split(":")[0].lower()
        elif isinstance(scope, dict):
            headers = dict(scope.get("headers", []))
            host_header = headers.get(b"host")
            if not host_header:
                return None
            host = host_header.decode().split(":")[0].lower()
        else:
            return None

        if not host.endswith(self.base_domain):
            return None

        prefix = host[: -len(self.base_domain)].rstrip(".")
        if not prefix:
            return None

        for sep in self.separators:
            if sep in prefix:
                return prefix.split(sep)[0]

        return prefix


class HeaderTenantResolver(BaseTenantResolver):
    """
    Resolve tenant using a request header.

    This resolver extracts the tenant identifier from a specific
    HTTP header in the request.

    Example
    -------
    Request header:
        X-Tenant-ID: tenant1

    Result:
        tenant1
    """

    def __init__(self, header_name: str = "x-tenant-id"):
        self.header_name = header_name.lower().encode()

    def resolve(self, scope) -> str | None:
        """
        Extract tenant identifier from request headers.

        Parameters
        ----------
        scope : dict or list of tuples
            ASGI scope containing request headers or a list of headers.

        Returns
        -------
        str | None
            Tenant identifier if header is present, otherwise None.
        """
        headers_dict = {}

        if isinstance(scope, dict):
            headers_dict = dict(scope.get("headers", []))
        elif isinstance(scope, (list, tuple)):
            headers_dict = dict(scope)
        else:
            return None

        tenant_header = headers_dict.get(self.header_name)
        if not tenant_header:
            return None
        return tenant_header.decode().strip()
