# tenant_context.py

from contextlib import contextmanager
from contextvars import ContextVar, Token

from fastapi import HTTPException, Request

from .service import TenantConfigService


_current_request_id: ContextVar[str | None] = ContextVar("request_id", default=None)
_current_tenant: ContextVar[str | None] = ContextVar("tenant", default=None)
_current_user_id: ContextVar[str | None] = ContextVar("user_id", default=None)


class TenantContextConfig:
    def __init__(
        self,
        tenants_source: str,
        *,
        tenant_header: str = "X-Tenant",
        user_id_header: str = "X-UserId",
        request_id_header: str = "X-Request-Id",
        default_tenant: str | None = None,
        default_user_id: str | None = None,
        allowed_tenants: set[str] | None = None,
        strict_whitelist: bool = False,
    ):
        self.tenant_header = tenant_header
        self.user_id_header = user_id_header
        self.request_id_header = request_id_header

        self.default_tenant = default_tenant
        self.default_user_id = default_user_id

        self.allowed_tenants = allowed_tenants
        self.strict_whitelist = strict_whitelist

        self.service = TenantConfigService(tenants_source)

    # -------------------------
    # TENANT
    # -------------------------

    def get_tenant(self, request: Request) -> str:
        tenant = request.headers.get(self.tenant_header)

        if tenant and tenant.strip():
            tenant = tenant.strip().lower()
        else:
            tenant = self.default_tenant

        if not tenant:
            raise HTTPException(400, f"Header {self.tenant_header} mancante")

        if self.allowed_tenants and self.strict_whitelist:
            if tenant not in self.allowed_tenants:
                raise HTTPException(403, "Tenant non consentito")

        try:
            self.service.get(tenant)
        except FileNotFoundError:
            raise HTTPException(404, f"Tenant '{tenant}' non trovato")

        return tenant

    def get_tenant_config(self, request: Request):
        return self.service.get(self.get_tenant(request))

    # -------------------------
    # HEADERS
    # -------------------------

    def get_user_id(self, request: Request) -> str | None:
        user_id = request.headers.get(self.user_id_header)
        return user_id.strip() if user_id and user_id.strip() else self.default_user_id

    def get_request_id(self, request: Request) -> str | None:
        request_id = request.headers.get(self.request_id_header)
        return request_id.strip() if request_id and request_id.strip() else None

    # -------------------------
    # CONTEXT
    # -------------------------

    @contextmanager
    def tenant_context(self, tenant: str | None):
        token: Token = _current_tenant.set(tenant)
        try:
            yield
        finally:
            _current_tenant.reset(token)

    @contextmanager
    def user_id_context(self, user_id: str | None):
        token: Token = _current_user_id.set(user_id)
        try:
            yield
        finally:
            _current_user_id.reset(token)

    @contextmanager
    def request_id_context(self, request_id: str | None):
        token: Token = _current_request_id.set(request_id)
        try:
            yield
        finally:
            _current_request_id.reset(token)

    # -------------------------
    # CURRENT
    # -------------------------

    def current_tenant(self) -> str | None:
        return _current_tenant.get()

    def current_user_id(self) -> str | None:
        return _current_user_id.get()

    def current_request_id(self) -> str | None:
        return _current_request_id.get()