from typing import Optional, Union
from uuid import UUID

from fastapi import (
    Depends,
    HTTPException,
    Request,
)
from fastapi.security import OAuth2PasswordBearer

from . import actions
from . import models
from .external import yield_db_session_from_env
from .settings import BOT_INSTALLATION_TOKEN, BOT_INSTALLATION_TOKEN_HEADER

# Login implementation follows:
# https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
oauth2_scheme_manual = OAuth2PasswordBearer(tokenUrl="token", auto_error=False)


async def get_current_user(
    token: UUID = Depends(oauth2_scheme),
    db_session=Depends(yield_db_session_from_env),
) -> models.User:
    try:
        token_object = actions.get_token(session=db_session, token=token)
    except actions.TokenNotFound:
        raise HTTPException(status_code=404, detail="Access token not found")
    if not token_object.active:
        raise HTTPException(status_code=403, detail="Token has expired")
    return token_object.user


def autogenerated_user_token_check(request: Request) -> bool:
    if BOT_INSTALLATION_TOKEN is None:
        raise ValueError("BOT_INSTALLATION_TOKEN environment variable must be set")

    autogenerated_user_token = False
    installation_token_header: Optional[str] = request.headers.get(
        BOT_INSTALLATION_TOKEN_HEADER, None
    )
    if (
        installation_token_header is not None
        and BOT_INSTALLATION_TOKEN == installation_token_header
    ):
        autogenerated_user_token = True
    elif (
        installation_token_header is not None
        and BOT_INSTALLATION_TOKEN != installation_token_header
    ):
        raise HTTPException(
            status_code=400,
            detail=f"Invalid {BOT_INSTALLATION_TOKEN_HEADER} provided",
        )
    return autogenerated_user_token


async def get_current_user_or_installation(
    request: Request,
    token: UUID = Depends(oauth2_scheme_manual),
    db_session=Depends(yield_db_session_from_env),
) -> Union[models.User, bool]:
    """
    Allow access if Bugout installation token provided, if not
    check user by default.
    """
    autogenerated_user = autogenerated_user_token_check(request)
    if autogenerated_user is True:
        return True
    elif autogenerated_user is False:
        user = await get_current_user(token, db_session)
        return user

    raise HTTPException(status_code=400, detail="Access denied")


async def is_token_restricted_or_installation(
    request: Request,
    token: UUID = Depends(oauth2_scheme_manual),
    db_session=Depends(yield_db_session_from_env),
) -> bool:
    """
    Allow access if Bugout installation provided.
    """
    autogenerated_user = autogenerated_user_token_check(request)
    if autogenerated_user is True:
        return False  # Return token.restricted = False
    elif autogenerated_user is False:
        token_restricted = await is_token_restricted(token, db_session)
        return token_restricted

    raise HTTPException(status_code=400, detail="Access denied")


async def is_token_restricted(
    token: UUID = Depends(oauth2_scheme),
    db_session=Depends(yield_db_session_from_env),
) -> bool:
    try:
        token_object = actions.get_token(session=db_session, token=token)
    except actions.TokenNotFound:
        raise HTTPException(status_code=404, detail="Access token not found")
    return token_object.restricted
