"""
Version number normalization library.
"""
import re
from typing import List, Optional, Tuple, Any

from pineboolib.core.utils import logging

SubVersionNumber = int
SubVersionAppendedText = str
SubVersionTuple = Tuple[SubVersionNumber, SubVersionAppendedText]


LOGGER = logging.get_logger(__name__)


class VersionNumber:
    """
    Create version objects from string that can be easily be compared together.
    """

    is_null: bool  # True if no version at all.
    raw_text: str  # Raw version text or empty string if null.
    text: str  # Version text or empty string if null.
    normalized: List["SubVersionTuple"]  # Normalized version

    def __init__(self, version_string: Optional[str], default: Optional[str] = None) -> None:
        """
        Create Version object.
        """
        if default is not None and version_string is None:
            version_string = default

        self.is_null = version_string is None

        if version_string is None:
            version_string = ""

        self.raw_text = version_string
        self.text = version_string.strip().lstrip("v")
        self.normalized = [] if self.is_null else self.normalize_complex(version_string)

    def __eq__(self, other_any: Any) -> bool:
        """Compare for equality."""
        other: "VersionNumber" = self.to_version_number(other_any)
        return self.normalized == other.normalized

    def __neq__(self, other_any: Any) -> bool:
        """Compare for unequality."""
        other: "VersionNumber" = self.to_version_number(other_any)
        return self.normalized != other.normalized

    def __gt__(self, other_any: Any) -> bool:
        """Compare for greater than."""
        other: "VersionNumber" = self.to_version_number(other_any)
        return self.normalized > other.normalized

    def __lt__(self, other_any: Any) -> bool:
        """Compare for less than."""
        other: "VersionNumber" = self.to_version_number(other_any)
        return self.normalized < other.normalized

    def __ge__(self, other_any: Any) -> bool:
        """Compare for greater than equal."""
        other: "VersionNumber" = self.to_version_number(other_any)
        return self.normalized >= other.normalized

    def __le__(self, other_any: Any) -> bool:
        """Compare for less than equal."""
        other: "VersionNumber" = self.to_version_number(other_any)
        return self.normalized <= other.normalized

    def __str__(self) -> str:
        """Get string representation of the version."""
        return self.text

    def __repr__(self) -> str:
        """Get python representation of the version."""
        return "<%s %r>" % (str(self.__class__.__name__), None if self.is_null else self.text)

    @classmethod
    def to_version_number(self, other_any: Any) -> "VersionNumber":
        """Convert any input to VersionNumber."""
        other: "VersionNumber"
        if isinstance(other_any, str):
            other = VersionNumber(other_any)
        elif isinstance(other_any, VersionNumber):
            other = other_any
        else:
            raise ValueError("Can't compare VersionNumber with %r" % type(other_any))
        return other

    @classmethod
    def normalize_complex(cls, raw_text: str) -> List["SubVersionTuple"]:
        """
        Normalize a complex version as 1.0.5b-ubuntu0 into something that can be used for comparison.
        """
        decomposed_str: List[str] = raw_text.strip().lstrip("v").split(".")
        normalized: List[SubVersionTuple] = []
        subver: str
        for subver in decomposed_str:
            subver = subver.strip()
            subver_appended_part: SubVersionAppendedText = subver.lstrip("0123456789")

            subver_number_part = (
                subver[: -len(subver_appended_part)] if subver_appended_part else subver
            )

            subver_number: SubVersionNumber = int(subver_number_part) if subver_number_part else -1
            normalized.append((subver_number, subver_appended_part))
        return normalized

    @classmethod
    def check(cls, mod_name: str, mod_ver: str, min_ver: str) -> bool:
        """Compare two version numbers and raise a warning if "minver" is not met."""
        if cls.normalize(mod_ver) < cls.normalize(min_ver):
            LOGGER.warning(
                "La version de <%s> es %s. La mínima recomendada es %s.", mod_name, mod_ver, min_ver
            )
            return False
        return True

    @classmethod
    def normalize(cls, value: str) -> List[int]:
        """Normalize version string numbers like 3.10.1 so they can be compared."""
        return [int(item) for item in re.sub(r"[^\d\.]", "", value).split(".")]
