from abc import ABC, abstractmethod
from enum import Enum
from semver import VersionInfo
from typing import Dict


class ExploitCategory(Enum):
    """
    Exploit category represents the high-level taxonomy of exploit effects.

    Possible values:
        :NONE: Either the category was not selected or is not one of the other possible values. This category should
            be used sparsely and after due consideration.
        :AUTH_MANIPULATION: The exploit is used to affect authentications or authorizations, such as privilege
            escalation or user manipulation.
        :DATA_MANIPULATION: The exploit is used to affect data, mostly through giving access to said data.
        :CODE_EXECUTION: The exploit enables execution of arbitrary code in the context of the action, so the result
            can be an opened session, new malware instance, etc.
    """
    NONE = 0,
    AUTH_MANIPULATION = 1,
    DATA_MANIPULATION = 2,
    CODE_EXECUTION = 4


class ExploitLocality(Enum):
    """
    The area of effect of the exploit, i.e., whether it can be used remotely, or whether only locally. Note that having
    a session opened to a remote machine counts as having a local access to the machine.

    Possible values:
        :NONE: Misconfiguration sentinel. No exploit should have this locality.
        :LOCAL: Exploit usable only locally.
        :REMOTE: Exploit usable over network.
    """
    NONE = 0
    LOCAL = 1
    REMOTE = 2


class ExploitParameterType(Enum):
    """
    Exploits support further parametrization. The parametrization works in a key-value manner, but unlike with actions,
    there is no domain specification of values, so they are all strings. This will likely change in the future. But
    at least keys (aka. parameter types have an explicit domain).

    Some parameters are mutable, i.e., able to be changed by the users, and some are immutable, which mostly describe
    in more details the effect of the exploit.

    Possible mutable values:
        :NONE: Misconfiguration sentinel. No exploit parameter should have this type.
        :IDENTITY: The parameter should contain a name of a user within a simulation.
        :ADDRESS: The parameter should contain an IP address.
        :SERVICE: The parameter should contain a service name.

    Possible immutable values:
        :IMPACT_IDENTITY: A successful exploitation will provide authorization for a selected few identities, or for
            all. Values: (ENUMERATED, ALL).
        :IMPACT_NODE: A successful exploitation will provide authorization for a selected few nodes, or for all.
            Values: (ENUMERATED, ALL).
        :IMPACT_SERVICE: A successful exploitation will provide authorization for a selected few services, or for all.
            Values: (ENUMERATED, ALL).
        :ENABLE_ELEVATED_ACCESS: An exploit enables to get root access. Values: (TRUE, FALSE).

    """
    NONE = 0,
    IDENTITY = 1,
    ADDRESS = 2,
    SERVICE = 3,
    IMPACT_IDENTITY = 1001,
    IMPACT_NODE = 1002,
    IMPACT_SERVICE = 1003,
    ENABLE_ELEVATED_ACCESS = 1004


class ExploitParameter(ABC):
    """
    A representation of exploit parameter.

    :param exploit_type: A type of the exploit parameter. TODO: Rename to include parameter.
    :type exploit_type: ExploitParameterType

    :param value: A value of the exploit parameter.
    :type value: str

    :param immutable: An immutability of the exploit parameter.
    :type immutable: bool
    """
    @abstractmethod
    def __init__(self, exploit_type: ExploitParameterType, value: str = "", immutable: bool = False) -> None:
        """ Initialization method for ExploitParameter """

    @property
    @abstractmethod
    def type(self) -> ExploitParameterType:
        """
        Returns a type of the exploit parameter.

        :rtype: ExploitParameterType
        """

    @property
    @abstractmethod
    def value(self) -> str:
        """
        Returns a value of the exploit parameter.

        :rtype: str
        """

    @abstractmethod
    def set_value(self, val: str) -> None:
        """
        Sets the value of the parameter, provided it is mutable one.

        :param val: A new value.
        :type val: str
        """


class VulnerableService(ABC):
    """
    A specification of a service and its version that are vulnerable for the purpose of exploit description.
    """

    @property
    @abstractmethod
    def id(self) -> str:
        """
        Returns the name of the service.

        :rtype: str
        """

    @property
    @abstractmethod
    def min_version(self) -> VersionInfo:
        """
        Returns the minimal affected version.

        :rtype: VersionInfo
        """

    @property
    @abstractmethod
    def max_version(self) -> VersionInfo:
        """
        Returns the maximal affected version.

        :rtype: VersionInfo
        """


class Exploit(ABC):
    """
    A description of a working exploit.
    """

    @property
    @abstractmethod
    def id(self) -> str:
        """
        Returns the unique identification of the exploit.

        :rtype: str
        """

    @property
    @abstractmethod
    def services(self) -> Dict[str, VulnerableService]:
        """
        Returns the collection of services that are affected by this exploit.

        :rtype: Dict[str, VulnerableService]:
        """

    @property
    @abstractmethod
    def locality(self) -> ExploitLocality:
        """
        Returns the locality of the exploit.

        :rtype: ExploitLocality
        """

    @property
    @abstractmethod
    def category(self) -> ExploitCategory:
        """
        Returns the category of the exploit.

        :rtype: ExploitCategory
        """

    @property
    @abstractmethod
    def parameters(self) -> Dict[ExploitParameterType, ExploitParameter]:
        """
        Returns the collection of exploit parameters.

        :rtype: Dict[ExploitParameterType, ExploitParameter]
        """
