from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jwt import InvalidTokenError
from sqlalchemy.orm import Session, selectinload

from app.database import get_db
from app.models import Role, User
from app.security import decode_token

bearer = HTTPBearer(auto_error=False)


def get_current_user(
    creds: HTTPAuthorizationCredentials | None = Depends(bearer),
    db: Session = Depends(get_db),
) -> User:
    if creds is None:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated")
    try:
        payload = decode_token(creds.credentials)
        if payload.get("type") != "access":
            raise HTTPException(status_code=401, detail="Invalid token type")
        user_id = int(payload["sub"])
    except (InvalidTokenError, KeyError, ValueError) as exc:
        raise HTTPException(status_code=401, detail="Invalid or expired token") from exc

    user = (
        db.query(User)
        .options(selectinload(User.roles).selectinload(Role.permissions))
        .filter(User.id == user_id)
        .first()
    )
    if user is None or not user.is_active:
        raise HTTPException(status_code=401, detail="User is inactive or missing")
    return user


def user_permissions(user: User) -> set[str]:
    keys: set[str] = set()
    for role in user.roles:
        for perm in role.permissions:
            keys.add(perm.key)
    return keys


def user_role_names(user: User) -> list[str]:
    return [role.name for role in user.roles]


def require_permission(permission: str):
    def checker(user: User = Depends(get_current_user)) -> User:
        if permission not in user_permissions(user):
            raise HTTPException(status_code=403, detail=f"Missing permission: {permission}")
        return user

    return checker


def is_admin(user: User) -> bool:
    return any(name in {"Super Admin", "Admin"} for name in user_role_names(user))
