"""
Contains methods to interact with organization api
"""

import json
import time
import uuid

from unify.apirequestsmng import ApiRequestManager
from unify.properties import Properties
from unify.properties import ClusterSetting


class OrgAdmin(ApiRequestManager):
    """
    Class to interact with organization endpoints
    """

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

    def __init__(self, cluster=None, props=Properties(ClusterSetting.KEY_RING)):
        """
        Class constructor

        :param cluster: Cluster name to be used
        :type cluster: name
        :param props: Properties instantiated object
        :type props: class:`unify.properties.Properties`
        """

        super().__init__(cluster=cluster, props=props)

        self.epoch_time = int(time.time())
        self.pi_tag_export_limit = {"piTagExportLimit": 999}
        self.expiry = {"expiry": 999}

        try:

            self.orgs_api_url = self.props.get_remote(self.cluster) + 'api/management/v1/orgs'
            self.org_info_url = self.props.get_remote(self.cluster) + 'api/v1/orgs/{}'

        except Exception as error:
            raise error

    def get_org_info(self, org_id):
        """
        Retrieves the org metadata

        :param org_id: Org id to be queried
        :type org_id: id or str
        :return: List of information on the organization
        """

        self.verify_properties()

        jobs = self.session.get(
            self.org_info_url.format(org_id),
            headers=self.build_header(
                aut_token=self.props.get_auth_token(),
                org_id=org_id,
                others=self.content_type_header)
        )

        return json.loads(jobs.content), jobs.status_code

    def create_organization(self, org_name):
        """
        Creates an organization with the given name

        :param org_name: Name or organization to be created
        :type org_name: str
        :return: Server response or exception if org name already exists
        """

        try:

            if org_name is None:
                org_name = str(uuid.uuid4())

            payload = {
                "name": str(org_name),
                "piTagExportLimit": self.pi_tag_export_limit,
                "expiry": self.expiry
            }

            org_create_post = self.session.post(
                self.orgs_api_url,
                headers=self.build_header(
                    aut_token=self.props.get_auth_token(cluster=self.cluster),
                    others=self.content_type_header),
                data=json.dumps(payload)
            )

            if org_create_post.status_code == 200:
                return json.loads(org_create_post.content)["id"]

            raise Exception(org_create_post.content)

        except Exception as error:

            raise error

    def delete_organization(self, org_id):
        """
        Deletes the org that matched the given org id

        :param org_id: Org id to be deleted
        :type org_id: int or str
        :return: Delete successful message
        """
        try:

            delete_endpoint = self.props.get_remote(
                cluster=self.cluster
            ) + 'api/management/v1/orgs/' + str(org_id)

            delete_request = self.session.delete(
                delete_endpoint,
                headers=self.build_header(
                    aut_token=self.props.get_auth_token(cluster=self.cluster)
                )
            )

            if delete_request.status_code == 200:
                return json.loads(delete_request.content)

            raise Exception(json.loads(delete_request.content))

        except Exception as error:
            raise error

    def invite_user(self, org_id, email, name, role):
        """
        Adds an user to the given org

        :param org_id: Org id where the user is going to be added
        :type org_id: int or str
        :param email: User's email
        :type email: str
        :param name: User's Name
        :type name: str
        :param role: User role. Accepts "Admin" or "Contributor"
        :type role: str
        :return: Invite user status message
        """
        try:

            invite_user_url = self.org_info_url.format(org_id) + '/users'

            header = self.build_header(
                aut_token=self.props.get_auth_token(cluster=self.cluster),
                org_id=org_id,
                others=self.content_type_header
            )

            payload = {"fullName": name, "email": email, "roleNames": [role]}

            invite_user_post = self.session.post(
                invite_user_url,
                headers=header,
                data=json.dumps(payload)
            )

            if invite_user_post.status_code == 201:
                return json.loads(invite_user_post.content)

            raise Exception(json.loads(invite_user_post.content))

        except Exception as error:

            raise error

    def invite_machine_user(self, org_id, id, password, fullname, role):
        """
        Invites a machine user

        :param org_id: Org identification where the user is going to be added
        :type org_id: int or str
        :param id: User's ID
        :type id: int or str
        :param password: User's Password
        :type password: str
        :param role: User role. Accepts "Admin" or "Contributor"
        :type role: str
        :return: Invite machine user status messae
        """
        try:

            invite_machine_user_url = self.org_info_url.format(org_id) + '/machine_users'

            header = self.build_header(
                aut_token=self.props.get_auth_token(cluster=self.cluster),
                org_id=org_id,
                others=self.content_type_header
            )

            payload = {
                "identifier": id,
                "password": password,
                "roleNames": [role],
                "fullName": fullname
            }

            invite_user_post = self.session.post(
                invite_machine_user_url,
                headers=header,
                data=json.dumps(payload)
            )

            if invite_user_post.status_code == 200:
                return json.loads(invite_user_post.content)

            raise Exception(json.loads(invite_user_post.content))

        except Exception as error:
            raise error

    def get_org_list(self):
        """
        Retrieves the org list of the current cluster, only the ones whos users has logged in

        :return: List of organizations
        """
        try:

            header = self.build_header(
                aut_token=self.props.get_auth_token(cluster=self.cluster)
            )

            request = self.session.get(
                '{}api/whoami'.format(
                    self.props.get_remote(cluster=self.cluster)),
                headers=header
            )

            if request.status_code == 200:
                return json.loads(request.content)["organizations"]

            raise Exception(request.content)

        except Exception as error:
            raise error
