# Copyright (C) 2019-2021 Zilliz. All rights reserved.
#
# 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 .connections import connections

class Role:
    """ Role, can be granted privileges which are allowed to execute some objects' apis. """

    def __init__(self, name: str, using="default", **kwargs):
        """ Constructs a role by name
            :param name: role name.
            :type  name: str
        """
        self._name = name
        self._using = using
        self._kwargs = kwargs

    def _get_connection(self):
        return connections._fetch_handler(self._using)

    @property
    def name(self):
        return self._name

    def create(self):
        """ Create a role
            It will success if the role isn't existed, otherwise fail.

        :example:
            >>> from pymilvus import connections, utility
            >>> from pymilvus.orm.role import Role
            >>> connections.connect()
            >>> role = Role(name=role_name)
            >>> role.create()
            >>> roles = utility.list_roles()
            >>> print(f"roles in Milvus: {roles}")
        """
        return self._get_connection().create_role(self._name)

    def drop(self):
        """ Drop a role
            It will success if the role is existed, otherwise fail.

        :example:
            >>> from pymilvus import connections, utility
            >>> from pymilvus.orm.role import Role
            >>> connections.connect()
            >>> role = Role(name=role_name)
            >>> role.drop()
            >>> roles = utility.list_roles()
            >>> print(f"roles in Milvus: {roles}")
        """
        return self._get_connection().drop_role(self._name)

    def add_user(self, username: str):
        """ Add user to role
            The user will get permissions that the role are allowed to perform operations.
            :param username: user name.
            :type  username: str

        :example:
            >>> from pymilvus import connections
            >>> from pymilvus.orm.role import Role
            >>> connections.connect()
            >>> role = Role(name=role_name)
            >>> role.add_user(username)
            >>> users = role.get_users()
            >>> print(f"users added to the role: {users}")
        """
        return self._get_connection().add_user_to_role(username, self._name)

    def remove_user(self, username: str):
        """ Remove user from role
            The user will remove permissions that the role are allowed to perform operations.
            :param username: user name.
            :type  username: str

        :example:
            >>> from pymilvus import connections
            >>> from pymilvus.orm.role import Role
            >>> connections.connect()
            >>> role = Role(name=role_name)
            >>> role.remove_user(username)
            >>> users = role.get_users()
            >>> print(f"users added to the role: {users}")
        """
        return self._get_connection().remove_user_from_role(username, self._name)

    def get_users(self):
        """ Get all users who are added to the role.
            :return a RoleInfo object which contains a RoleItem group
                According to the RoleItem, you can get a list of usernames.

            RoleInfo groups:
            - UserItem: <role_name:admin>, <users:('root',)>

        :example:
            >>> from pymilvus import connections
            >>> from pymilvus.orm.role import Role
            >>> connections.connect()
            >>> role = Role(name=role_name)
            >>> users = role.get_users()
            >>> print(f"users added to the role: {users}")
        """
        roles = self._get_connection().select_one_role(self._name, True)
        if len(roles.groups) == 0:
            return []
        return roles.groups[0].users

    def is_exist(self):
        """ Check whether the role is existed.
            :return a bool value
                It will be True if the role is existed, otherwise False.

        :example:
            >>> from pymilvus import connections
            >>> from pymilvus.orm.role import Role
            >>> connections.connect()
            >>> role = Role(name=role_name)
            >>> is_exist = role.is_exist()
            >>> print(f"the role: {is_exist}")
        """
        roles = self._get_connection().select_one_role(self._name, False)
        return len(roles.groups) != 0

    def grant(self, object: str, object_name: str, privilege: str):
        """ Grant a privilege for the role
            :param object: object type.
            :type  object: str
            :param object_name: identifies a specific object name.
            :type  object_name: str
            :param privilege: privilege name.
            :type  privilege: str

        :example:
            >>> from pymilvus import connections
            >>> from pymilvus.orm.role import Role
            >>> connections.connect()
            >>> role = Role(role_name)
            >>> role.grant("Collection", collection_name, "Insert")
        """
        return self._get_connection().grant_privilege(self._name, object, object_name, privilege)

    def revoke(self, object: str, object_name: str, privilege: str):
        """ Revoke a privilege for the role
            :param object: object type.
            :type  object: str
            :param object_name: identifies a specific object name.
            :type  object_name: str
            :param privilege: privilege name.
            :type  privilege: str

        :example:
            >>> from pymilvus import connections
            >>> from pymilvus.orm.role import Role
            >>> connections.connect()
            >>> role = Role(role_name)
            >>> role.revoke("Collection", collection_name, "Insert")
        """
        return self._get_connection().revoke_privilege(self._name, object, object_name, privilege)

    def list_grant(self, object: str, object_name: str):
        """ List a grant info for the role and the specific object
            :param object: object type.
            :type  object: str
            :param object_name: identifies a specific object name.
            :type  object_name: str
            :return a GrantInfo object
            :rtype GrantInfo

            GrantInfo groups:
            - GrantItem: <object:Collection>, <object_name:foo>, <role_name:x>, <grantor_name:root>, <privilege:Load>

        :example:
            >>> from pymilvus import connections
            >>> from pymilvus.orm.role import Role
            >>> connections.connect()
            >>> role = Role(role_name)
            >>> role.list_grant("Collection", collection_name)
        """
        return self._get_connection().select_grant_for_role_and_object(self._name, object, object_name)

    def list_grants(self):
        """ List a grant info for the role
            :return a GrantInfo object
            :rtype GrantInfo

            GrantInfo groups:
            - GrantItem: <object:Collection>, <object_name:foo>, <role_name:x>, <grantor_name:root>, <privilege:Load>

        :example:
            >>> from pymilvus import connections
            >>> from pymilvus.orm.role import Role
            >>> connections.connect()
            >>> role = Role(role_name)
            >>> role.list_grants()
        """
        return self._get_connection().select_grant_for_one_role(self._name)
