"""
Translation Dictionary Validator

Validates translation dictionary schema and semantic correctness.
"""

import json
import re
from pathlib import Path
from typing import Any, Dict, List, Optional

from pydantic import ValidationError as PydanticValidationError

from .canonical import DSS_CANONICAL_COMPONENTS, DSS_CANONICAL_TOKENS
from .models import TranslationDictionary, TranslationSource


class ValidationError:
    """Single validation error."""

    def __init__(self, message: str, path: Optional[str] = None, severity: str = "error"):
        self.message = message
        self.path = path
        self.severity = severity  # error, warning, info

    def __str__(self) -> str:
        if self.path:
            return f"[{self.severity}] {self.path}: {self.message}"
        return f"[{self.severity}] {self.message}"


class ValidationResult:
    """Validation result container."""

    def __init__(self):
        self.is_valid = True
        self.errors: List[ValidationError] = []
        self.warnings: List[ValidationError] = []
        self.info: List[ValidationError] = []

    def add_error(self, message: str, path: Optional[str] = None) -> None:
        self.errors.append(ValidationError(message, path, "error"))
        self.is_valid = False

    def add_warning(self, message: str, path: Optional[str] = None) -> None:
        self.warnings.append(ValidationError(message, path, "warning"))

    def add_info(self, message: str, path: Optional[str] = None) -> None:
        self.info.append(ValidationError(message, path, "info"))


