import time
from .signature import (
    SIGNATURE_HMAC_SHA1,
    SIGNATURE_PLAINTEXT,
    SIGNATURE_RSA_SHA1,
)
from .signature import (
    verify_hmac_sha1,
    verify_plaintext,
    verify_rsa_sha1,
)
from .errors import (
    InvalidRequestError,
    MissingRequiredParameterError,
    UnsupportedSignatureMethodError,
    InvalidNonceError,
    InvalidSignatureError,
)


class BaseServer:
    SIGNATURE_METHODS = {
        SIGNATURE_HMAC_SHA1: verify_hmac_sha1,
        SIGNATURE_RSA_SHA1: verify_rsa_sha1,
        SIGNATURE_PLAINTEXT: verify_plaintext,
    }
    SUPPORTED_SIGNATURE_METHODS = [SIGNATURE_HMAC_SHA1]
    EXPIRY_TIME = 300

    @classmethod
    def register_signature_method(cls, name, verify):
        """Extend signature method verification.

        :param name: A string to represent signature method.
        :param verify: A function to verify signature.

        The ``verify`` method accept ``OAuth1Request`` as parameter::

            def verify_custom_method(request):
                # verify this request, return True or False
                return True

            Server.register_signature_method('custom-name', verify_custom_method)
        """
        cls.SIGNATURE_METHODS[name] = verify

    def validate_timestamp_and_nonce(self, request):
        """Validate ``oauth_timestamp`` and ``oauth_nonce`` in HTTP request.

        :param request: OAuth1Request instance
        """
        timestamp = request.oauth_params.get('oauth_timestamp')
        nonce = request.oauth_params.get('oauth_nonce')

        if request.signature_method == SIGNATURE_PLAINTEXT:
            # The parameters MAY be omitted when using the "PLAINTEXT"
            # signature method
            if not timestamp and not nonce:
                return

        if not timestamp:
            raise MissingRequiredParameterError('oauth_timestamp')

        try:
            # The timestamp value MUST be a positive integer
            timestamp = int(timestamp)
            if timestamp < 0:
                raise InvalidRequestError('Invalid "oauth_timestamp" value')

            if self.EXPIRY_TIME and time.time() - timestamp > self.EXPIRY_TIME:
                raise InvalidRequestError('Invalid "oauth_timestamp" value')
        except (ValueError, TypeError):
            raise InvalidRequestError('Invalid "oauth_timestamp" value')

        if not nonce:
            raise MissingRequiredParameterError('oauth_nonce')

        if self.exists_nonce(nonce, request):
            raise InvalidNonceError()

    def validate_oauth_signature(self, request):
        """Validate ``oauth_signature`` from HTTP request.

        :param request: OAuth1Request instance
        """
        method = request.signature_method
        if not method:
            raise MissingRequiredParameterError('oauth_signature_method')

        if method not in self.SUPPORTED_SIGNATURE_METHODS:
            raise UnsupportedSignatureMethodError()

        if not request.signature:
            raise MissingRequiredParameterError('oauth_signature')

        verify = self.SIGNATURE_METHODS.get(method)
        if not verify:
            raise UnsupportedSignatureMethodError()

        if not verify(request):
            raise InvalidSignatureError()

    def get_client_by_id(self, client_id):
        """Get client instance with the given ``client_id``.

        :param client_id: A string of client_id
        :return: Client instance
        """
        raise NotImplementedError()

    def exists_nonce(self, nonce, request):
        """The nonce value MUST be unique across all requests with the same
        timestamp, client credentials, and token combinations.

        :param nonce: A string value of ``oauth_nonce``
        :param request: OAuth1Request instance
        :return: Boolean
        """
        raise NotImplementedError()
