from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
import jwt
from app.db import get_db
from app.core.security import decode_access_token
from app.models import User, UserPermissionOverride, Role

bearer = HTTPBearer(auto_error=False)
async def current_user(creds: HTTPAuthorizationCredentials | None = Depends(bearer), db: AsyncSession = Depends(get_db)) -> User:
    if not creds:
        raise HTTPException(status_code=401, detail="Authentication required")
    try: payload = decode_access_token(creds.credentials)
    except jwt.PyJWTError: raise HTTPException(status_code=401, detail="Invalid or expired token")
    user = await db.scalar(select(User).where(User.id == payload.get("sub"), User.is_active.is_(True)))
    if not user: raise HTTPException(status_code=401, detail="Account inactive or missing")
    if int(payload.get("ver", 0)) != int(user.token_version or 0):
        raise HTTPException(status_code=401, detail="Session revoked. Sign in again.")
    return user

def require_roles(*roles: str):
    async def check(user: User = Depends(current_user)) -> User:
        if user.role not in roles:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Insufficient permission")
        return user
    return check


# Sensitive administrative permissions cannot be granted through per-user overrides.
_OWNER_ONLY = {"users.manage", "users.password_reset", "users.role_change", "users.permissions", "system.settings", "payroll.approve", "security.block"}

def require_permission(permission: str, *roles: str):
    async def check(user: User = Depends(current_user), db: AsyncSession = Depends(get_db)) -> User:
        override = await db.scalar(select(UserPermissionOverride).where(
            UserPermissionOverride.user_id == user.id, UserPermissionOverride.permission == permission
        ))
        if permission in _OWNER_ONLY and user.role != "OWNER":
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Owner role required for this permission")
        if override is not None:
            if not override.allowed:
                raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Permission denied by account policy")
            if permission not in _OWNER_ONLY:
                return user
        role_record = await db.scalar(select(Role).where(Role.name == user.role))
        if role_record is not None:
            permissions = role_record.permissions_json or []
            if "*" in permissions or permission in permissions:
                return user
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=f"Missing permission: {permission}")
        if user.role not in roles:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=f"Missing permission: {permission}")
        return user
    return check
