from enum import Enum
from functools import total_ordering

from vulnsearch.exceptions import UtilValueError

risk_colors = {
    "CRITICAL": "#BA1300",
    "HIGH": "#E88510",
    "MEDIUM": "#F5F511",
    "LOW": "green",
    "NONE": "#888888",
}


@total_ordering
class Risk(Enum):
    critical = "CRITICAL"
    high = "HIGH"
    medium = "MEDIUM"
    low = "LOW"
    none = "NONE"

    @classmethod
    def __missing__(cls, value):
        if isinstance(value, str):
            value = value.upper()
            for member in cls:
                if member.value == value:
                    return member
        raise ValueError(f"Unknown severity: {value}")

    def __lt__(self, other):
        if isinstance(other, Risk):
            return self.to_score() < other.to_score()
        return NotImplemented

    def __str__(self):
        """Convert risks to a string."""
        return self.value.capitalize()

    def to_rich_color(self):
        """Convert risks to a rich color."""
        match self:
            case Risk.none:
                return f"[{risk_colors['NONE']}]"
            case Risk.low:
                return f"[{risk_colors['LOW']}]"
            case Risk.medium:
                return f"[{risk_colors['MEDIUM']}]"
            case Risk.high:
                return f"[{risk_colors['HIGH']}]"
            case Risk.critical:
                return f"[{risk_colors['CRITICAL']}]"
            case _:
                raise UtilValueError("unexpected severity value")

    def to_score(self) -> int:
        match self:
            case Risk.none:
                return 0
            case Risk.low:
                return 1
            case Risk.medium:
                return 2
            case Risk.high:
                return 3
            case Risk.critical:
                return 4

    @classmethod
    def from_score(cls, score: float) -> "Risk":
        if score is None or score == 0.0:
            return cls.none
        if 0.1 <= score <= 3.9:
            return cls.low
        if 4.0 <= score <= 6.9:
            return cls.medium
        if 7.0 <= score <= 8.9:
            return cls.high
        return cls.critical
