from typing import List

from django.contrib.auth import get_user_model
from ninja import Router, Schema, Field
from ninja.errors import HttpError
from ninja.pagination import paginate
from ninja.security import HttpBearer

from .logic import PackageManager
from .models import User, Compound, APIToken


class BearerTokenAuth(HttpBearer):
    def authenticate(self, request, token):
        for token_obj in APIToken.objects.select_related("user").all():
            if token_obj.check_token(token) and token_obj.is_valid():
                return token_obj.user
        raise HttpError(401, "Invalid or expired token")


def _anonymous_or_real(request):
    if request.user.is_authenticated and not request.user.is_anonymous:
        return request.user
    return get_user_model().objects.get(username="anonymous")


router = Router(auth=BearerTokenAuth())


class UserSchema(Schema):
    email: str
    username: str
    id: str = Field(None, alias="url")


class PackageIn(Schema):
    name: str
    description: str


class PackageOut(Schema):
    id: str = Field(None, alias="url")
    name: str
    reviewed: bool
    compound_links: str = None

    @staticmethod
    def resolve_compound_links(obj):
        return f"{obj.url}/compound"


class Error(Schema):
    message: str


class CompoundSchema(Schema):
    name: str = Field(None, alias="name")
    id: str = Field(None, alias="url")
    smiles: str = Field(None, alias="default_structure.smiles")
    reviewed: bool = Field(None, alias="package.reviewed")


@router.get("/user", response={200: List[UserSchema], 403: Error})
def get_users(request):
    return User.objects.all()


@router.get("/package", response={200: List[PackageOut], 403: Error})
def get_packages(request):
    return PackageManager.get_all_readable_packages(request.user, include_reviewed=True)


@router.post("/package", response=PackageOut)
def create_package(request, package: PackageIn):
    user = request.auth
    name = package.name.strip()
    description = package.description.strip()

    p = PackageManager.create_package(user, name, description=description)
    return p


@router.get("/package/{uuid:package_uuid}", response={200: PackageOut, 403: Error})
def get_package(request, package_uuid):
    try:
        return PackageManager.get_package_by_id(request.auth, package_id=package_uuid)
    except ValueError:
        return 403, {
            "message": f"Getting Package with id {package_uuid} failed due to insufficient rights!"
        }


@router.get("/compound", response={200: List[CompoundSchema], 403: Error})
@paginate
def get_compounds(request):
    qs = Compound.objects.none()
    for p in PackageManager.get_reviewed_packages():
        qs |= Compound.objects.filter(package=p)
    return qs


@router.get(
    "/package/{uuid:package_uuid}/compound", response={200: List[CompoundSchema], 403: Error}
)
@paginate
def get_package_compounds(request, package_uuid):
    try:
        p = PackageManager.get_package_by_id(request.auth, package_uuid)
        return Compound.objects.filter(package=p)
    except ValueError:
        return 403, {
            "message": f"Getting Compounds for Package with id {package_uuid} failed due to insufficient rights!"
        }
