import requests
from py_cas.Models import *
import jwt


class CAS:
    def __init__(self, conf: InitConfig):
        self.conf = conf
        # self.ServiceAddress = conf.service_address
        # self.EnableWs = conf.enable_ws
        self.PermissionsUrls = PermissionsUrls()
        self.TokenUrls = TokenURLs()
        self.ActionPermissions = ActionPermission()
        self.RoleUrl = RolesUrls()
        self.UserRoleUrl = UserRoleUrls()
        self.TrustedServiceURL = TrustedServiceUrls()

    @staticmethod
    def result_resolver(result):
        if result.status_code == 200:
            return result
        if result.status_code == 201:
            return result
        if result.status_code == 404:
            raise Exception(result.json()["detail"])
        if result.status_code == 406:
            raise Exception('access denied')
        if result.status_code == 400:
            raise Exception(result.json()["detail"])
        if result.status_code == 422:
            raise Exception(result.json()["detail"])

        if result.status_code == 500:
            raise Exception("server side error, please check the logs")

    def url_creator(self, request_url):
        return f"{self.conf.service_address}{request_url}"

    def __post(self, request_url, body: dict, headers: dict = None, params: dict = None):
        result = requests.post(url=
                               self.url_creator(request_url=request_url), json=body, headers=headers, params=params)
        return CAS.result_resolver(result)

    def __get(self, request_url, headers: dict = None, params: dict = None):
        result = requests.get(url=
                              self.url_creator(request_url=request_url), headers=headers, params=params)
        return CAS.result_resolver(result)

    def __put(self, request_url, body: dict = None, headers: dict = None, params: dict = None):
        result = requests.put(url=
                              self.url_creator(request_url=request_url), json=body, headers=headers, params=params)

        return CAS.result_resolver(result)

    def __delete(self, request_url, headers: dict = None, params: dict = None, body: dict = None):
        result = requests.delete(url=
                                 self.url_creator(request_url=request_url), headers=headers, params=params, json=body)
        return CAS.result_resolver(result)

    def init_token(self, user_id: str,
                   others: dict = None, add_scopes: bool = False) -> JWTInitTokenResponseModel:
        result = self.__post(request_url=TokenURLs().INIT_TOKEN, body=JWTInitTokenModel(user_id=user_id,
                                                                                        others=others).dict(),
                             params={"add_scopes": add_scopes})
        return JWTInitTokenResponseModel(**result.json())

    def validate_token(self, token: str, permission: str = None) -> JWTTokenValidateResponseModel:
        now = datetime.utcnow()
        if self.conf.key:
            try:
                decoded_token = jwt.decode(token, key=self.conf.key)
                decoded_token = TokenModel(**decoded_token)
            except Exception as ex:
                raise Exception("access denied")
            if not permission:

                if decoded_token.others.rt and now < decoded_token.others.rt:
                    return JWTTokenValidateResponseModel(user_id=decoded_token.user_id, requested_permission=False)

            if permission:
                if self.conf.key:
                    if decoded_token.others.rt and now < decoded_token.others.rt:
                        if decoded_token.others.sc.__contains__(permission):
                            return JWTTokenValidateResponseModel(user_id=decoded_token.user_id, requested_permission=True)

        result = self.__get(request_url=TokenURLs().VALIDATE_TOKEN, params={
            "token": token,
            "permission": permission
        })
        if result.status_code != 200:
            raise Exception("access denied")
        return JWTTokenValidateResponseModel(**result.json())

    def remove_token(self, token: str):
        result = self.__get(request_url=self.TokenUrls.REMOVE_TOKEN, headers={"token": token})
        return RemoveTokenResponseModel(**result.json())

    def add_permission(self, permission_name: str,
                       description: str, token: str) -> AddPermissionResultModel:
        result = self.__post(request_url=self.PermissionsUrls.ADD_PERMISSION,
                             body=AddPermissionBodyModel(permission_name=permission_name,
                                                         description=description).dict(),
                             headers={"token": token, 'permission': self.ActionPermissions.ADD_PERMISSION})

        return AddPermissionResultModel(**result.json())

    def remove_permission(self, name: str, token: str) -> RemovePermissionResultModel:
        result = self.__delete(self.PermissionsUrls.REMOVE_PERMISSION,
                               headers={"token": token, 'permission': self.ActionPermissions.ADD_PERMISSION},
                               params={"name": name})
        return RemovePermissionResultModel(**result.json())

    def get_permission(self, name: str, token: str) -> GetPermissionResultModel:
        result = self.__get(request_url=self.PermissionsUrls.GET_PERMISSION, params={"name": name},
                            headers={"token": token, 'permission': self.ActionPermissions.ADD_PERMISSION})
        return GetPermissionResultModel(**result.json())

    def get_all_permission(self, token: str, page: int = 1) -> GetListOfPermissionsResultModel:
        result = self.__get(request_url=self.PermissionsUrls.GET_ALL_PERMISSION, params={"page": page},
                            headers={'token': token, 'permission': self.ActionPermissions.GET_ALL_PERMISSION})
        output: dict = result.json()
        output.update({"page": page})
        return GetListOfPermissionsResultModel(**output)

    def add_role(self, name: str, description: str, permissions_list: List[str], token: str) -> AddRoleResultModel:
        result = self.__post(request_url=self.RoleUrl.ADD_ROLE,
                             body=AddRoleDataModel(role_name=name,
                                                   description=description,
                                                   permission_list=permissions_list).dict(),
                             headers={"token": token, "permission": self.ActionPermissions.ADD_ROLE})
        return AddRoleResultModel(**result.json())

    def get_role(self, name: str, token: str) -> GetRoleResultModel:
        result = self.__get(request_url=self.RoleUrl.GET_ROLE, headers={"token": token, 'permission': 'get_role'},
                            params={"role_name": name})
        return GetRoleResultModel(**result.json())

    def update_role(self, name: str, new_permissions: List[str], token: str,
                    update_all_users_role: bool = False) -> UpdateRoleResponseModel:
        result = self.__put(request_url=self.RoleUrl.UPDATE_PERMISSION,
                            body=UpdateRolePermissionLists(role_name=name,
                                                           permissions=new_permissions,
                                                           update_user_roles=update_all_users_role).dict(),
                            headers={"token": token, "permission": self.ActionPermissions.UPDATE_ROLE})
        return UpdateRoleResponseModel(**result.json())

    def get_all_roles(self, token: str, page: int = 1) -> GetAllRolesResponseModel:
        result = self.__get(request_url=self.RoleUrl.GET_ALL_ROLE,
                            headers={'token': token, 'permission': self.ActionPermissions.GET_ROLE},
                            params={'page': page})
        output: dict = result.json()
        output.update({"page": page})
        return GetAllRolesResponseModel(**output)

    def remove_role(self, name: str, token: str) -> RemoveRoleResultModel:
        result = self.__delete(request_url=self.RoleUrl.REMOVE_ROLE,
                               headers={"token": token, 'permission': self.ActionPermissions.REMOVE_ROLE},
                               params={"role_name": name})
        return RemoveRoleResultModel(**result.json())

    def add_user_role(self, user_id: str, role_list: List[str], token: str, permissions_list: List[str] = None
                      ) -> AddUserRolePermissionResponseModel:
        if permissions_list is None:
            permissions_list = []
        result = self.__post(request_url=self.UserRoleUrl.ADD_USER_ROLE,
                             body=AddUserRolePermissionBodyModel(user_id=user_id,
                                                                 roles_list=role_list,
                                                                 permissions_list=permissions_list).dict(),
                             headers={'token': token, 'permission': self.ActionPermissions.ADD_USER_ROLE})

        return AddUserRolePermissionResponseModel(**result.json())

    def update_user_role(self, user_id: str, role_list: List[str], token: str, permissions_list: List[str] = None):
        if permissions_list is None:
            permissions_list = []
        result = self.__put(request_url=self.UserRoleUrl.UPDATE_USER_ROLE,
                            body=UpdateUserRolePermissionBodyModel(user_id=user_id,
                                                                   roles_list=role_list,
                                                                   permissions_list=permissions_list).dict(),
                            headers={'token': token, 'permission': self.ActionPermissions.UPDATE_USER_ROLE})
        return UpdateUserRoleResponseModel(**result.json())

    def remove_user_role(self, user_id: str, token: str, role_list: List[str] = None,
                         permissions_list: List[str] = None):
        if permissions_list is None:
            permissions_list = []
        if role_list is None:
            role_list = []
        result = self.__delete(request_url=self.UserRoleUrl.DELETE_USER_ROLE,
                               headers={'token': token, 'permission': self.ActionPermissions.DELETE_USER_ROLE},
                               body=RemoveUserRolePermission(user_id=user_id,
                                                             permissions_to_remove_list=permissions_list,
                                                             roles_to_remove_list=role_list).dict())
        return RemoveUserRolePermissionResponseModel(**result.json())

    def get_user_role(self, user_id: str, token: str) -> UserRole:
        result = self.__get(request_url=self.UserRoleUrl.GET_USER_ROLE,
                            headers={'token': token,
                                     'permission': self.ActionPermissions.GET_USER_ROLE},
                            params={"user_id": user_id})
        return UserRole(**result.json())

    def get_all_user_role(self, token: str, page: int = 1) -> GetAllUserRolePermissionResponseModel:
        result = self.__get(request_url=self.UserRoleUrl.GET_ALL_USER_ROLE,
                            headers={'token': token, 'permission': self.ActionPermissions.GET_ALL_PERMISSION},
                            params={"page": page})
        output: dict = result.json()
        output.update({"page": page})

        return GetAllUserRolePermissionResponseModel(**output)

    def add_trusted_service(self, service_name: str, token: str) -> AddTrustedServiceResponseModel:
        result = self.__post(request_url=self.TrustedServiceURL.ADD,
                             headers={'token': token, 'permission': 'add_trusted_service'},
                             body=AddTrustedServiceBodyModel(**{'service_name': service_name}).dict())
        return AddTrustedServiceResponseModel(**result.json())

    def remove_trusted_service(self, service_name: str, token: str) -> RemoveTrustedServiceResponseModel:
        result = self.__delete(request_url=self.TrustedServiceURL.DELETE,
                               headers={"token": token, 'permission': 'delete_trusted_service'},
                               params={"service_name": service_name})
        return RemoveTrustedServiceResponseModel(**result.json())

    def get_trusted_service(self, service_name: str, token: str) -> TrustedServiceModel:
        result = self.__get(request_url=self.TrustedServiceURL.GET,
                            headers={
                                'token': token,
                                'permission': 'get_trusted_service'
                            },
                            params={"service_name": service_name})
        return TrustedServiceModel(**result.json())

    def get_all_trusted_service(self, token: str, page: int = 1) -> GetAllTrustedServiceResponseModel:
        result = self.__get(request_url=self.TrustedServiceURL.GET_ALL,
                            headers={'token': token, 'permission': 'get_trusted_service'},
                            params={"page": page})
        output: dict = result.json()
        output.update({"page": page})
        return GetAllTrustedServiceResponseModel(**output)

    def renew_trusted_service(self, service_name: str, token: str) -> NewSecretTrustedServiceResponseModel:
        result = self.__put(request_url=self.TrustedServiceURL.RENEW,
                            headers={'token': token, 'permission': 'renew_trusted_service'},
                            params={"service_name": service_name})
        return NewSecretTrustedServiceResponseModel(**result.json())
