"""Tag validation for canonical English policy and namespace rules.

Implements validation per Spec Section 2.3 & 3.2.
"""

import re
from typing import Optional

import structlog

from src.extraction_v2.taxonomy_loader import TaxonomyLoader

logger = structlog.get_logger(__name__)


class TagValidator:
    """Validates tags against taxonomy rules and canonical English policy.

    Phase 3 Enhancement: Enforces namespace validation and canonical English.
    """

    def __init__(self, taxonomy_loader: TaxonomyLoader):
        """Initialize tag validator.

        Args:
            taxonomy_loader: Loaded taxonomy configuration
        """
        self.taxonomy = taxonomy_loader
        self.logger = structlog.get_logger(__name__)

    def validate_tag(self, tag: str) -> tuple[bool, Optional[str], Optional[str]]:
        """Validate a tag against all rules.

        Args:
            tag: Tag string in format "namespace:value"

        Returns:
            Tuple of (is_valid, validation_status, reason)
            - validation_status: "valid", "flagged", "rejected"
            - reason: Human-readable reason or None
        """
        # Basic format check
        if ":" not in tag:
            return False, "rejected", "missing_namespace_separator"

        namespace, value = tag.split(":", 1)

        # Check forbidden namespaces
        forbidden = self.taxonomy.get_forbidden_namespaces()
        if namespace in forbidden:
            return False, "rejected", f"forbidden_namespace:{namespace}"

        # Check if value is empty
        if not value or not value.strip():
            return False, "rejected", "empty_tag_value"

        # Check pre-defined tags (always valid)
        all_tags = self.taxonomy.get_all_tags()
        if tag in all_tags:
            return True, "valid", None

        # Check dynamic namespaces
        dynamic_namespaces = self.taxonomy.get_dynamic_namespaces()
        if namespace in dynamic_namespaces:
            # Validate dynamic tag value
            is_valid, reason = self._validate_dynamic_tag(namespace, value)
            if not is_valid:
                return False, "rejected", reason
            return True, "valid", None

        # Unknown namespace - discovery mode handling
        strict_mode = self.taxonomy.namespace_rules.get("strict_mode", False)
        if strict_mode:
            return False, "rejected", f"unknown_namespace:{namespace}"
        else:
            # Discovery mode: flag but allow for review
            return True, "flagged", f"unknown_namespace_pending_review:{namespace}"

    def _validate_dynamic_tag(
        self, namespace: str, value: str
    ) -> tuple[bool, Optional[str]]:
        """Validate a dynamic tag value against namespace rules.

        Args:
            namespace: Namespace name (e.g., "topic", "entity")
            value: Tag value

        Returns:
            Tuple of (is_valid, reason)
        """
        ns_rules = self.taxonomy.namespace_rules["namespaces"][namespace]
        validation = ns_rules.get("validation", {})

        # Check length
        min_len = validation.get("min_length", 1)
        max_len = validation.get("max_length", 100)
        if not (min_len <= len(value) <= max_len):
            return False, f"length_violation:{min_len}-{max_len}"

        # Check pattern
        pattern = validation.get("pattern")
        if pattern:
            if not re.match(pattern, value):
                return False, f"pattern_violation:{pattern}"

        # Check canonical English policy (Spec Section 3.2)
        canonical_lang = validation.get("canonical_language", "en")
        allow_non_ascii = validation.get("allow_non_ascii", False)

        if canonical_lang == "en" and not allow_non_ascii:
            # Strict ASCII check (rejects Japanese, Vietnamese, etc.)
            if not value.isascii():
                return False, "non_ascii_violation:canonical_english_required"

            # Additional check: ensure lowercase with underscores
            if not re.match(r"^[a-z0-9_]+$", value):
                return False, "format_violation:lowercase_with_underscores_required"

        return True, None

    def validate_batch(
        self, tags: list[str]
    ) -> dict[str, dict[str, Optional[str]]]:
        """Validate a batch of tags.

        Args:
            tags: List of tag strings

        Returns:
            Dict mapping tag to validation result:
            {
                "req:functional": {"status": "valid", "reason": None},
                "misc:test": {"status": "rejected", "reason": "forbidden_namespace:misc"}
            }
        """
        results = {}
        for tag in tags:
            is_valid, status, reason = self.validate_tag(tag)
            results[tag] = {"status": status, "reason": reason}

        return results

    def filter_valid_tags(
        self, tags: list[str], include_flagged: bool = True
    ) -> tuple[list[str], list[str]]:
        """Filter tags into valid and rejected.

        Args:
            tags: List of tag strings
            include_flagged: Whether to include flagged tags in valid list

        Returns:
            Tuple of (valid_tags, rejected_tags)
        """
        valid = []
        rejected = []

        for tag in tags:
            is_valid, status, reason = self.validate_tag(tag)

            if status == "valid":
                valid.append(tag)
            elif status == "flagged" and include_flagged:
                valid.append(tag)
            else:
                rejected.append(tag)
                self.logger.warning(
                    "tag_rejected",
                    tag=tag,
                    reason=reason,
                    status=status,
                )

        return valid, rejected
