"""
mixcoatl.admin.api_key
----------------------

Implements access to the DCM ApiKey API
"""
from mixcoatl.resource import Resource
from mixcoatl.decorators.lazy import lazy_property
from mixcoatl.decorators.validations import required_attrs
from mixcoatl.utils import uncamel, camelize, camel_keys, uncamel_keys
import json


class ApiKey(Resource):

    """An API key is an access key and secret key that provide API access into DCM."""
    PATH = 'admin/ApiKey'
    COLLECTION_NAME = 'apiKeys'
    PRIMARY_KEY = 'access_key'

    def __init__(self, access_key=None, endpoint=None, *args, **kwargs):
        Resource.__init__(self, endpoint=endpoint)
        self.__access_key = access_key

    @property
    def access_key(self):
        """The primary identifier of the `ApiKey`. Same as `DCM_ACCESS_KEY`"""
        return self.__access_key

    @lazy_property
    def account(self):
        """`dict` - The account with which this API key is associated."""
        return self.__account

    @lazy_property
    def activation(self):
        """`str` - The date and time when this key was activated."""
        return self.__activation

    @lazy_property
    def expiration(self):
        """`str` - The date and time when this API key should automatically be made inactivate."""
        return self.__expiration

    @expiration.setter
    def expiration(self, e):
        self.__expiration = e

    @lazy_property
    def customer(self):
        """`dict` - The customer to whom this API key belongs."""
        return self.__customer

    @lazy_property
    def customer_management_key(self):
        """`bool` - Identifies whether or not this key can be used across all customer accounts."""
        return self.__customer_management_key

    @lazy_property
    def description(self):
        """`str` - A user-friendly description of this API key."""
        return self.__description

    @description.setter
    def description(self, d):
        self.__description = d

    @lazy_property
    def name(self):
        """`str` - The user-friendly name used to identify the key."""
        return self.__name

    @name.setter
    def name(self, n):
        self.__name = n

    @lazy_property
    def secret_key(self):
        """`str` - The secret part of this API key."""
        return self.__secret_key

    @lazy_property
    def state(self):
        """`str` - The status of the key *(i.e. `ACTIVE`)*"""
        return self.__state

    @lazy_property
    def system_management_key(self):
        """`bool` - Identifies if the key can be used for DCM system management functions"""
        return self.__system_management_key

    @lazy_property
    def user(self):
        """`dict` - The user associated with this API key. Account-level keys return `{'user_id': -1}`"""
        return self.__user

    @required_attrs(['description', 'name'])
    def create(self):
        """Call the API to generate an API key from the current instance of `ApiKey`"""

        payload = {
            'generateApiKey': [{'description': self.description, 'name': self.name}]}
        s = self.post(data=json.dumps(payload))
        if self.last_error is None:
            self.__access_key = s['apiKeys'][0]['accessKey']
            self.load()
        else:
            raise ApiKeyGenerationException(self.last_error)

    def invalidate(self, reason='key deleted via mixcoatl'):
        """Call the API to invalidate the current instance of `ApiKey`
        This is the same as deleting the api key

        :param reason: the reason for invalidating the key
        :type reason: str.
        :returns: True
        :raises: :class:`ApiKeyInvalidationException`
        """
        params = {'reason': reason}
        self.delete(params=params)
        if self.last_error is None:
            return True
        else:
            raise ApiKeyInvalidationException(self.last_error)

    @classmethod
    def generate_api_key(cls, key_name, description, expiration=None):
        """Generates a new API key

        >>> ApiKey.generate_api_key('my-api-key', 'this is my api key')
        {'access_key':'ABCDEFGHIJKL':....}

        :param key_name: the name for the key
        :type key_name: str.
        :param description: the description for the key
        :type description: str.
        :param expiration: *unused for now*
        :type expiration: str.
        :returns: :class:`ApiKey`
        :raises: :class:`ApiKeyGenerationException`
        """

        a = cls()
        a.name = key_name
        a.description = description
        a.create()
        return a

    @classmethod
    def all(cls, keys_only=False, endpoint=None, **kwargs):
        """Get all api keys

        .. note::

            The keys used to make the request determine results visibility

        :param keys_only: Only return `access_key` instead of `ApiKey` objects
        :type keys_only: bool.
        :param detail: The level of detail to return - `basic` or `extended`
        :type detail: str.
        :param account_id: Display all system keys belonging to `account_id`
        :type account_id: int.
        :param user_id: Display all keys belonging to `user_id`
        :type user_id: int.
        :returns: `list` - of :class:`ApiKey` or :attr:`access_key`
        """

        if 'access_key' in kwargs:
            r = Resource(cls.PATH + "/" + kwargs['access_key'], endpoint=endpoint)
            params = {}
        else:
            r = Resource(cls.PATH, endpoint=endpoint)

            if 'detail' in kwargs:
                r.request_details = kwargs['detail']
            else:
                r.request_details = 'basic'

            if 'account_id' in kwargs:
                params = {'accountId': kwargs['account_id']}
            elif 'user_id' in kwargs:
                params = {'userId': kwargs['user_id']}
            else:
                params = {}

        x = r.get(params=params)
        if r.last_error is None:
            if keys_only is True:
                return [i[camelize(cls.PRIMARY_KEY)]
                        for i in x[cls.COLLECTION_NAME]]
            else:
                return [type(cls.__name__, (object,), i)
                        for i in uncamel_keys(x)[uncamel(cls.COLLECTION_NAME)]]
        else:
            raise ApiKeyException(r.last_error)


class ApiKeyException(BaseException):
    pass


class ApiKeyGenerationException(ApiKeyException):
    pass


class ApiKeyInvalidationException(ApiKeyException):
    pass

[(97, 'mixcoatl.decorators.validations.required_attrs', 'required_attrs', False, 'from mixcoatl.decorators.validations import required_attrs\n'), (22, 'mixcoatl.resource.Resource.__init__', 'Resource.__init__', False, 'from mixcoatl.resource import Resource\n'), (169, 'mixcoatl.resource.Resource', 'Resource', False, 'from mixcoatl.resource import Resource\n'), (172, 'mixcoatl.resource.Resource', 'Resource', False, 'from mixcoatl.resource import Resource\n'), (103, 'json.dumps', 'json.dumps', False, 'import json\n'), (189, 'mixcoatl.utils.camelize', 'camelize', False, 'from mixcoatl.utils import uncamel, camelize, camel_keys, uncamel_keys\n'), (193, 'mixcoatl.utils.uncamel_keys', 'uncamel_keys', False, 'from mixcoatl.utils import uncamel, camelize, camel_keys, uncamel_keys\n'), (193, 'mixcoatl.utils.uncamel', 'uncamel', False, 'from mixcoatl.utils import uncamel, camelize, camel_keys, uncamel_keys\n')]