from dataclasses import dataclass, field
from typing import Union
import jwt
from fastapi import HTTPException, Request, status
from src.skys_llc_auth.utils import UserRole, TokenType

@dataclass(slots=True, frozen=True)
class TokenValidation:
    token: str
    role: Union[UserRole, list[UserRole]] | None
    key: str = field(default=None)
    algorithms: str = field(default=None)

    _default_key: str = field(default=None, init=False, repr=False)
    _default_algorithms: str = field(default=None, init=False, repr=False)

    def __post_init__(self):
        if self.key is None:
            object.__setattr__(self, 'key', self._default_key)
        if self.algorithms is None:
            object.__setattr__(self, 'algorithms', self._default_algorithms)

    def _decode(self) -> dict[str, str]:
        try:
            token = jwt.decode(self.token, key=self.key, algorithms=[self.algorithms])
            return dict(token)
        except jwt.PyJWTError as exp_err:
            raise HTTPException(
                detail=exp_err.args,
                status_code=status.HTTP_401_UNAUTHORIZED,
            ) from exp_err

    def _has_access(self) -> bool:
        if isinstance(self.role, list):
            return self._decode().get("role", None) in [i.value for i in self.role]
        elif isinstance(self.role, UserRole):
            return self._decode().get("role", None) == self.role.value
        else:
            return False

    def _not_access(self) -> bool:
        if isinstance(self.role, list):
            return self._decode().get("role", None) not in [i.value for i in self.role]
        elif isinstance(self.role, UserRole):
            return self._decode().get("role", None) != self.role.value
        else:
            return False

    def check_permission(self, *, exclude: bool) -> None:
        if exclude:
            access_token = self._not_access()
        else:
            access_token = self._has_access()
        if not access_token:
            raise HTTPException(
                detail="Permission denied",
                status_code=status.HTTP_403_FORBIDDEN,
            )

    def user_id(self) -> str:
        user_id = self._decode().get("id", None)
        if user_id:
            return user_id
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Token must have key id",
        )

    def token_type_jwt(self) -> str | None:
        return self._decode().get("token_type_jwt", None)

    def user_role(self) -> Union[str, None]:
        return self._decode().get("role", None)
    
    @classmethod
    def set_defaults(cls, key: str, algorithms: str) -> None:
        cls._default_key = key
        cls._default_algorithms = algorithms

    @classmethod
    def with_defaults(cls, token: str, role: Union[UserRole, list[UserRole]] | None = None) -> 'TokenValidation':
        return cls(token=token, role=role)


async def _get_token_from_request(request: Request) -> str:
    if not request.headers.get("Authorization", None):
        raise HTTPException(
            detail="Request must have a Authorization token",
            status_code=status.HTTP_401_UNAUTHORIZED,
        )
    token = request.headers["Authorization"].split(" ")
    if token[0] != "Bearer":
        raise HTTPException(
            detail="Предоставлен не Bearer токен",
            status_code=status.HTTP_401_UNAUTHORIZED,
        )
    if len(token) != 2:
        raise HTTPException(
            detail="Invalid basic header. Credentials string should not contain spaces.",
            status_code=status.HTTP_401_UNAUTHORIZED,
        )

    payload = TokenValidation(token=token[1], role=None)

    if payload.token_type_jwt() != TokenType.access.value:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token type",
        )

    return token[1]
