"""ID slugification and validation for Legionnaire."""

import re


def slugify(raw: str) -> str:
    """Transform raw input into a valid ID.

    Replaces whitespace runs with '-' while preserving '/' characters.

    Args:
        raw: The raw input string to slugify.

    Returns:
        The slugified ID string.
    """
    return re.sub(r"\s+", "-", raw)


class InvalidIdError(ValueError):
    """Raised when an ID fails validation."""

    pass


def validate_id(id_str: str) -> None:
    """Validate an ID for use as git ref and filesystem path.

    Raises InvalidIdError if the ID is invalid.

    Args:
        id_str: The ID string to validate.

    Raises:
        InvalidIdError: If the ID fails validation with a descriptive message.
    """
    if not id_str:
        raise InvalidIdError("ID cannot be empty")

    # Check for empty segments (no //, leading or trailing /)
    if id_str.startswith("/"):
        raise InvalidIdError("ID cannot start with '/'")
    if id_str.endswith("/"):
        raise InvalidIdError("ID cannot end with '/'")
    if "//" in id_str:
        raise InvalidIdError("ID cannot contain empty segments '//'")

    # Split into segments and validate each
    segments = id_str.split("/")
    for segment in segments:
        if not segment:
            raise InvalidIdError("ID cannot contain empty segments")

        # No . or .. segments
        if segment == ".":
            raise InvalidIdError("ID cannot contain '.' segment")
        if segment == "..":
            raise InvalidIdError("ID cannot contain '..' segment")

        # No .lock suffix
        if segment.endswith(".lock"):
            raise InvalidIdError("ID segment cannot end with '.lock'")

    # Check for ASCII control characters (0x00-0x1F and 0x7F)
    for char in id_str:
        if ord(char) < 0x20 or ord(char) == 0x7F:
            raise InvalidIdError(f"ID cannot contain control character (ord={ord(char)})")

    # Git ref hazards: ~ ^ : ? * [ \
    git_hazards = set("~^:?*[\\")
    for char in id_str:
        if char in git_hazards:
            raise InvalidIdError(f"ID cannot contain git-unsafe character '{char}'")

    # Reject @{
    if "@{" in id_str:
        raise InvalidIdError("ID cannot contain '@{' sequence")

    # Spaces should not remain after slugify
    if " " in id_str:
        raise InvalidIdError("ID cannot contain spaces")


def slugify_and_validate(raw: str) -> str:
    """Slugify and validate an ID in one step.

    Args:
        raw: The raw input string.

    Returns:
        The validated slugified ID.

    Raises:
        InvalidIdError: If the slugified ID fails validation.
    """
    slugified = slugify(raw)
    validate_id(slugified)
    return slugified
