from enum import Enum
from typing import List, Dict
from uuid import uuid4
from semver import VersionInfo

from cyst.api.logic.exploit import ExploitCategory, ExploitLocality, ExploitParameterType, VulnerableService, Exploit, ExploitParameter


class ExploitParameterImpl(ExploitParameter):
    # Immutable parameters are further specifications of an exploit, which can be used by the system, but cannot
    # be altered by the attacker (although they can be queried [and directly accessed by that's another story])
    def __init__(self, exploit_type: ExploitParameterType, value: str = "", immutable: bool = False):
        self._type = exploit_type
        self._value = value
        self._immutable = immutable

    @property
    def type(self) -> ExploitParameterType:
        return self._type

    @property
    def value(self) -> str:
        return self._value

    def set_value(self, value) -> None:
        # Failure to bypass immutability is silently ignored
        if not self._immutable:
            self._value = value


class VulnerableServiceImpl(VulnerableService):
    def __init__(self, id: str, min_version: str = "0.0.0", max_version: str = "0.0.0"):
        self._id = id

        if max_version == "0.0.0":
            max_version = min_version

        self._min_ver = VersionInfo.parse(min_version)
        self._max_ver = VersionInfo.parse(max_version)

        if self._min_ver > self._max_ver:
            raise Exception("Version mismatch, minimal version larger than maximal")

    @property
    def id(self):
        return self._id

    @property
    def min_version(self):
        return self._min_ver

    @property
    def max_version(self):
        return self._max_ver


class ExploitImpl(Exploit):
    def __init__(self, id: str = "", services: List[VulnerableService] = None, locality:
                 ExploitLocality = ExploitLocality.NONE, category: ExploitCategory = ExploitCategory.NONE,
                 *parameters: ExploitParameter) -> None:
        if not id:
            self._id = str(uuid4())
        else:
            self._id = id

        self._services = {}
        if services:
            for service in services:
                self._services[service.id] = service

        self._locality = locality
        self._category = category

        self._parameters = {}
        if parameters:
            for param in parameters:
                self._parameters[param.type] = param

    @property
    def id(self) -> str:
        return self._id

    @property
    def services(self) -> Dict[str, VulnerableService]:
        return self._services

    @property
    def locality(self) -> ExploitLocality:
        return self._locality

    @property
    def category(self) -> ExploitCategory:
        return self._category

    @property
    def parameters(self) -> Dict[ExploitParameterType, ExploitParameter]:
        return self._parameters
