"""
K8s name validation against RFC 1123 and DNS subdomain standards.

This module provides validation for Kubernetes resource names:
- Cluster names: RFC 1123 hostname requirements
- Namespace names: DNS subdomain requirements
- Workload names: DNS subdomain requirements

References:
- RFC 1123: https://datatracker.ietf.org/doc/html/rfc1123#section-2.1
- K8s DNS subdomain: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names
"""

import re
from typing import Optional

from src.log_context import get_logger


class K8sNameValidator:
    """Validates k8s names against RFC 1123 and DNS subdomain standards."""

    # RFC 1123 hostname pattern (for cluster names)
    # - Start with alphanumeric [a-zA-Z0-9]
    # - Middle can contain [a-zA-Z0-9.-]
    # - End with alphanumeric [a-zA-Z0-9]
    # - Max 253 characters total
    RFC1123_PATTERN = r'^[a-zA-Z0-9](?:[a-zA-Z0-9.-]{0,251}[a-zA-Z0-9])?$'

    # DNS subdomain pattern (for namespace/workload names)
    # - Start with lowercase alphanumeric [a-z0-9]
    # - Middle can contain [a-z0-9.-]
    # - End with lowercase alphanumeric [a-z0-9]
    # - Max 253 characters total
    # - Supports dot-separated segments
    DNS_SUBDOMAIN_PATTERN = r'^(?=.{1,253}$)[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'

    # Special markers exempt from validation (internal use only)
    EXEMPT_NAMES = {"__UNALLOCATED__"}

    def __init__(self):
        """Initialize validator with compiled patterns."""
        self.rfc1123_regex = re.compile(self.RFC1123_PATTERN)
        self.dns_subdomain_regex = re.compile(self.DNS_SUBDOMAIN_PATTERN)
        self.logger = get_logger(__name__)

    def validate_cluster_name(self, name: str) -> tuple[bool, Optional[str]]:
        """
        Validate cluster name against RFC 1123 hostname requirements.

        Args:
            name: Cluster name to validate

        Returns:
            Tuple of (is_valid, error_message)
            - (True, None) if valid
            - (False, error_message) if invalid

        Examples:
            >>> validator = K8sNameValidator()
            >>> validator.validate_cluster_name("prod-us-east-web-a1b2")
            (True, None)
            >>> validator.validate_cluster_name("-invalid")
            (False, "Must start with alphanumeric character")
        """
        if not name:
            return False, "Cluster name cannot be empty"

        # Check length
        if len(name) > 253:
            return False, f"Cluster name length {len(name)} exceeds maximum of 253 characters"

        # Check pattern
        if not self.rfc1123_regex.match(name):
            # Provide specific error message
            if not name[0].isalnum():
                return False, "Must start with alphanumeric character (RFC 1123)"
            if not name[-1].isalnum():
                return False, "Must end with alphanumeric character (RFC 1123)"

            # Check for invalid characters
            invalid_chars = set(name) - set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-')
            if invalid_chars:
                return False, f"Contains invalid characters: {sorted(invalid_chars)} (RFC 1123 allows only [a-zA-Z0-9.-])"

            return False, "Does not match RFC 1123 hostname pattern"

        return True, None

    def validate_namespace_name(self, name: str) -> tuple[bool, Optional[str]]:
        """
        Validate namespace name against DNS subdomain requirements.

        Args:
            name: Namespace name to validate

        Returns:
            Tuple of (is_valid, error_message)

        Examples:
            >>> validator = K8sNameValidator()
            >>> validator.validate_namespace_name("team-abc123")
            (True, None)
            >>> validator.validate_namespace_name("Team-ABC")
            (False, "Must be lowercase only")
        """
        return self._validate_dns_subdomain(name, "namespace")

    def validate_workload_name(self, name: str) -> tuple[bool, Optional[str]]:
        """
        Validate workload name against DNS subdomain requirements.

        Args:
            name: Workload name to validate

        Returns:
            Tuple of (is_valid, error_message)

        Examples:
            >>> validator = K8sNameValidator()
            >>> validator.validate_workload_name("service-frontend-c3d4")
            (True, None)
            >>> validator.validate_workload_name("service_frontend")
            (False, "Contains invalid characters: ['_']")
        """
        return self._validate_dns_subdomain(name, "workload")

    def _validate_dns_subdomain(self, name: str, name_type: str) -> tuple[bool, Optional[str]]:
        """
        Internal validation against DNS subdomain requirements.

        Args:
            name: Name to validate
            name_type: Type of name (for error messages)

        Returns:
            Tuple of (is_valid, error_message)
        """
        # Check for exempt names
        if name in self.EXEMPT_NAMES:
            self.logger.debug(f"Name '{name}' exempt from validation", name_type=name_type)
            return True, None

        if not name:
            return False, f"{name_type.capitalize()} name cannot be empty"

        # Check length
        if len(name) > 253:
            return False, f"{name_type.capitalize()} name length {len(name)} exceeds maximum of 253 characters"

        # Check pattern
        if not self.dns_subdomain_regex.match(name):
            # Provide specific error message
            if name != name.lower():
                return False, "Must be lowercase only (DNS subdomain)"

            if not name[0].isalnum() or not name[0].islower():
                if name[0] == '_':
                    return False, "Must start with lowercase alphanumeric (underscores not allowed in DNS subdomain)"
                return False, "Must start with lowercase alphanumeric character (DNS subdomain)"

            if not name[-1].isalnum() or not name[-1].islower():
                if name[-1] in ['-', '.']:
                    return False, f"Must end with lowercase alphanumeric character (cannot end with '{name[-1]}')"
                return False, "Must end with lowercase alphanumeric character (DNS subdomain)"

            # Check for invalid characters
            valid_chars = set('abcdefghijklmnopqrstuvwxyz0123456789.-')
            invalid_chars = set(name) - valid_chars
            if invalid_chars:
                return False, f"Contains invalid characters: {sorted(invalid_chars)} (DNS subdomain allows only [a-z0-9.-])"

            return False, "Does not match DNS subdomain pattern"

        return True, None

    def validate_batch(
        self,
        names: list[str],
        name_type: str
    ) -> dict[str, Optional[str]]:
        """
        Validate multiple names at once.

        Args:
            names: List of names to validate
            name_type: Type of names ("cluster", "namespace", or "workload")

        Returns:
            Dictionary mapping name -> error_message (None if valid)

        Examples:
            >>> validator = K8sNameValidator()
            >>> names = ["valid-name", "Invalid-Name", "valid-2"]
            >>> results = validator.validate_batch(names, "namespace")
            >>> results
            {"valid-name": None, "Invalid-Name": "Must be lowercase only", "valid-2": None}
        """
        results = {}

        # Select validation method
        if name_type == "cluster":
            validate_fn = self.validate_cluster_name
        elif name_type == "namespace":
            validate_fn = self.validate_namespace_name
        elif name_type == "workload":
            validate_fn = self.validate_workload_name
        else:
            raise ValueError(f"Unknown name_type: {name_type}. Must be 'cluster', 'namespace', or 'workload'")

        # Validate each name
        for name in names:
            is_valid, error = validate_fn(name)
            results[name] = error  # None if valid, error message if invalid

        return results

    def get_validation_summary(
        self,
        validation_results: dict[str, Optional[str]]
    ) -> dict[str, any]:
        """
        Generate summary statistics from validation results.

        Args:
            validation_results: Results from validate_batch()

        Returns:
            Dictionary with summary statistics

        Examples:
            >>> results = {"name1": None, "name2": "Error", "name3": None}
            >>> summary = validator.get_validation_summary(results)
            >>> summary["valid_count"]
            2
            >>> summary["invalid_count"]
            1
        """
        valid_names = [name for name, error in validation_results.items() if error is None]
        invalid_names = [name for name, error in validation_results.items() if error is not None]

        return {
            "total_count": len(validation_results),
            "valid_count": len(valid_names),
            "invalid_count": len(invalid_names),
            "validation_rate": len(valid_names) / len(validation_results) if validation_results else 0.0,
            "invalid_names": invalid_names[:10],  # First 10 for review
            "error_summary": self._summarize_errors(validation_results)
        }

    def _summarize_errors(self, validation_results: dict[str, Optional[str]]) -> dict[str, int]:
        """Group validation errors by type for summary."""
        error_counts = {}

        for name, error in validation_results.items():
            if error is not None:
                # Extract error type (first sentence)
                error_type = error.split('(')[0].strip()
                error_counts[error_type] = error_counts.get(error_type, 0) + 1

        return error_counts
