#
# Copyright (c) 2021 the Hugging Face team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.#
from enum import Enum
from functools import partial
from typing import List, Optional

import requests
from fastapi import Depends, HTTPException
from fastapi.security.http import HTTPAuthorizationCredentials, HTTPBase
from pydantic import BaseModel
from requests import ConnectionError, HTTPError
from starlette.status import HTTP_401_UNAUTHORIZED


HF_API = "https://huggingface.co/api"


class RepoRole(Enum):
    read = 1
    write = 2
    admin = 3


class Organization(BaseModel):
    name: str
    role_in_org: RepoRole


class MoonlandingUser(BaseModel):
    """Dataclass holding a user info"""

    username: str
    email: Optional[str]
    orgs: Optional[List[Organization]]


UnauthenticatedError = partial(
    HTTPException,
    status_code=HTTP_401_UNAUTHORIZED,
    headers={"WWW-Authenticate": 'Bearer realm="Access to the API"'},
)

api_key = HTTPBase(scheme="bearer", auto_error=False)


async def authenticate(credentials: Optional[HTTPAuthorizationCredentials] = Depends(api_key)) -> MoonlandingUser:
    if credentials is None:
        raise UnauthenticatedError(detail="Not authenticated")

    if credentials.scheme.lower() != "bearer":
        raise UnauthenticatedError(detail="Not authenticated")

    token = credentials.credentials
    try:
        user_identity = moonlanding_auth(token)
    except HTTPError as exc:
        if exc.response.status_code == 401:
            raise UnauthenticatedError(detail="Invalid credentials")
        else:
            raise UnauthenticatedError(detail="Error when authenticating")
    except ConnectionError:
        raise UnauthenticatedError(detail="Authentication backend could not be reached")

    username = user_identity["name"]
    email = user_identity["email"]

    if user_identity["type"] != "user":
        raise UnauthenticatedError(detail="Invalid credentials. You should use the credentials from a user.")

    orgs = [Organization(name=org["name"], role_in_org=RepoRole[org["roleInOrg"]]) for org in user_identity["orgs"]]

    return MoonlandingUser(username=username, email=email, orgs=orgs)


def moonlanding_auth(token: str) -> dict:
    """Validate token with Moon Landing
    TODO: cache requests to avoid flooding Moon Landing
    """
    auth_repsonse = requests.get(HF_API + "/whoami-v2", headers={"Authorization": f"Bearer {token}"}, timeout=3)
    auth_repsonse.raise_for_status()
    return auth_repsonse.json()
