"""
Structure Validator and Composer for Module Components

Provides:
1. Deterministic validation of component structures against hierarchy
2. Fallback structure generation from inputs
3. Symbol-enriched relationship text for embeddings
4. Template composition system

This is module-agnostic - works with any ModuleWrapper.

Usage:
    from adapters.structure_validator import StructureValidator

    validator = StructureValidator(wrapper)

    # Validate a structure
    is_valid, issues = validator.validate_structure("§[đ, ᵬ]")

    # Generate structure from inputs
    structure = validator.generate_structure_from_inputs({"text": "Hello", "button": "Click"})

    # Get symbol-enriched relationship text for embedding
    text = validator.get_enriched_relationship_text("Section")
"""

import logging
import re
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Set, Tuple

logger = logging.getLogger(__name__)


@dataclass
class ValidationResult:
    """Result of structure validation."""

    is_valid: bool
    structure: str
    issues: List[str] = field(default_factory=list)
    suggestions: List[str] = field(default_factory=list)
    resolved_components: Dict[str, str] = field(
        default_factory=dict
    )  # symbol → component name


@dataclass
class ComponentSlot:
    """A slot in a structure that can accept inputs."""

    component_name: str
    symbol: str
    field_name: Optional[str] = None
    accepts_types: List[str] = field(default_factory=list)
    multiplier: int = 1


# =============================================================================
# INPUT TYPE INFERENCE
# =============================================================================

# Patterns to infer what component an input needs
INPUT_PATTERNS = {
    # Text content → DecoratedText or TextParagraph
    "text": ["DecoratedText", "TextParagraph"],
    "title": ["DecoratedText", "CardHeader"],
    "subtitle": ["DecoratedText", "CardHeader"],
    "description": ["TextParagraph", "DecoratedText"],
    # Media → Image or Icon
    "image": ["Image"],
    "image_url": ["Image"],
    "icon": ["Icon"],
    "icon_name": ["Icon"],
    # Interactive → Button
    "button": ["Button", "ButtonList"],
    "button_text": ["Button"],
    "buttons": ["ButtonList"],
    "action": ["Button", "OnClick"],
    "url": ["OpenLink", "Button"],
    # Selection → SelectionInput
    "select": ["SelectionInput"],
    "options": ["SelectionInput"],
    "dropdown": ["SelectionInput"],
    "checkbox": ["SelectionInput"],
    # Grid → Grid/GridItem
    "grid": ["Grid"],
    "items": ["Grid", "GridItem"],
    "columns": ["Columns"],
    # Date/Time
    "date": ["DateTimePicker"],
    "time": ["DateTimePicker"],
    "datetime": ["DateTimePicker"],
}

# Components that can be direct children of Section (Widget subtypes)
WIDGET_TYPES = {
    "DecoratedText",
    "TextParagraph",
    "Image",
    "ButtonList",
    "Grid",
    "SelectionInput",
    "DateTimePicker",
    "Divider",
    "TextInput",
    "Columns",
    "ChipList",
}

# Components that need a wrapper
NEEDS_WRAPPER = {
    "Button": "ButtonList",  # Button needs to be in ButtonList
    "Chip": "ChipList",  # Chip needs to be in ChipList
    "GridItem": "Grid",  # GridItem needs to be in Grid
    "Column": "Columns",  # Column needs to be in Columns
}


