from dataclasses import dataclass, field
from semver import VersionInfo
from typing import List, Union, Dict, Optional
from uuid import uuid4
from serde import serialize, coerce

from cyst.api.configuration.configuration import ConfigItem
from cyst.api.logic.exploit import ExploitParameterType, ExploitCategory, ExploitLocality


@serialize(type_check=coerce)
@dataclass
class VulnerableServiceConfig(ConfigItem):
    """ Configuration of vulnerable service.

    This represents a version range of an exploitable service and is used only by exploits and exploit evaluation
    mechanisms.

    :param service: A name of the service. It is an equivalent of PassiveService/PassiveServiceConfig name.
    :type service: str

    :param min_version: A minimum version of the service that is vulnerable. Even though the type supports any string,
        using anything else than string representation of a semantic version will lead to problems.
    :type min_version: Union[VersionInfo, str]

    :param max_version: A maximum version of the service that is vulnerable. Even though the type supports any string,
        using anything else than string representation of a semantic version will lead to problems.
    :type max_version: Union[VersionInfo, str]
    """
    service: str
    min_version: Union[VersionInfo, str]
    max_version: Union[VersionInfo, str] = field(default="0.0.0")
    ref: str = field(default_factory=lambda: str(uuid4()))
    name: str = "__vulnerable_service"
    id: str = ""


@serialize(type_check=coerce)
@dataclass
class ExploitParameterConfig(ConfigItem):
    """ Configuration of exploit parameter.

    Exploit parameter either represents a specification of an exploit, which can't be modified by a user, but is
    taken into account by exploit evaluation mechanisms (e.g., if the impact is one user or all users), or provides a
    mean for a user to supply additional information necessary for full execution of the exploit (e.g., identity of
    a user to impersonate).

    Example:

    .. code-block:: python

        parameters=[
            ExploitParameterConfig(ExploitParameterType.IDENTITY, immutable=False),
            ExploitParameterConfig(ExploitParameterType.ENABLE_ELEVATED_ACCESS, "FALSE", immutable=True)
        ],

    :param type: A type of the exploit parameter.
    :type type: ExploitParameterType

    :param value: A value of the parameter.
    :type value: Optional[str]

    :param immutable: A flag indicating, whether a user can change the value of the parameter.
    :type immutable: bool
    """
    type: ExploitParameterType
    value: Optional[str] = None
    immutable: bool = True
    ref: str = field(default_factory=lambda: str(uuid4()))
    name: str = "__exploit_parameter"
    id: str = ""


@serialize(type_check=coerce)
@dataclass
class ExploitConfig(ConfigItem):
    """ Configuration of a working exploit.

    If a definition of exploit exists, then a services it refers to are vulnerable, as long as their version match.
    Patching the service and bumping a version over the max vulnerable version is the only countermeasure.

    Example:

    .. code-block:: python

        ExploitConfig(
            services=[
                VulnerableServiceConfig(
                    name="bash",
                    min_version="5.0.0",
                    max_version="5.1.0"
                )
            ],
            locality=ExploitLocality.LOCAL,
            category=ExploitCategory.AUTH_MANIPULATION,
            parameters=[
                ExploitParameterConfig(
                    type=ExploitParameterType.ENABLE_ELEVATED_ACCESS,
                    value="TRUE",
                    immutable=True
                )
            ],
            id="bash_root_exploit"
        )

    :param services: A list of services that are vulnerable to this exploit.
    :type services: List[Union[VulnerableServiceConfig, str]]

    :param locality: Determines if the exploit can be used locally or remotely.
    :type locality: ExploitLocality

    :param category: Determines the category of the exploit.
    :type category: ExploitCategory

    :param parameters: An optional list of exploit parameters.
    :type parameters: Optional[List[ExploitParameterConfig]]
    """
    services: List[Union[VulnerableServiceConfig, str]]
    locality: ExploitLocality
    category: ExploitCategory
    parameters: Optional[List[ExploitParameterConfig]] = None
    ref: str = field(default_factory=lambda: str(uuid4()))
    name: str = "__exploit"
    id: str = ""
