# -*- coding: utf-8 -*-
"""
security cli module.
"""

import pyrin.security.token.services as token_services
import pyrin.security.hashing.services as hashing_services
import pyrin.security.encryption.services as encryption_services
import pyrin.security.utils.services as security_utils_services

from pyrin.cli.decorators import cli_invoke, cli_group
from pyrin.core.structs import CLI


@cli_group('security')
class SecurityCLI(CLI):
    """
    security cli class.

    this class exposes all security cli commands.
    """

    @cli_invoke
    def token(self, payload=None, **options):
        """
        generates a pair of access and refresh tokens using specified handler.

        the generated token is in the form of `header_hash.payload_hash.signature_hash`
        and each part is encoded using a signing key.

        :keyword dict payload: a dictionary containing key/values as payload.
                               note that for better performance, keep payload
                               as small as possible.

        :keyword str handler_name: name of token handler to be used.
                                   if not provided, default handler
                                   from relevant configs will be used.

        :keyword dict custom_headers: a dictionary containing custom headers.

        :keyword bool is_fresh: indicates that this token is fresh.
                                being fresh means that token is generated by
                                providing user credentials to server.
                                if not provided, defaults to False.

        :raises TokenHandlerNotFoundError: token handler not found error.

        :returns: tuple[str access_token, str refresh_token]
        :rtype: tuple[str, str]
        """

        access = token_services.generate_access_token(payload, **options)
        refresh = token_services.generate_refresh_token(payload, **options)
        return ['ACCESS-TOKEN:', (access,), 'REFRESH-TOKEN:', (refresh,)]

    @cli_invoke
    def hash(self, text, **options):
        """
        gets the hash of input text using a random or specified salt.

        :param str text: text to be hashed.

        :keyword str handler_name: handler name to be used for hash generation.
                                   if not provided, default handler from
                                   relevant configs will be used.

        :keyword bytes salt: salt to be used for hashing.
                             if not provided, a random salt will be generated
                             considering `salt_length` option.

        :keyword str internal_algorithm: internal algorithm to be used
                                         for hashing. if not provided,
                                         default value from relevant
                                         config will be used.

        :keyword int rounds: rounds to perform for generating hash.
                             if not provided, default value from
                             relevant config will be used.

        :keyword int salt_length: salt length to be used for hashing.
                                  if `salt` option is provided, then
                                  this value will be ignored.
                                  if not provided, default value from
                                  relevant config will be used.

        :keyword str prefix: prefix to be used for bcrypt hashing.

        :rtype: str
        """

        hashed_value = hashing_services.generate_hash(text, **options)
        return ['HASH:', (hashed_value,)]

    @cli_invoke
    def encrypt(self, text, **options):
        """
        encrypts the given value using specified handler and returns the encrypted result.

        :param str text: text to be encrypted.

        :keyword str handler_name: handler name to be used for encryption.
                                   if not provided, default handler from
                                   relevant configs will be used.

        :raises EncryptionHandlerNotFoundError: encryption handler not found error.

        :rtype: str
        """

        encrypted = encryption_services.encrypt(text, **options)
        return ['ENCRYPTED:', (encrypted,)]

    @cli_invoke
    def decrypt(self, encrypted, **options):
        """
        decrypts the given full encrypted value using specified
        handler and returns the decrypted result.

        :param str encrypted: full encrypted value to be decrypted.

        :raises InvalidEncryptionValueError: invalid encryption value error.
        :raises EncryptionHandlerNotFoundError: encryption handler not found error.
        :raises DecryptionError: decryption error.

        :rtype: str
        """

        decrypted = encryption_services.decrypt(encrypted, **options)
        return ['DECRYPTED:', (decrypted,)]

    @cli_invoke
    def rsa_key(self, **options):
        """
        generates a pair of public/private rsa keys.

        :keyword int length: key length in bits.
                             if not provided, `rsa_default_key_length`
                             config will be used.

        :returns: tuple[str public_key, str private_key]
        :rtype: tuple[str, str]
        """

        public, private = security_utils_services.generate_rsa_key(**options)
        return ['PUBLIC-KEY:', (public,), 'PRIVATE-KEY:', (private,)]

    @cli_invoke
    def bytes(self, **options):
        """
        gets a secure random bytes with given length.

        the result value should not be decoded to string, because
        it's not a safe-string and may cause an error.
        if you want string representation, use `get_hex` or `get_url_safe` methods.

        :keyword int length: length of random bytes to be get.
                             if not provided, `default_secure_random_size`
                             config will be used.

        :rtype: bytes
        """

        bytes_value = security_utils_services.get_bytes(**options)
        return ['BYTES:', (bytes_value,)]

    @cli_invoke
    def hex(self, **options):
        """
        gets a secure random hex string with given length.

        :keyword int length: length of random string to be get in bytes.
                             if not provided, `default_secure_random_size`
                             config will be used.

        :rtype: str
        """

        hex_value = security_utils_services.get_hex(**options)
        return ['HEX:', (hex_value,)]

    @cli_invoke
    def url_safe(self, **options):
        """
        gets a secure random url-safe string with given length.

        :keyword int length: length of random string to be get in bytes.
                             if not provided, `default_secure_random_size`
                             config will be used.

        :rtype: str
        """

        url_safe = security_utils_services.get_url_safe(**options)
        return ['URL-SAFE:', (url_safe,)]
