"""
Value sanitization for K8s name injection in metadata stage.

This module provides sanitization of metadata values before they are injected
into K8s namespace names, ensuring the final names comply with DNS subdomain
requirements.
"""

import re
from typing import Optional

from src.k8s.validation.name_validator import K8sNameValidator
from src.log_context import get_logger


class K8sValueSanitizer:
    """Sanitizes metadata values for safe injection into k8s names."""

    # Maximum safe length for a value to be injected (leaves room for base name + hyphen)
    DEFAULT_MAX_VALUE_LENGTH = 200

    def __init__(self, name_validator: K8sNameValidator):
        """
        Initialize sanitizer with validator.

        Args:
            name_validator: Validator for checking final name compliance
        """
        self.name_validator = name_validator
        self.logger = get_logger(__name__)

    def sanitize_for_k8s_name(
        self,
        value: str,
        max_length: int = DEFAULT_MAX_VALUE_LENGTH
    ) -> str:
        """
        Sanitize value for k8s name injection.

        Transforms the value to be DNS subdomain compliant:
        1. Convert to lowercase
        2. Replace invalid characters with hyphens
        3. Remove leading/trailing hyphens and dots
        4. Collapse consecutive hyphens
        5. Truncate to max_length
        6. Ensure result is non-empty

        Args:
            value: Value to sanitize
            max_length: Maximum length for sanitized value

        Returns:
            Sanitized value safe for injection

        Examples:
            >>> sanitizer = K8sValueSanitizer(K8sNameValidator())
            >>> sanitizer.sanitize_for_k8s_name("Pulse-Analytics")
            "pulse-analytics"
            >>> sanitizer.sanitize_for_k8s_name("pulse_analytics")
            "pulse-analytics"
            >>> sanitizer.sanitize_for_k8s_name("analytics-")
            "analytics"
        """
        if not value or not value.strip():
            self.logger.warning("Empty value provided for sanitization")
            return "default"

        # Step 1: Convert to lowercase
        result = value.lower()
        self.logger.debug(f"Lowercase: '{value}' -> '{result}'")

        # Step 2: Replace invalid characters with hyphen
        # Valid chars for DNS subdomain: [a-z0-9.-]
        # Replace everything else with hyphen
        result = re.sub(r'[^a-z0-9.-]', '-', result)
        self.logger.debug(f"Replace invalid: -> '{result}'")

        # Step 3: Remove leading/trailing hyphens and dots
        result = result.strip('-.')
        self.logger.debug(f"Strip edges: -> '{result}'")

        # Step 4: Collapse consecutive hyphens
        result = re.sub(r'-+', '-', result)
        self.logger.debug(f"Collapse hyphens: -> '{result}'")

        # Step 5: Truncate to max_length if needed
        if len(result) > max_length:
            result = result[:max_length]
            # Re-strip edges after truncation (might have cut at hyphen/dot)
            result = result.rstrip('-.')
            self.logger.debug(f"Truncate: -> '{result}' (length: {len(result)})")

        # Step 6: Ensure non-empty result
        if not result:
            self.logger.warning(f"Sanitization resulted in empty string for value '{value}', using 'default'")
            return "default"

        # Log the transformation if changed
        if result != value:
            self.logger.info(
                "Sanitized value for k8s injection",
                original=value,
                sanitized=result,
                original_length=len(value),
                sanitized_length=len(result)
            )

        return result

    def sanitize_values(
        self,
        values: list[str],
        max_length: int = DEFAULT_MAX_VALUE_LENGTH
    ) -> list[str]:
        """
        Sanitize multiple values at once.

        Args:
            values: List of values to sanitize
            max_length: Maximum length for each sanitized value

        Returns:
            List of sanitized values

        Examples:
            >>> sanitizer = K8sValueSanitizer(K8sNameValidator())
            >>> sanitizer.sanitize_values(["Pulse", "Analytics", "Team_A"])
            ["pulse", "analytics", "team-a"]
        """
        sanitized = []
        for value in values:
            sanitized_value = self.sanitize_for_k8s_name(value, max_length)
            sanitized.append(sanitized_value)

        return sanitized

    def validate_injection_safety(
        self,
        base_names: list[str],
        values: list[str]
    ) -> tuple[bool, Optional[str]]:
        """
        Pre-validate that injection won't create invalid names.

        Checks the maximum possible length before injection happens.
        This is a fail-fast check to catch length overflow issues early.

        Args:
            base_names: List of base namespace names
            values: List of values that will be injected

        Returns:
            Tuple of (is_safe, error_message)
            - (True, None) if injection is safe
            - (False, error_message) if injection would fail

        Examples:
            >>> sanitizer = K8sValueSanitizer(K8sNameValidator())
            >>> base_names = ["team-abc123"]
            >>> values = ["pulse-analytics"]
            >>> is_safe, error = sanitizer.validate_injection_safety(base_names, values)
            >>> is_safe
            True
        """
        if not base_names:
            return False, "No base names provided for validation"

        if not values:
            return False, "No values provided for validation"

        # Find longest base name
        max_base_len = max(len(name) for name in base_names)

        # Find longest value
        max_value_len = max(len(value) for value in values)

        # Calculate max possible length (base + hyphen + value)
        max_final_len = max_base_len + 1 + max_value_len

        # DNS subdomain max length is 253
        if max_final_len > 253:
            return False, (
                f"Metadata injection would exceed DNS subdomain limit of 253 characters. "
                f"Maximum possible length: {max_final_len} "
                f"(longest base name: {max_base_len} chars, "
                f"longest value: {max_value_len} chars, "
                f"separator: 1 char). "
                f"Consider using shorter metadata values or shorter base names."
            )

        self.logger.info(
            "Injection safety validated",
            max_base_length=max_base_len,
            max_value_length=max_value_len,
            max_final_length=max_final_len,
            dns_limit=253,
            safety_margin=253 - max_final_len
        )

        return True, None

    def validate_injected_names(
        self,
        injected_names: list[str]
    ) -> tuple[bool, list[str]]:
        """
        Validate that all injected names are DNS subdomain compliant.

        This is a post-injection validation to verify that the final
        names meet all requirements.

        Args:
            injected_names: List of namespace names after injection

        Returns:
            Tuple of (all_valid, invalid_names)
            - all_valid: True if all names are valid
            - invalid_names: List of names that failed validation

        Examples:
            >>> sanitizer = K8sValueSanitizer(K8sNameValidator())
            >>> names = ["team-abc123-pulse", "team-xyz-analytics"]
            >>> all_valid, invalid = sanitizer.validate_injected_names(names)
            >>> all_valid
            True
        """
        invalid_names = []

        for name in injected_names:
            is_valid, error = self.name_validator.validate_namespace_name(name)
            if not is_valid:
                invalid_names.append(name)
                self.logger.error(
                    "Invalid name after injection",
                    name=name,
                    error=error
                )

        all_valid = len(invalid_names) == 0

        if all_valid:
            self.logger.info(
                "All injected names validated successfully",
                total_names=len(injected_names)
            )
        else:
            self.logger.error(
                "Some injected names failed validation",
                total_names=len(injected_names),
                invalid_count=len(invalid_names),
                invalid_names=invalid_names[:10]  # First 10 for review
            )

        return all_valid, invalid_names

    def get_sanitization_summary(
        self,
        original_values: list[str],
        sanitized_values: list[str]
    ) -> dict:
        """
        Generate summary statistics for sanitization operation.

        Args:
            original_values: Original values before sanitization
            sanitized_values: Values after sanitization

        Returns:
            Dictionary with summary statistics

        Examples:
            >>> sanitizer = K8sValueSanitizer(K8sNameValidator())
            >>> original = ["Pulse", "Analytics", "Team-A"]
            >>> sanitized = sanitizer.sanitize_values(original)
            >>> summary = sanitizer.get_sanitization_summary(original, sanitized)
            >>> summary["total_values"]
            3
        """
        if len(original_values) != len(sanitized_values):
            raise ValueError(
                f"Mismatched lengths: {len(original_values)} original, "
                f"{len(sanitized_values)} sanitized"
            )

        changed_values = []
        unchanged_values = []

        for orig, san in zip(original_values, sanitized_values):
            if orig != san:
                changed_values.append((orig, san))
            else:
                unchanged_values.append(orig)

        return {
            "total_values": len(original_values),
            "changed_count": len(changed_values),
            "unchanged_count": len(unchanged_values),
            "change_rate": len(changed_values) / len(original_values) if original_values else 0.0,
            "changed_examples": changed_values[:5],  # First 5 examples
            "avg_original_length": sum(len(v) for v in original_values) / len(original_values) if original_values else 0,
            "avg_sanitized_length": sum(len(v) for v in sanitized_values) / len(sanitized_values) if sanitized_values else 0,
        }
