from contextlib import contextmanager
from contextvars import ContextVar, Token

from fastapi import HTTPException, Request

from .db_session import TenantDatabaseManager
from .service import TenantConfigService, TenantRuntimeConfigService

####logging di tenant e requestid
_current_request_id: ContextVar[str | None] = ContextVar(
    "current_request_id",
    default=None,
)

_current_tenant: ContextVar[str | None] = ContextVar(
    "current_tenant",
    default=None,
)

_current_user_id: ContextVar[str | None] = ContextVar(
    "current_user_id",
    default=None,
)

class MultitenantConfig:
    def __init__(
        self,
        tenants_source: str,
        *,
        #settings,
        tenant_header: str = "X-Tenant",
        user_id_header: str = "X-UserId",
        default_tenant: str | None = None,
        default_user_id: str | None = None,
        allowed_tenants: set[str] | None = None,
        strict_whitelist: bool = False,
        mode: str = "testing",
        echo_sql: bool = False,
        pool_size: int = 3,
        max_overflow: int = 0,
        pool_timeout: int = 5,
        pool_pre_ping: bool = True,
        app_name_prefix: str = "fastapi",
    ):
        #self.settings = settings

        self.tenant_header = tenant_header
        self.user_id_header = user_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.mode = mode
        self.echo_sql = echo_sql
        self.pool_size = pool_size
        self.max_overflow = max_overflow
        self.pool_timeout = pool_timeout
        self.pool_pre_ping = pool_pre_ping
        self.app_name_prefix = app_name_prefix

        self.service = TenantConfigService(tenants_source)
        ###per ora commentata da vedere (https://chatgpt.com/c/69be4f40-1070-832a-ad71-6889e885bef3)
        #self.runtime = TenantRuntimeConfigService(self.service, settings)
        self.db = TenantDatabaseManager(self)
        
        
    # -------------------------
    # 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 Exception:
            raise HTTPException(404, f"Tenant '{tenant}' non trovato")

        return tenant
    
    # -------------------------
    # CONFIG
    # -------------------------

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

    # -------------------------
    # RQUESTID (ContextVar)
    # -------------------------

    @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)

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

    # -------------------------
    # TENANT (ContextVar)
    # -------------------------

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

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


    # -------------------------
    # USER ID (ContextVar)
    # -------------------------

    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

    @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)

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

    # -------------------------
    # DB SESSION (dependency)
    # -------------------------

    async def get_db_session(self, request: Request):
        tenant = self.get_tenant(request)
        user_id = self.get_user_id(request)
        
        with self.tenant_context(tenant), self.user_id_context(user_id):
            async with self.db.db_session_context(tenant):
                yield


    # -------------------------
    # HELPERS
    # -------------------------

    def current_db_session(self):
        session = self.db.get_current_db_session()
        if session is None:
            raise RuntimeError("Nessuna sessione DB attiva")
        return session
    
    async def dispose(self):
        await self.db.dispose_all()