# ------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------

from abc import ABCMeta, abstractmethod
from typing import Any, FrozenSet

from .algorithm import Algorithm


class Key(object, metaclass=ABCMeta):
    _supported_encryption_algorithms: FrozenSet[Any] = frozenset([])
    _supported_key_wrap_algorithms: FrozenSet[Any] = frozenset([])
    _supported_signature_algorithms: FrozenSet[Any] = frozenset([])

    def __init__(self):
        self._kid = None

    @property
    def default_encryption_algorithm(self):
        return None

    @property
    def default_key_wrap_algorithm(self):
        return None

    @property
    def default_signature_algorithm(self):
        return None

    @property
    def supported_encryption_algorithms(self):
        return self._supported_encryption_algorithms

    @property
    def supported_key_wrap_algorithms(self):
        return self._supported_key_wrap_algorithms

    @property
    def supported_signature_algorithms(self):
        return self._supported_signature_algorithms

    @property
    def kid(self):
        return self._kid

    @abstractmethod
    def is_private_key(self):
        pass

    @abstractmethod
    def decrypt(self, cipher_text, **kwargs):
        raise NotImplementedError()

    @abstractmethod
    def encrypt(self, plain_text, **kwargs):
        raise NotImplementedError()

    @abstractmethod
    def wrap_key(self, key, **kwargs):
        raise NotImplementedError()

    @abstractmethod
    def unwrap_key(self, encrypted_key, **kwargs):
        raise NotImplementedError()

    @abstractmethod
    def sign(self, digest, **kwargs):
        raise NotImplementedError()

    @abstractmethod
    def verify(self, digest, signature, **kwargs):
        raise NotImplementedError()

    def _get_algorithm(self, op, **kwargs):
        default_algorithm, supported_algorithms = {
            "encrypt": (self.default_encryption_algorithm, self.supported_encryption_algorithms),
            "decrypt": (self.default_encryption_algorithm, self.supported_encryption_algorithms),
            "wrapKey": (self.default_key_wrap_algorithm, self.supported_key_wrap_algorithms),
            "unwrapKey": (self.default_key_wrap_algorithm, self.supported_key_wrap_algorithms),
            "sign": (self.default_signature_algorithm, self.supported_signature_algorithms),
            "verify": (self.default_signature_algorithm, self.supported_signature_algorithms),
        }[op]

        algorithm = kwargs.get("algorithm", default_algorithm)

        if not isinstance(algorithm, Algorithm):
            algorithm = Algorithm.resolve(algorithm)

        if not algorithm or not supported_algorithms or algorithm.name() not in supported_algorithms:
            raise ValueError(f"unsupported algorithm '{algorithm}'")

        return algorithm
