"""IP address and CIDR range validation and normalisation utilities.

All IP handling in BanGUI goes through these helpers to enforce consistency
and prevent malformed addresses from reaching fail2ban.
"""

import ipaddress


def is_valid_ip(address: str) -> bool:
    """Return ``True`` if *address* is a valid IPv4 or IPv6 address.

    Args:
        address: The string to validate.

    Returns:
        ``True`` if the string represents a valid IP address, ``False`` otherwise.
    """
    try:
        ipaddress.ip_address(address)
        return True
    except ValueError:
        return False


def is_valid_network(cidr: str) -> bool:
    """Return ``True`` if *cidr* is a valid IPv4 or IPv6 network in CIDR notation.

    Args:
        cidr: The string to validate, e.g. ``"192.168.0.0/24"``.

    Returns:
        ``True`` if the string is a valid CIDR network, ``False`` otherwise.
    """
    try:
        ipaddress.ip_network(cidr, strict=False)
        return True
    except ValueError:
        return False


def is_valid_ip_or_network(value: str) -> bool:
    """Return ``True`` if *value* is a valid IP address or CIDR network.

    Args:
        value: The string to validate.

    Returns:
        ``True`` if the string is a valid IP address or CIDR range.
    """
    return is_valid_ip(value) or is_valid_network(value)


def normalise_ip(address: str) -> str:
    """Return a normalised string representation of an IP address.

    IPv6 addresses are compressed to their canonical short form.
    IPv4 addresses are returned unchanged.

    Args:
        address: A valid IP address string.

    Returns:
        Normalised IP address string.

    Raises:
        ValueError: If *address* is not a valid IP address.
    """
    return str(ipaddress.ip_address(address))


def normalise_network(cidr: str) -> str:
    """Return a normalised string representation of a CIDR network.

    Host bits are masked to produce the network address.

    Args:
        cidr: A valid CIDR network string, e.g. ``"192.168.1.5/24"``.

    Returns:
        Normalised network string, e.g. ``"192.168.1.0/24"``.

    Raises:
        ValueError: If *cidr* is not a valid network.
    """
    return str(ipaddress.ip_network(cidr, strict=False))


def ip_version(address: str) -> int:
    """Return 4 or 6 depending on the IP version of *address*.

    Args:
        address: A valid IP address string.

    Returns:
        ``4`` for IPv4, ``6`` for IPv6.

    Raises:
        ValueError: If *address* is not a valid IP address.
    """
    return ipaddress.ip_address(address).version
