# -*- coding: utf-8 -*-
"""
token interface module.
"""

from abc import abstractmethod
from threading import Lock

from pyrin.core.structs import CoreObject, MultiSingletonMeta
from pyrin.core.exceptions import CoreNotImplementedError


class TokenSingletonMeta(MultiSingletonMeta):
    """
    token singleton meta class.
    this is a thread-safe implementation of singleton.
    """

    _instances = dict()
    _lock = Lock()


class AbstractTokenBase(CoreObject, metaclass=TokenSingletonMeta):
    """
    abstract token base class.
    """

    @abstractmethod
    def generate_access_token(self, payload, **options):
        """
        generates an access token from the given inputs and returns it.

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

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

        :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 CoreNotImplementedError: core not implemented error.

        :returns: token.
        :rtype: str
        """

        raise CoreNotImplementedError()

    @abstractmethod
    def generate_refresh_token(self, payload, **options):
        """
        generates a refresh token from the given inputs and returns it.

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

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

        :keyword dict custom_headers: a dictionary containing custom headers.

        :raises CoreNotImplementedError: core not implemented error.

        :returns: token.
        :rtype: str
        """

        raise CoreNotImplementedError()

    @abstractmethod
    def get_payload(self, token, **options):
        """
        decodes token and gets the payload data.

        if token signature could not be verified, it will raise an error.

        :param str token: token to get it's payload.

        :raises CoreNotImplementedError: core not implemented error.

        :rtype: dict
        """

        raise CoreNotImplementedError()

    @abstractmethod
    def get_unverified_payload(self, token, **options):
        """
        decodes token and gets the payload data without verifying the signature.

        note that returned payload must not be trusted for any critical operations.

        :param str token: token to get it's payload.

        :raises CoreNotImplementedError: core not implemented error.

        :rtype: dict
        """

        raise CoreNotImplementedError()

    @abstractmethod
    def get_unverified_header(self, token, **options):
        """
        gets the header dict of token without verifying the signature.

        note that returned header must not be trusted for critical operations.

        :param str token: token to get it's header.

        :raises CoreNotImplementedError: core not implemented error.

        :rtype: dict
        """

        raise CoreNotImplementedError()

    @abstractmethod
    def generate_key(self, **options):
        """
        generates a valid key for this handler and returns it.

        :keyword int length: the length of generated key in bytes.
                             note that some token handlers may not accept custom
                             key length so this value would be ignored on those handlers.

        :raises CoreNotImplementedError: core not implemented error.

        :rtype: str | tuple[str, str]
        """

        raise CoreNotImplementedError()

    @abstractmethod
    def get_kid(self):
        """
        gets kid value to be used in token header for this handler.

        it must be unique for each handler.

        :raises CoreNotImplementedError: core not implemented error.

        :rtype: str
        """

        raise CoreNotImplementedError()