class StructureValidator:
    """
    Validates and composes component structures.

    Module-agnostic: works with any ModuleWrapper instance.
    """

    def __init__(self, wrapper: "ModuleWrapper"):
        """
        Initialize with a ModuleWrapper instance.

        Args:
            wrapper: ModuleWrapper with loaded components and relationships
        """
        self.wrapper = wrapper
        self._symbols: Optional[Dict[str, str]] = None
        self._reverse_symbols: Optional[Dict[str, str]] = None
        self._relationships: Optional[Dict[str, List[str]]] = None
        self._widget_types: Set[str] = WIDGET_TYPES.copy()

    @property
    def symbols(self) -> Dict[str, str]:
        """Get symbol mappings (cached).

        Uses shared symbols from structure_dsl for consistency with the DSL parser.
        Falls back to ModuleWrapper-generated symbols if structure_dsl not available.
        """
        if self._symbols is None:
            try:
                # Use shared symbols from structure_dsl for consistency
                from gchat.structure_dsl import COMPONENT_TO_SYMBOL, ensure_initialized

                ensure_initialized()
                if COMPONENT_TO_SYMBOL:
                    self._symbols = COMPONENT_TO_SYMBOL.copy()
                else:
                    # Fallback to ModuleWrapper-generated symbols
                    self._symbols = self.wrapper.generate_component_symbols(
                        use_prefix=False
                    )
            except ImportError:
                # structure_dsl not available, use ModuleWrapper symbols
                self._symbols = self.wrapper.generate_component_symbols(
                    use_prefix=False
                )
        return self._symbols

    @property
    def reverse_symbols(self) -> Dict[str, str]:
        """Get reverse symbol mappings: symbol → component name."""
        if self._reverse_symbols is None:
            self._reverse_symbols = {v: k for k, v in self.symbols.items()}
        return self._reverse_symbols

    @property
    def relationships(self) -> Dict[str, List[str]]:
        """Get parent → children relationships (cached)."""
        if self._relationships is None:
            raw_rels = self.wrapper.extract_relationships_by_parent(max_depth=3)
            # Flatten to just child class names
            self._relationships = {}
            for parent, children in raw_rels.items():
                child_names = list(
                    set(
                        c.get("child_class", "")
                        for c in children
                        if c.get("child_class")
                    )
                )
                if child_names:
                    self._relationships[parent] = child_names
        return self._relationships

    # =========================================================================
    # STRUCTURE PARSING
    # =========================================================================

    def parse_structure(self, structure: str) -> List[Dict[str, Any]]:
        """
        Parse a DSL structure string into components.

        Args:
            structure: DSL string like "§[đ, ᵬ×2]"

        Returns:
            List of parsed components with nesting info
        """
        # Remove whitespace
        structure = structure.strip()

        components = []
        self._parse_recursive(structure, components, depth=0)
        return components

    def _parse_recursive(self, s: str, components: List, depth: int):
        """Recursively parse structure."""
        if not s:
            return

        # Split by comma at current level (not inside brackets)
        parts = self._split_at_level(s)

        for part in parts:
            part = part.strip()
            if not part:
                continue

            # Parse component: symbol(×N)?([children])?
            # Use proper bracket matching, not regex for nested brackets
            symbol = ""
            multiplier = 1
            children_str = ""

            i = 0
            # Extract symbol (everything before [ or × or end)
            while i < len(part) and part[i] not in "[×":
                symbol += part[i]
                i += 1

            symbol = symbol.strip()
            if not symbol:
                continue

            # Check for multiplier (×N)
            if i < len(part) and part[i] == "×":
                i += 1
                num_str = ""
                while i < len(part) and part[i].isdigit():
                    num_str += part[i]
                    i += 1
                if num_str:
                    multiplier = int(num_str)

            # Check for children [...]
            if i < len(part) and part[i] == "[":
                # Find matching closing bracket using level counting
                level = 0
                start = i + 1
                for j in range(i, len(part)):
                    if part[j] == "[":
                        level += 1
                    elif part[j] == "]":
                        level -= 1
                        if level == 0:
                            children_str = part[start:j]
                            break

            # Resolve symbol to component name
            component_name = self.reverse_symbols.get(symbol, symbol)

            comp = {
                "symbol": symbol,
                "name": component_name,
                "multiplier": multiplier,
                "depth": depth,
                "children": [],
            }

            # Parse children if present
            if children_str:
                self._parse_recursive(children_str, comp["children"], depth + 1)

            components.append(comp)

    def _split_at_level(self, s: str) -> List[str]:
        """Split string by comma, but only at the top bracket level."""
        parts = []
        current = ""
        level = 0

        for char in s:
            if char == "[":
                level += 1
                current += char
            elif char == "]":
                level -= 1
                current += char
            elif char == "," and level == 0:
                parts.append(current.strip())
                current = ""
            else:
                current += char

        if current.strip():
            parts.append(current.strip())

        return parts

    # =========================================================================
    # VALIDATION
    # =========================================================================

    def validate_structure(self, structure: str) -> ValidationResult:
        """
        Validate a DSL structure against the component hierarchy.

        Args:
            structure: DSL string like "§[đ, Ƀ[ᵬ×2]]"

        Returns:
            ValidationResult with is_valid, issues, and suggestions
        """
        result = ValidationResult(
            is_valid=True,
            structure=structure,
        )

        try:
            components = self.parse_structure(structure)
        except Exception as e:
            result.is_valid = False
            result.issues.append(f"Parse error: {e}")
            return result

        # Validate each component
        for comp in components:
            self._validate_component(comp, None, result)

        return result

    def _validate_component(
        self,
        comp: Dict,
        parent: Optional[Dict],
        result: ValidationResult,
    ):
        """Validate a single component and its children."""
        name = comp["name"]
        symbol = comp["symbol"]

        # Check if symbol is known
        if symbol not in self.reverse_symbols and name not in self.symbols:
            result.issues.append(f"Unknown symbol/component: {symbol}")
            # Don't mark invalid - might still work
        else:
            result.resolved_components[symbol] = name

        # Check parent-child relationship
        if parent:
            parent_name = parent["name"]
            valid_children = self.relationships.get(parent_name, [])

            # Check direct relationship
            if name not in valid_children:
                # Check if it's a Widget type (can go in Section/Column)
                if parent_name in ["Section", "Column"] and name in self._widget_types:
                    pass  # Valid - Widget types can go in Section/Column
                elif name in NEEDS_WRAPPER:
                    wrapper = NEEDS_WRAPPER[name]
                    result.issues.append(f"{name} should be wrapped in {wrapper}")
                    result.suggestions.append(
                        f"Use {self.symbols.get(wrapper, wrapper)}[{symbol}] instead of {symbol}"
                    )
                    result.is_valid = False
                else:
                    result.issues.append(
                        f"{name} cannot be direct child of {parent_name}"
                    )
                    result.is_valid = False

        # Validate children recursively
        for child in comp.get("children", []):
            self._validate_component(child, comp, result)

    def can_contain(self, parent: str, child: str) -> bool:
        """
        Check if parent component can contain child component.

        Args:
            parent: Parent component name
            child: Child component name

        Returns:
            True if valid containment
        """
        # Direct relationship
        if child in self.relationships.get(parent, []):
            return True

        # Widget types can go in Section/Column
        if parent in ["Section", "Column"] and child in self._widget_types:
            return True

        # Check if child needs wrapper that parent can contain
        if child in NEEDS_WRAPPER:
            wrapper = NEEDS_WRAPPER[child]
            return self.can_contain(parent, wrapper)

        return False

    # =========================================================================
    # STRUCTURE GENERATION (Fallback)
    # =========================================================================

    def generate_structure_from_inputs(
        self,
        inputs: Dict[str, Any],
        container: str = "Section",
    ) -> str:
        """
        Deterministically generate a valid structure from inputs.

        This is the FALLBACK when LLM doesn't provide perfect structure.

        Args:
            inputs: Dict of input values (e.g., {"text": "Hello", "button": "Click"})
            container: Root container component (default: Section)

        Returns:
            Valid DSL structure string
        """
        # Step 1: Infer components from input keys
        needed_components = self._infer_components_from_inputs(inputs)

        # Step 2: Wrap components that need wrappers
        wrapped = self._apply_wrappers(needed_components)

        # Step 3: Filter to only Widget types (can go in Section)
        valid_children = [c for c in wrapped if c in self._widget_types]

        # Step 4: Build structure
        container_sym = self.symbols.get(container, container)

        if not valid_children:
            # Default to DecoratedText if no valid components found
            return f"{container_sym}[{self.symbols.get('DecoratedText', 'đ')}]"

        child_syms = []
        for child in valid_children:
            sym = self.symbols.get(child, child)

            # Check if this component has sub-components
            if child == "ButtonList" and "buttons" in inputs:
                # Count buttons
                btn_count = (
                    len(inputs["buttons"]) if isinstance(inputs["buttons"], list) else 1
                )
                btn_sym = self.symbols.get("Button", "ᵬ")
                child_syms.append(f"{sym}[{btn_sym}×{btn_count}]")
            elif child == "Grid" and "items" in inputs:
                item_count = (
                    len(inputs["items"]) if isinstance(inputs["items"], list) else 1
                )
                item_sym = self.symbols.get("GridItem", "ǵ")
                child_syms.append(f"{sym}[{item_sym}×{item_count}]")
            else:
                child_syms.append(sym)

        return f"{container_sym}[{', '.join(child_syms)}]"

    def _infer_components_from_inputs(self, inputs: Dict[str, Any]) -> List[str]:
        """Infer which components are needed based on input keys."""
        components = []

        for key in inputs.keys():
            key_lower = key.lower()

            # Check exact match first
            if key_lower in INPUT_PATTERNS:
                components.extend(INPUT_PATTERNS[key_lower][:1])  # Take first match
                continue

            # Check partial match
            for pattern, comps in INPUT_PATTERNS.items():
                if pattern in key_lower or key_lower in pattern:
                    components.extend(comps[:1])
                    break

        # Remove duplicates while preserving order
        seen = set()
        unique = []
        for c in components:
            if c not in seen:
                seen.add(c)
                unique.append(c)

        return unique

    def _apply_wrappers(self, components: List[str]) -> List[str]:
        """Apply necessary wrappers to components."""
        result = []

        for comp in components:
            if comp in NEEDS_WRAPPER:
                wrapper = NEEDS_WRAPPER[comp]
                if wrapper not in result:
                    result.append(wrapper)
            else:
                result.append(comp)

        return result

    # =========================================================================
    # SYMBOL-ENRICHED RELATIONSHIPS (for embedding)
    # =========================================================================

    def get_enriched_relationship_text(self, component_name: str) -> str:
        """
        Get relationship text enriched with symbols for embedding.

        This creates text like:
        "§ Section contains ʍ Widget | §[đ, ᵬ] Section with DecoratedText Button"

        Args:
            component_name: Component to get relationships for

        Returns:
            Symbol-enriched text suitable for embedding
        """
        symbol = self.symbols.get(component_name, "?")
        children = self.relationships.get(component_name, [])

        if not children:
            return f"{symbol} {component_name}"

        parts = []

        # Part 1: Name with symbol
        parts.append(f"{symbol} {component_name}")

        # Part 2: Contains relationships with symbols
        child_parts = []
        for child in children[:5]:  # Limit for embedding efficiency
            child_sym = self.symbols.get(child, "?")
            child_parts.append(f"{child_sym} {child}")

        parts.append(f"contains {', '.join(child_parts)}")

        # Part 3: DSL example
        child_syms = [self.symbols.get(c, c) for c in children[:3]]
        dsl_example = f"{symbol}[{', '.join(child_syms)}]"
        parts.append(dsl_example)

        return " | ".join(parts)

    def get_all_enriched_relationships(self) -> Dict[str, str]:
        """
        Get enriched relationship text for all components.

        Returns:
            Dict mapping component name to enriched text
        """
        enriched = {}

        for component_name in self.relationships.keys():
            enriched[component_name] = self.get_enriched_relationship_text(
                component_name
            )

        return enriched

    # =========================================================================
    # TEMPLATE COMPOSITION
    # =========================================================================

    def compose_from_template(
        self,
        template: str,
        inputs: Dict[str, Any],
    ) -> Tuple[str, Dict[str, Any]]:
        """
        Compose a structure from a template with variable substitution.

        Templates use {variable} syntax:
        - "§[đ, Ƀ[ᵬ×{button_count}]]"
        - "§[ɨ, đ×{text_count}, Ƀ[ᵬ×{button_count}]]"

        Args:
            template: Template string with {variables}
            inputs: Dict with variable values and content

        Returns:
            Tuple of (resolved_structure, field_mapping)
        """
        structure = template

        # Substitute variables
        for key, value in inputs.items():
            placeholder = f"{{{key}}}"
            if placeholder in structure:
                if isinstance(value, (list, tuple)):
                    structure = structure.replace(placeholder, str(len(value)))
                else:
                    structure = structure.replace(placeholder, str(value))

        # Remove any remaining unresolved placeholders (default to 1)
        structure = re.sub(r"\{[^}]+\}", "1", structure)

        return structure, inputs


# =============================================================================
# FACTORY FUNCTION
# =============================================================================


def create_validator(wrapper: "ModuleWrapper") -> StructureValidator:
    """
    Create a StructureValidator for a ModuleWrapper.

    Args:
        wrapper: Initialized ModuleWrapper

    Returns:
        StructureValidator instance
    """
    return StructureValidator(wrapper)