class TranslationValidator:
    """
    Validates translation dictionaries.

    Validation stages:
    1. Schema validation - JSON structure matches Pydantic model
    2. Token path validation - DSS paths are canonical
    3. Component validation - Component mappings are valid
    4. Custom prop validation - Namespacing is correct
    5. Consistency validation - No conflicts or duplicates
    """

    # Valid DSS path pattern - allows lowercase letters, numbers, and dots
    DSS_PATH_PATTERN = re.compile(r"^[a-z][a-z0-9]*(\.[a-z0-9]+)*$")

    def __init__(self, strict: bool = False, allow_unknown_tokens: bool = True):
        """
        Initialize validator.

        Args:
            strict: If True, unknown DSS tokens are errors (not warnings)
            allow_unknown_tokens: If False, all tokens must exist in canonical
        """
        self.strict = strict
        self.allow_unknown_tokens = allow_unknown_tokens

    def validate_dictionary(self, data: Dict[str, Any]) -> ValidationResult:
        """
        Validate a translation dictionary.

        Args:
            data: Dictionary data to validate

        Returns:
            ValidationResult with all errors/warnings
        """
        result = ValidationResult()

        # Stage 1: Schema validation
        self._validate_schema(data, result)
        if not result.is_valid:
            return result

        # Stage 2: Token path validation
        self._validate_token_paths(data, result)

        # Stage 3: Component validation
        self._validate_components(data, result)

        # Stage 4: Custom prop validation
        self._validate_custom_props(data, result)

        # Stage 5: Consistency validation
        self._validate_consistency(data, result)

        return result

    def _validate_schema(self, data: Dict[str, Any], result: ValidationResult) -> None:
        """Stage 1: Validate JSON structure."""
        try:
            TranslationDictionary(**data)
        except PydanticValidationError as e:
            for error in e.errors():
                path = ".".join(str(loc) for loc in error["loc"])
                result.add_error(error["msg"], path)
        except Exception as e:
            result.add_error(f"Schema validation failed: {str(e)}")

    def _validate_token_paths(self, data: Dict[str, Any], result: ValidationResult) -> None:
        """Stage 2: Validate DSS token paths."""
        mappings = data.get("mappings", {})
        tokens = mappings.get("tokens", {})

        for source_token, dss_path in tokens.items():
            # Validate path format
            if not self.DSS_PATH_PATTERN.match(dss_path):
                result.add_error(
                    f"Invalid DSS path format: '{dss_path}' "
                    "(must be dot-notation like 'color.primary.500')",
                    f"mappings.tokens.{source_token}",
                )
                continue

            # Validate against canonical structure
            if dss_path not in DSS_CANONICAL_TOKENS:
                if self._is_custom_namespace(dss_path):
                    # Custom namespaces are allowed
                    result.add_info(
                        f"Custom namespace token: {dss_path}",
                        f"mappings.tokens.{source_token}",
                    )
                elif self.allow_unknown_tokens:
                    result.add_warning(
                        f"DSS token not in canonical structure: {dss_path}",
                        f"mappings.tokens.{source_token}",
                    )
                else:
                    result.add_error(
                        f"Unknown DSS token: {dss_path}",
                        f"mappings.tokens.{source_token}",
                    )

    def _validate_components(self, data: Dict[str, Any], result: ValidationResult) -> None:
        """Stage 3: Validate component mappings."""
        mappings = data.get("mappings", {})
        components = mappings.get("components", {})

        for source_comp, dss_comp in components.items():
            # Extract base component name (before any variant specifiers)
            base_comp = dss_comp.split("[")[0]

            if base_comp not in DSS_CANONICAL_COMPONENTS:
                result.add_warning(
                    f"Component not in DSS canonical set: {base_comp}",
                    f"mappings.components.{source_comp}",
                )

            # Validate variant syntax if present
            if "[" in dss_comp:
                if not self._validate_variant_syntax(dss_comp):
                    result.add_error(
                        f"Invalid variant syntax: {dss_comp}",
                        f"mappings.components.{source_comp}",
                    )

    def _validate_custom_props(self, data: Dict[str, Any], result: ValidationResult) -> None:
        """Stage 4: Validate custom prop namespacing."""
        custom_props = data.get("custom_props", {})

        for prop_name, prop_value in custom_props.items():
            # Must use dot notation
            if "." not in prop_name:
                result.add_error(
                    f"Custom prop must use dot-notation namespace: {prop_name}",
                    f"custom_props.{prop_name}",
                )
                continue

            # Should use brand/custom namespace
            parts = prop_name.split(".")
            if len(parts) >= 2 and parts[1] not in ("brand", "custom"):
                result.add_warning(
                    f"Custom prop should use 'brand' or 'custom' namespace: {prop_name}. "
                    f"Recommended: {parts[0]}.brand.{'.'.join(parts[1:])}",
                    f"custom_props.{prop_name}",
                )

    def _validate_consistency(self, data: Dict[str, Any], result: ValidationResult) -> None:
        """Stage 5: Validate internal consistency."""
        mappings = data.get("mappings", {})
        tokens = mappings.get("tokens", {})
        custom_props = data.get("custom_props", {})

        # Check for duplicate DSS targets
        dss_targets = list(tokens.values())
        seen = set()
        for target in dss_targets:
            if target in seen:
                result.add_warning(
                    f"Multiple source tokens map to same DSS token: {target}",
                    "mappings.tokens",
                )
            seen.add(target)

        # Check custom props don't conflict with mappings
        for prop_name in custom_props.keys():
            if prop_name in tokens.values():
                result.add_error(
                    f"Custom prop conflicts with mapping target: {prop_name}",
                    f"custom_props.{prop_name}",
                )

    def _is_custom_namespace(self, path: str) -> bool:
        """Check if path uses custom namespace."""
        parts = path.split(".")
        if len(parts) >= 2:
            return parts[1] in ("brand", "custom")
        return False

    def _validate_variant_syntax(self, comp: str) -> bool:
        """Validate component variant syntax like Button[variant=primary]."""
        if "[" not in comp:
            return True

        # Check for matching brackets
        if comp.count("[") != comp.count("]"):
            return False

        # Extract variant part
        variant_match = re.search(r"\[([^\]]+)\]", comp)
        if not variant_match:
            return False

        # Validate key=value format
        variant_str = variant_match.group(1)
        for pair in variant_str.split(","):
            if "=" not in pair:
                return False
            key, value = pair.split("=", 1)
            if not key.strip() or not value.strip():
                return False

        return True

    def validate_file(self, file_path: str) -> ValidationResult:
        """
        Validate a translation dictionary file.

        Args:
            file_path: Path to JSON file

        Returns:
            ValidationResult
        """
        result = ValidationResult()

        try:
            with open(file_path, "r", encoding="utf-8") as f:
                data = json.load(f)
        except json.JSONDecodeError as e:
            result.add_error(f"Invalid JSON: {str(e)}")
            return result
        except FileNotFoundError:
            result.add_error(f"File not found: {file_path}")
            return result

        return self.validate_dictionary(data)
