import os

from sqlalchemy import select
from src.core.database import get_db
from src.models.empresas import Empresa
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Optional, Literal, TypedDict
from fastapi import Depends, HTTPException, Request, status


class AuthContext(TypedDict, total=False):
    type: Literal["front", "enterprise"]
    token: str
    empresa: Empresa


def _extract_bearer_token(
    authorization_header: Optional[str],
) -> Optional[str]:
    if not authorization_header:
        return None
    parts = authorization_header.split()
    if len(parts) == 2 and parts[0].lower() == "bearer":
        return parts[1].strip()
    return None


async def get_auth_context(
    request: Request,
    db: AsyncSession = Depends(get_db),
) -> AuthContext:
    """
    Resolve credenciais em dois modos:
    - FRONT GLOBAL: Authorization: Bearer <FRONT_API_TOKEN>
    """
    front_api_token = os.getenv("FRONT_API_TOKEN", "").strip()

    x_token = (
        request.headers.get("x-enterprise-token")
        or request.headers.get("X-Enterprise-Token")
        or ""
    ).strip()
    bearer = (
        _extract_bearer_token(
            request.headers.get("authorization")
            or request.headers.get("Authorization")
        )
        or ""
    )

    if front_api_token and bearer and bearer == front_api_token:
        return {"type": "front", "token": bearer}

    candidate = x_token or bearer
    if candidate:
        result = await db.execute(
            select(Empresa).where(Empresa.token == candidate)
        )
        empresa: Optional[Empresa] = result.scalar_one_or_none()
        if empresa:
            return {
                "type": "enterprise",
                "token": candidate,
                "empresa": empresa}

    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Credenciais inválidas ou ausentes",
    )


async def require_enterprise_or_front(
    ctx: AuthContext = Depends(get_auth_context),
) -> AuthContext:
    """
    Permite acesso com token global do front (type=front) ou token de empresa
    (type=enterprise).
        Retorna o contexto para a rota decidir como usar.
    """
    return ctx


async def require_enterprise(
    ctx: AuthContext = Depends(get_auth_context),
) -> Empresa:
    if ctx.get("type") == "enterprise" and ctx.get("empresa") is not None:
        return ctx["empresa"]
    raise HTTPException(
        status_code=status.HTTP_403_FORBIDDEN,
        detail="Ação requer token de empresa",
    )


async def require_front(
    ctx: AuthContext = Depends(get_auth_context),
) -> None:
    if ctx.get("type") == "front":
        return None
    raise HTTPException(
        status_code=status.HTTP_403_FORBIDDEN,
        detail="Ação restrita ao token global do front",
    )
