"""Network address validators for TUI inputs."""

from __future__ import annotations

import re
from ipaddress import AddressValueError, IPv4Address, IPv4Network
from typing import Literal

from textual.validation import ValidationResult, Validator

ValidatorType = Literal["address", "subnet", "gateway", "dns"]


class NetworkValidator(Validator):
    """Unified validator for network address fields.

    Supports validation of subnets, IP addresses, gateways, and DNS servers.
    For address validation, can optionally check if address is within a subnet.
    """

    def __init__(self, v_type: ValidatorType, subnet: str | None = None) -> None:
        """Initialize the validator.

        Args:
            v_type: The type of validation to perform
            subnet: Optional subnet for address containment validation
        """
        super().__init__()
        self.v_type = v_type
        self.subnet = subnet

    @staticmethod
    def is_valid_subnet(subnet: str) -> tuple[bool, str | None]:
        """Validate subnet is in CIDR format.

        Args:
            subnet: The subnet CIDR string to test

        Returns:
            Tuple of (is_valid, error_message)
        """
        try:
            network = IPv4Network(subnet)
        except (AddressValueError, ValueError) as e:
            return False, str(e)
        else:
            if network.prefixlen == 32:
                return False, "No network bits are set"
            return True, None

    @staticmethod
    def is_valid_dns(dns: str) -> tuple[bool, str | None]:
        """Validate DNS server list.

        Args:
            dns: Comma-separated list of DNS servers

        Returns:
            Tuple of (is_valid, error_message)
        """
        servers = list(filter(None, dns.split(",")))

        if not servers:
            return False, "DNS Servers cannot be empty"

        for server in servers:
            try:
                IPv4Address(server.strip())
            except AddressValueError as e:
                return False, str(e)

        return True, None

    def is_valid_address(
        self, address: str, subnet: str | None = None
    ) -> tuple[bool, str | None]:
        """Validate an IP address, optionally checking subnet containment.

        Args:
            address: The IP address to validate
            subnet: Optional subnet to check containment

        Returns:
            Tuple of (is_valid, error_message)
        """
        try:
            parsed_address = IPv4Address(address)
        except AddressValueError as e:
            return False, str(e)

        if not subnet:
            return True, None

        valid_subnet, _ = self.is_valid_subnet(subnet)

        if not valid_subnet:
            return True, None

        if parsed_address not in IPv4Network(subnet):
            return False, f"{subnet} does not contain {address}"

        return True, None

    def validate(self, value: str) -> ValidationResult:
        """Validate the input value based on validator type.

        Args:
            value: The value to validate

        Returns:
            ValidationResult indicating success or failure
        """
        match self.v_type:
            case "subnet":
                valid, error = self.is_valid_subnet(value)
                if valid:
                    return self.success()
                else:
                    return self.failure(error)
            case "address":
                valid, error = self.is_valid_address(value, self.subnet)
                if valid:
                    return self.success()
                else:
                    return self.failure(error)
            case "gateway":
                # Gateway is optional - empty values are valid
                if not value.strip():
                    return self.success()
                valid, error = self.is_valid_address(value)
                if valid:
                    return self.success()
                else:
                    return self.failure(error)
            case "dns":
                # DNS is optional - empty values are valid
                if not value.strip():
                    return self.success()
                valid, error = self.is_valid_dns(value)
                if valid:
                    return self.success()
                else:
                    return self.failure(error)
            case _:
                return self.failure("No Validation Type found")


class HostnameValidator(Validator):
    """Validates hostnames."""

    HOSTNAME_PATTERN = re.compile(r"^(?!-)[A-Za-z0-9-]{1,63}(?<!-)$")

    def validate(self, value: str) -> ValidationResult:
        """Validate a hostname.

        Args:
            value: The hostname string to validate

        Returns:
            ValidationResult indicating success or failure
        """
        if not value:
            return self.failure("Hostname is required")

        if len(value) > 253:
            return self.failure("Hostname too long (max 253 characters)")

        # Check each label
        labels = value.split(".")
        for label in labels:
            if not self.HOSTNAME_PATTERN.match(label):
                return self.failure(
                    "Invalid hostname (use letters, numbers, hyphens only)"
                )

        return self.success()


class VlanIdValidator(Validator):
    """Validates VLAN IDs (2-4094)."""

    def validate(self, value: str) -> ValidationResult:
        """Validate a VLAN ID.

        Args:
            value: The VLAN ID string to validate

        Returns:
            ValidationResult indicating success or failure
        """
        if not value:
            return self.failure("VLAN ID is required")

        try:
            vlan_id = int(value)
            if 2 <= vlan_id <= 4094:
                return self.success()
            return self.failure("VLAN ID must be between 2 and 4094")
        except ValueError:
            return self.failure("VLAN ID must be a number")
