from typing import Optional
from uuid import UUID

from fastapi import Depends, Request
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.exceptions import ForbiddenError, UnauthorizedError
from src.core.security import decode_access_token
from src.core.constants import UserRole, ROLE_HIERARCHY
from src.database.session import get_db

security = HTTPBearer(auto_error=False)


async def get_current_user(
    request: Request,
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
    db: AsyncSession = Depends(get_db),
):
    """Decode JWT and return current user model."""
    if not credentials:
        raise UnauthorizedError("Bearer token required")

    payload = decode_access_token(credentials.credentials)
    if not payload:
        raise UnauthorizedError("Invalid or expired token")

    user_id = payload.get("sub")
    if not user_id:
        raise UnauthorizedError("Invalid token payload")

    # Lazy import to avoid circular dependency
    from src.apps.auth.services.auth_service import AuthService

    service = AuthService(db)
    user = await service.get_user_by_id(UUID(user_id))
    if not user:
        raise UnauthorizedError("User not found")
    if user.status != "active":
        raise UnauthorizedError("User account is inactive")

    return user


async def get_current_user_optional(
    request: Request,
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
    db: AsyncSession = Depends(get_db),
):
    """Return current user or None for optional auth routes."""
    if not credentials:
        return None
    try:
        return await get_current_user(request, credentials, db)
    except Exception:
        return None


def require_roles(*roles: UserRole):
    """Dependency factory for RBAC role enforcement."""

    async def role_checker(current_user=Depends(get_current_user)):
        if current_user.role not in [r.value for r in roles]:
            raise ForbiddenError(
                f"This action requires one of: {', '.join(r.value for r in roles)}"
            )
        return current_user

    return role_checker


def require_min_role(min_role: UserRole):
    """Dependency factory for hierarchy-based role check."""

    async def role_checker(current_user=Depends(get_current_user)):
        user_level = ROLE_HIERARCHY.get(UserRole(current_user.role), 0)
        required_level = ROLE_HIERARCHY.get(min_role, 0)
        if user_level < required_level:
            raise ForbiddenError("Insufficient permissions")
        return current_user

    return role_checker


async def get_tenant_id(request: Request) -> Optional[str]:
    """Extract resolved tenant_id from request state."""
    return getattr(request.state, "tenant_id", None)


async def require_tenant(request: Request) -> str:
    """Ensure a tenant is resolved for this request."""
    tenant_id = getattr(request.state, "tenant_id", None)
    if not tenant_id:
        raise UnauthorizedError("Tenant context required")
    return tenant_id
