from __future__ import annotations

import re


def compare_versions(left: str, right: str) -> int:
    left_parts = version_tuple(left)
    right_parts = version_tuple(right)
    max_length = max(len(left_parts), len(right_parts))
    padded_left = left_parts + (0,) * (max_length - len(left_parts))
    padded_right = right_parts + (0,) * (max_length - len(right_parts))
    if padded_left < padded_right:
        return -1
    if padded_left > padded_right:
        return 1
    return 0


def version_satisfies_range(
    version: str,
    *,
    version_min: str | None = None,
    version_max_exclusive: str | None = None,
) -> bool:
    if version_min is not None and compare_versions(version, version_min) < 0:
        return False
    if version_max_exclusive is not None and compare_versions(version, version_max_exclusive) >= 0:
        return False
    return True


def version_range_is_valid(
    *,
    version_min: str | None = None,
    version_max_exclusive: str | None = None,
) -> bool:
    if version_min is None or version_max_exclusive is None:
        return True
    return compare_versions(version_min, version_max_exclusive) < 0


def format_version_range(
    *,
    version_min: str | None = None,
    version_max_exclusive: str | None = None,
) -> str:
    parts: list[str] = []
    if version_min is not None:
        parts.append(f">= {version_min}")
    if version_max_exclusive is not None:
        parts.append(f"< {version_max_exclusive}")
    return ", ".join(parts) if parts else "any version"


def version_tuple(value: str) -> tuple[int, ...]:
    match = re.match(r"^\s*v?(\d+(?:\.\d+)*)", value)
    if not match:
        return (0,)
    return tuple(int(part) for part in match.group(1).split("."))
