"""
Singleton ModuleWrapper for card_framework.

This provides a single shared ModuleWrapper instance for all gchat modules
that need to access card_framework components. Avoids redundant wrapper
creation and ensures consistent configuration.

Features:
    - Singleton pattern for shared access
    - Text indices for keyword/phrase search
    - Symbol generation for compact DSL notation
    - Styling registry for color/formatting rules

Usage:
    from gchat.card_framework_wrapper import get_card_framework_wrapper

    wrapper = get_card_framework_wrapper()

    # Vector search
    results = wrapper.search("button with icon")

    # Text search (keyword)
    results = wrapper.search_by_text("Button", field="name")

    # Relationship search
    results = wrapper.search_by_relationship_text("clickable image")

    # Hybrid search (text + vector)
    results = wrapper.hybrid_search("decorated text with button")

    # Get symbols for DSL
    symbols = get_gchat_symbols()
"""

import logging
import threading
from typing import Dict, List, Optional

from config.enhanced_logging import setup_logger

logger = setup_logger(__name__)

# Thread-safe singleton
_wrapper: Optional["ModuleWrapper"] = None
_wrapper_lock = threading.Lock()

# Cached symbols
_symbols: Optional[Dict[str, str]] = None
_symbols_lock = threading.Lock()

# =============================================================================
# GCHAT-SPECIFIC CONFIGURATION
# =============================================================================

# Google Chat Card API Limits
GCHAT_CARD_MAX_BYTES = 20_000  # Maximum card payload size
GCHAT_TEXT_MAX_CHARS = 18_000  # Maximum text message length
GCHAT_SAFE_LIMIT_RATIO = 0.75  # Recommended operating ratio (75% of max)
GCHAT_SAFE_CARD_BYTES = int(GCHAT_CARD_MAX_BYTES * GCHAT_SAFE_LIMIT_RATIO)  # ~15KB

# Domain-specific stopwords for gchat card search
GCHAT_STOPWORDS = [
    "widget",  # Too generic
    "component",  # Too generic
    "google",  # Domain noise
    "chat",  # Domain noise
    "card",  # Everything is a card
    "framework",  # Module name noise
]

# NOTE: Symbols are auto-generated by SymbolGenerator based on LETTER_SYMBOLS pools.
# No hardcoded overrides - ModuleWrapper.generate_component_symbols() is the SSoT.
# See adapters/symbol_generator.py:LETTER_SYMBOLS for the character pools.


# =============================================================================
# SINGLETON ACCESS
# =============================================================================


def get_card_framework_wrapper(
    force_reinitialize: bool = False,
    ensure_text_indices: bool = True,
) -> "ModuleWrapper":
    """
    Get the singleton ModuleWrapper for card_framework.

    This wrapper is shared across:
        - SmartCardBuilder
        - TemplateComponent
        - card_tools
        - CardBuilderV2
        - Any other gchat modules needing card_framework access

    Args:
        force_reinitialize: If True, recreate the wrapper even if one exists.
                           Use sparingly (e.g., after collection schema changes).
        ensure_text_indices: If True, ensure text indices exist on first init.

    Returns:
        Shared ModuleWrapper instance configured for card_framework
    """
    global _wrapper

    with _wrapper_lock:
        if _wrapper is None or force_reinitialize:
            _wrapper = _create_wrapper(ensure_text_indices=ensure_text_indices)

    return _wrapper


def _create_wrapper(ensure_text_indices: bool = True) -> "ModuleWrapper":
    """Create and configure the ModuleWrapper instance."""
    from adapters.module_wrapper import ModuleWrapper
    from config.settings import settings

    logger.info("🔧 Creating singleton ModuleWrapper for card_framework...")

    wrapper = ModuleWrapper(
        module_or_name="card_framework",
        qdrant_url=settings.qdrant_url,
        qdrant_api_key=settings.qdrant_api_key,
        collection_name=settings.card_collection,  # mcp_gchat_cards_v7
        auto_initialize=False,  # Don't re-index, collection already populated
        index_nested=True,
        index_private=False,
        max_depth=5,  # Capture full component hierarchy
        skip_standard_library=True,
    )

    # Initialize Qdrant client for text index operations
    wrapper.initialize()

    component_count = len(wrapper.components) if wrapper.components else 0
    logger.info(f"✅ Singleton ModuleWrapper ready: {component_count} components")

    # Create text indices if requested (idempotent - skips if exists)
    if ensure_text_indices and wrapper.client:
        try:
            logger.info("📝 Ensuring text indices exist...")
            indices_created = wrapper.create_text_indices(
                enable_stemming=True,
                enable_stopwords=True,
                enable_phrase_matching=True,
                enable_ascii_folding=True,
                custom_stopwords=GCHAT_STOPWORDS,
            )
            if indices_created > 0:
                logger.info(f"✅ Created {indices_created} new text indices")
            else:
                logger.debug("Text indices already exist")
        except Exception as e:
            logger.warning(f"⚠️ Failed to create text indices: {e}")

    return wrapper


def reset_wrapper():
    """
    Reset the singleton wrapper and cached symbols.

    Use this when you need to force reinitialization, such as:
        - After updating the Qdrant collection schema
        - During testing
        - After configuration changes
    """
    global _wrapper, _symbols

    with _wrapper_lock:
        if _wrapper is not None:
            logger.info("🔄 Resetting singleton ModuleWrapper")
            _wrapper = None

    with _symbols_lock:
        _symbols = None


# =============================================================================
# SYMBOL GENERATION
# =============================================================================


def get_gchat_symbols(force_regenerate: bool = False) -> Dict[str, str]:
    """
    Get the symbol table for gchat card components.

    Returns cached symbols, generating them on first call.
    Symbols are auto-generated by SymbolGenerator based on LETTER_SYMBOLS pools.

    Args:
        force_regenerate: If True, regenerate symbols even if cached

    Returns:
        Dict mapping component names to symbols (e.g., {"Button": "ᵬ"})
    """
    global _symbols

    with _symbols_lock:
        if _symbols is None or force_regenerate:
            _symbols = _generate_gchat_symbols()

    return _symbols


def _generate_gchat_symbols() -> Dict[str, str]:
    """Load symbols from Qdrant collection (Single Source of Truth).

    IMPORTANT: Symbols are loaded FROM Qdrant, not regenerated, to ensure
    consistency between queries and stored data. The symbols were embedded
    into the collection at indexing time.
    """
    wrapper = get_card_framework_wrapper(ensure_text_indices=False)

    symbols = {}

    # LOAD symbols directly from Qdrant's symbol payload field
    # This ensures runtime symbols match what's indexed
    if wrapper.client:
        try:
            from qdrant_client import models

            # Scroll through ALL class components to get their stored symbols
            offset = None
            while True:
                results, offset = wrapper.client.scroll(
                    collection_name=wrapper.collection_name,
                    scroll_filter=models.Filter(
                        must=[
                            models.FieldCondition(
                                key="type",
                                match=models.MatchValue(value="class"),
                            )
                        ]
                    ),
                    limit=500,
                    offset=offset,
                    with_payload=["name", "symbol"],
                )

                for p in results:
                    name = p.payload.get("name")
                    symbol = p.payload.get("symbol")
                    if name and symbol:
                        symbols[name] = symbol

                if offset is None:
                    break

            if symbols:
                logger.info(f"🔣 Loaded {len(symbols)} symbols from Qdrant (SSoT)")
                return symbols

        except Exception as e:
            logger.warning(f"Failed to load symbols from Qdrant: {e}")

    # Fallback: Generate symbols if Qdrant unavailable
    logger.warning("⚠️ Falling back to symbol generation (Qdrant unavailable)")
    from adapters.symbol_generator import SymbolGenerator

    component_names = []
    if wrapper.components:
        component_names = [
            comp.name
            for comp in wrapper.components.values()
            if comp.component_type == "class"
        ]

    generator = SymbolGenerator(module_prefix=None)
    symbols = generator.generate_symbols(component_names)

    logger.info(f"✅ Generated {len(symbols)} symbols (fallback)")
    return symbols


def get_gchat_symbol_table_text() -> str:
    """
    Get a formatted symbol table for LLM instructions.

    Returns:
        Markdown-formatted symbol table
    """
    symbols = get_gchat_symbols()

    # Group by first letter
    from collections import defaultdict

    by_letter = defaultdict(list)
    for comp, sym in symbols.items():
        by_letter[comp[0].upper()].append((sym, comp))

    lines = ["## Google Chat Card Symbols\n"]
    for letter in sorted(by_letter.keys()):
        items = by_letter[letter]
        mappings = [f"{sym}={comp}" for sym, comp in sorted(items, key=lambda x: x[1])]
        lines.append(f"**{letter}:** {', '.join(mappings)}")

    return "\n".join(lines)


def configure_structure_dsl_symbols():
    """
    Configure the structure_dsl module with gchat symbols.

    This updates the global symbol tables in structure_dsl.py to use
    the gchat-specific symbols, enabling both natural language and
    symbol-based card descriptions.
    """
    import gchat.structure_dsl as structure_dsl_module
    from gchat.structure_dsl import (
        ALL_SYMBOLS,
        COMPONENT_TO_SYMBOL,
        SYMBOL_TO_COMPONENT,
    )

    symbols = get_gchat_symbols()

    # Update structure_dsl globals
    SYMBOL_TO_COMPONENT.clear()
    COMPONENT_TO_SYMBOL.clear()

    for comp, sym in symbols.items():
        SYMBOL_TO_COMPONENT[sym] = comp
        COMPONENT_TO_SYMBOL[comp] = sym

    ALL_SYMBOLS.clear()
    ALL_SYMBOLS.update(SYMBOL_TO_COMPONENT.keys())

    # Mark as initialized
    structure_dsl_module._initialized = True

    logger.info(f"🔣 Configured structure_dsl with {len(symbols)} symbols")


# =============================================================================
# CONVENIENCE SEARCH FUNCTIONS
# =============================================================================


def search_components(
    query: str,
    limit: int = 10,
    search_mode: str = "hybrid",
) -> List[Dict]:
    """
    Search for card components using natural language or symbols.

    Args:
        query: Search query (natural language, keywords, or symbols)
        limit: Maximum results
        search_mode: "vector", "text", "relationship", or "hybrid"

    Returns:
        List of matching components
    """
    wrapper = get_card_framework_wrapper()

    if search_mode == "vector":
        return wrapper.search(query, limit=limit)
    elif search_mode == "text":
        return wrapper.search_by_text(query, limit=limit)
    elif search_mode == "relationship":
        return wrapper.search_by_relationship_text(query, limit=limit)
    else:  # hybrid
        return wrapper.hybrid_search(query, limit=limit)


def find_component_by_symbol(symbol: str) -> Optional[Dict]:
    """
    Find a component by its symbol.

    Args:
        symbol: Component symbol (e.g., "ᵬ" for Button)

    Returns:
        Component info dict or None
    """
    symbols = get_gchat_symbols()

    # Reverse lookup
    reverse = {v: k for k, v in symbols.items()}

    component_name = reverse.get(symbol)
    if not component_name:
        return None

    # Search for the component
    wrapper = get_card_framework_wrapper()
    results = wrapper.search_by_text(
        component_name, field="name", limit=1, is_phrase=True
    )

    return results[0] if results else None


# =============================================================================
# AUTO-GENERATED DSL DOCUMENTATION
# =============================================================================


def get_dsl_documentation(
    include_examples: bool = True, include_hierarchy: bool = True
) -> str:
    """
    Auto-generate DSL documentation from the symbol table and relationships.

    This creates a dynamic docstring that reflects the current symbols
    and component hierarchy, suitable for MCP tool descriptions.

    Args:
        include_examples: Whether to include usage examples
        include_hierarchy: Whether to include component hierarchy

    Returns:
        Markdown-formatted documentation string
    """
    symbols = get_gchat_symbols()
    relationships = get_component_relationships_for_dsl()

    lines = ["## Card Structure DSL\n"]
    lines.append("Google Chat cards are built from dataclass components.")
    lines.append("Each component has a unique symbol for compact structure notation.\n")

    # Auto-generated symbol table (grouped by first letter)
    lines.append("### Symbol Mappings (auto-generated)\n")
    from collections import defaultdict

    by_letter = defaultdict(list)
    for comp, sym in sorted(symbols.items()):
        by_letter[comp[0].upper()].append(f"{sym}={comp}")

    for letter in sorted(by_letter.keys()):
        mappings = by_letter[letter]
        if len(mappings) <= 4:
            lines.append(f"**{letter}:** {', '.join(mappings)}")
        else:
            # Show first 4 for brevity
            lines.append(f"**{letter}:** {', '.join(mappings[:4])}...")

    # Add hierarchy section (structural parent-child from dataclass fields)
    if include_hierarchy and relationships:
        lines.append("\n### Component Hierarchy (from dataclass fields)\n")
        lines.append("Shows which components can contain which (parent → children):\n")

        # Show hierarchy with symbols
        for parent, children in sorted(relationships.items()):
            parent_sym = symbols.get(parent, parent)
            child_syms = []
            for child in children[:5]:  # Limit for readability
                child_sym = symbols.get(child, child)
                child_syms.append(f"{child_sym}={child}")

            if len(children) > 5:
                child_syms.append(f"+{len(children) - 5} more")

            lines.append(f"- {parent_sym} **{parent}** → [{', '.join(child_syms)}]")

    # Build syntax examples using actual symbols
    section_sym = symbols.get("Section", "§")
    button_sym = symbols.get("Button", "ᵬ")
    dtext_sym = symbols.get("DecoratedText", "đ")
    grid_sym = symbols.get("Grid", "ℊ")
    gitem_sym = symbols.get("GridItem", "ǵ")

    lines.append("\n### DSL Syntax\n")
    lines.append(
        f"- `{section_sym}[{dtext_sym}, {button_sym}]` → Section with DecoratedText and Button"
    )
    lines.append(f"- `{button_sym}×2` → 2 Buttons (multiplier notation)")
    lines.append(f"- `{grid_sym}[{gitem_sym}×4]` → Grid with 4 GridItems")
    lines.append("- Nesting: `parent[child1, child2[grandchild]]`")

    if include_examples:
        lines.append("\n### Examples\n")
        lines.append("```python")
        lines.append("# Simple card with text and button")
        lines.append(f'structure_dsl="{section_sym}[{dtext_sym}, {button_sym}]"')
        lines.append('card_params={"text": "Hello", "button_text": "Click"}')
        lines.append("")
        lines.append("# Grid with 6 items")
        lines.append(f'structure_dsl="{section_sym}[{grid_sym}[{gitem_sym}×6]]"')
        lines.append("```")

    return "\n".join(lines)


def get_dsl_field_description() -> str:
    """
    Get a compact field description for the structure_dsl parameter.

    Returns a single-line description suitable for Field(description=...).
    """
    symbols = get_gchat_symbols()

    # Get key symbols for the description
    key_mappings = []
    key_components = [
        "Section",
        "DecoratedText",
        "Button",
        "ButtonList",
        "Grid",
        "GridItem",
        "Image",
        "Icon",
        "TextInput",
        "OnClick",
    ]

    for comp in key_components:
        if comp in symbols:
            key_mappings.append(f"{symbols[comp]}={comp}")

    return (
        f"DSL structure using symbols. "
        f"Example: '§[đ, Ƀ[ᵬ×2]]' = Section with DecoratedText and 2 Buttons. "
        f"Symbols: {', '.join(key_mappings[:8])}..."
    )


def get_hierarchy_tree_text(
    root_components: Optional[List[str]] = None,
    max_depth: int = 3,
    include_symbols: bool = True,
) -> str:
    """
    Generate a deterministic text-based tree representation of component hierarchy.

    Delegates to ModuleWrapper.get_hierarchy_tree_text() for the actual implementation.

    Args:
        root_components: Optional list of root component names to start from.
                        If None, uses common card containers (Card, Section, etc.)
        max_depth: Maximum depth to traverse (default 3)
        include_symbols: Whether to include symbols in the output

    Returns:
        ASCII tree representation of the hierarchy
    """
    wrapper = get_card_framework_wrapper()

    # Default root components for gchat cards
    if root_components is None:
        root_components = ["Card", "Section", "DecoratedText", "Grid", "ButtonList"]

    return wrapper.get_hierarchy_tree_text(
        root_components=root_components,
        max_depth=max_depth,
        include_symbols=include_symbols,
    )


def get_full_hierarchy_documentation(include_tree: bool = True) -> str:
    """
    Generate complete hierarchy documentation with symbols and tree visualization.

    Delegates to ModuleWrapper.get_full_module_documentation().

    Args:
        include_tree: Whether to include the ASCII tree visualization

    Returns:
        Complete markdown documentation string
    """
    wrapper = get_card_framework_wrapper()

    return wrapper.get_full_module_documentation(
        include_tree=include_tree,
        include_symbols=True,
        include_examples=True,
    )


def get_component_relationships_for_dsl() -> Dict[str, List[str]]:
    """
    Get component relationships formatted for DSL validation.

    Returns a dict mapping parent components to their valid children,
    using component names (not symbols).

    Dynamically loads relationships from Qdrant and expands base class
    references (e.g., Widget) to actual widget subclasses via Python introspection.
    """
    wrapper = get_card_framework_wrapper()
    relationships = {}

    # Step 1: Get Widget subclasses via Python introspection (most accurate)
    widget_subclasses = _get_widget_subclasses()
    logger.info(f"📦 Found {len(widget_subclasses)} Widget subclasses via introspection")

    if not wrapper.client:
        logger.warning("No Qdrant client - using introspection-based relationships")
        return _build_relationships_from_introspection(widget_subclasses)

    try:
        from qdrant_client import models

        # Step 2: Get all classes with their relationships from Qdrant
        results, _ = wrapper.client.scroll(
            collection_name=wrapper.collection_name,
            scroll_filter=models.Filter(
                must=[
                    models.FieldCondition(
                        key="type",
                        match=models.MatchValue(value="class"),
                    )
                ]
            ),
            limit=300,
            with_payload=["name", "relationships"],
        )

        # Step 3: Build relationships, expanding Widget -> all widget subclasses
        for point in results:
            payload = point.payload
            name = payload.get("name")
            rels = payload.get("relationships", {})
            children = rels.get("child_classes", [])

            if name and children:
                # Expand "Widget" to all actual widget subclasses
                expanded_children = []
                for child in children:
                    if child == "Widget":
                        # Replace Widget with all subclasses from introspection
                        expanded_children.extend(widget_subclasses)
                    else:
                        expanded_children.append(child)

                relationships[name] = expanded_children

        logger.info(
            f"📋 Loaded {len(relationships)} component relationships for DSL (Qdrant + introspection)"
        )

        # Log Section's children for debugging
        section_children = relationships.get("Section", [])
        logger.info(f"📋 Section can contain: {len(section_children)} widget types: {section_children}")

        return relationships

    except Exception as e:
        logger.warning(f"Failed to load relationships from Qdrant: {e}")
        return _build_relationships_from_introspection(widget_subclasses)


def _get_widget_subclasses() -> List[str]:
    """Get all Widget subclasses via Python introspection."""
    try:
        from card_framework.v2.widget import Widget

        def get_all_subclasses(cls):
            all_subs = []
            for sub in cls.__subclasses__():
                all_subs.append(sub)
                all_subs.extend(get_all_subclasses(sub))
            return all_subs

        subclasses = get_all_subclasses(Widget)
        return sorted([c.__name__ for c in subclasses])
    except ImportError:
        logger.warning("Could not import Widget class for introspection")
        return [
            "Action", "Button", "ButtonList", "Chip", "ChipList",
            "Columns", "DateTimePicker", "DecoratedText", "Divider",
            "Grid", "Image", "SelectionInput", "SwitchControl",
            "TextInput", "TextParagraph", "UpdatedWidget",
        ]


def _build_relationships_from_introspection(widget_subclasses: List[str]) -> Dict[str, List[str]]:
    """Build relationships using Python introspection when Qdrant is unavailable."""
    return {
        "Card": ["CardHeader", "Section", "CardFixedFooter"],
        "Section": ["CollapseControl"] + widget_subclasses,
        "Columns": ["Column"],
        "Column": widget_subclasses,  # Column can contain any widget
        "ButtonList": ["Button"],
        "ChipList": ["Chip"],
        "Grid": ["GridItem"],
        "DecoratedText": ["Icon", "Button", "OnClick", "SwitchControl"],
        "Button": ["Icon", "OnClick", "Color"],
        "Chip": ["Icon", "OnClick"],
        "OnClick": ["OpenLink", "Action", "OverflowMenu"],
        "OverflowMenu": ["OverflowMenuItem"],
        "GridItem": ["ImageComponent"],
        "TextInput": ["Suggestions", "Validation"],
        "SelectionInput": ["SelectionItem", "PlatformDataSource"],
    }




# =============================================================================
# DSL PARSING HELPERS (using refactored module_wrapper.dsl_parser)
# =============================================================================


def get_dsl_parser():
    """
    Get a configured DSL parser for gchat card components.

    Uses the new refactored DSLParser from adapters.module_wrapper.dsl_parser
    which provides:
    - Robust tokenization and parsing
    - Qdrant query generation
    - DSL extraction from descriptions
    - Validation against component hierarchy

    Returns:
        DSLParser instance configured with gchat symbols and relationships
    """
    from adapters.module_wrapper.dsl_parser import DSLParser

    symbols = get_gchat_symbols()
    relationships = get_component_relationships_for_dsl()

    return DSLParser(
        symbol_mapping=symbols,
        reverse_mapping={v: k for k, v in symbols.items()},
        relationships=relationships,
    )


def parse_dsl(dsl_string: str):
    """
    Parse a DSL string using the refactored DSL parser.

    Args:
        dsl_string: DSL notation like "§[đ×3, Ƀ[ᵬ×2]]"

    Returns:
        DSLParseResult with:
        - is_valid: Whether structure is valid
        - component_counts: Dict of component name → count
        - component_paths: Flat list of component names
        - root_nodes: Parsed tree structure
        - issues: List of validation issues

    Example:
        result = parse_dsl("§[đ×3, Ƀ[ᵬ×2]]")
        if result.is_valid:
            print(result.component_counts)
            # {'Section': 1, 'DecoratedText': 3, 'ButtonList': 1, 'Button': 2}
    """
    parser = get_dsl_parser()
    return parser.parse(dsl_string)


def extract_dsl_from_description(description: str) -> Optional[str]:
    """
    Extract DSL notation from a description string.

    Uses the refactored DSL parser for robust extraction.

    Args:
        description: Text that may contain DSL notation
                    e.g., "§[đ×3, Ƀ[ᵬ×2]] Server Status Dashboard"

    Returns:
        DSL string if found, None otherwise
        e.g., "§[δ×3, Ƀ[ᵬ×2]]"

    Example:
        dsl = extract_dsl_from_description("§[đ×3] Server Status")
        # Returns: "§[đ×3]"
    """
    if not description:
        return None

    parser = get_dsl_parser()
    return parser.extract_dsl_from_text(description)


def dsl_to_qdrant_queries(
    dsl_string: str, collection_name: Optional[str] = None
) -> List[Dict]:
    """
    Generate Qdrant queries from a DSL string.

    Uses the refactored DSL parser to generate optimized queries
    for the relationships, components, and inputs vectors.

    Args:
        dsl_string: DSL notation
        collection_name: Qdrant collection name (uses settings.card_collection if None)

    Returns:
        List of query dicts with:
        - vector_name: Which vector to search ('relationships', 'components', 'inputs')
        - query_text: The query text
        - filters: Any filters to apply

    Example:
        queries = dsl_to_qdrant_queries("§[đ×3, Ƀ[ᵬ×2]]")
        for q in queries:
            print(f"{q['vector_name']}: {q['query_text']}")
    """
    from config.settings import settings

    parser = get_dsl_parser()
    result = parser.parse(dsl_string)

    if not result.is_valid:
        logger.warning(f"Invalid DSL structure: {result.issues}")
        return []

    collection = collection_name or settings.card_collection
    queries = parser.to_qdrant_queries(result, collection)

    return [q.to_dict() for q in queries]


def validate_dsl(dsl_string: str) -> tuple:
    """
    Validate a DSL structure against the component hierarchy.

    Args:
        dsl_string: DSL notation to validate

    Returns:
        Tuple of (is_valid, issues_list)

    Example:
        is_valid, issues = validate_dsl("§[đ, ᵬ]")
        if not is_valid:
            print(f"Issues: {issues}")
    """
    parser = get_dsl_parser()
    result = parser.parse(dsl_string)
    return result.is_valid, result.issues


def expand_dsl(dsl_string: str) -> str:
    """
    Expand DSL symbols to full component names.

    Args:
        dsl_string: Compact DSL like "§[đ, ᵬ×2]"

    Returns:
        Expanded notation like "Section[DecoratedText, Button×2]"
    """
    parser = get_dsl_parser()
    return parser.expand_dsl(dsl_string)


def compact_dsl(component_notation: str) -> str:
    """
    Compact component names to DSL symbols.

    Args:
        component_notation: Full names like "Section[DecoratedText, Button×2]"

    Returns:
        Compact DSL like "§[đ, ᵬ×2]"
    """
    parser = get_dsl_parser()
    return parser.compact_to_dsl(component_notation)


# =============================================================================
# CONTENT DSL HELPERS
# =============================================================================


def parse_content_dsl(content_text: str):
    """
    Parse Content DSL text into structured blocks.

    Content DSL allows expressing component content with styling modifiers:

        δ 'Status: Online' success bold
        ᵬ Click Here https://example.com
          Continue on next line

    Features:
        - Symbol prefix indicates component type (δ, ᵬ, §, etc.)
        - Quoted or unquoted text content
        - Style modifiers (yellow, bold, success, error, italic, etc.)
        - URL detection for button/link actions
        - Continuation lines (indented) for multi-line content

    Args:
        content_text: Multi-line Content DSL text

    Returns:
        ContentDSLResult with:
        - is_valid: Whether parsing succeeded
        - blocks: List of ContentBlock objects
        - issues: Any parsing issues

    Example:
        result = parse_content_dsl('''
            § Dashboard bold
            δ 'Server Status' success
            ᵬ Refresh https://api.example.com/refresh
        ''')

        for block in result.blocks:
            print(f"{block.primary.component_name}: {block.to_jinja()}")
    """
    parser = get_dsl_parser()
    return parser.parse_content_dsl(content_text)


def content_to_jinja(content_text: str) -> List[str]:
    """
    Convert Content DSL directly to Jinja expressions.

    A convenience function that parses Content DSL and returns
    the Jinja template expressions for each block.

    Args:
        content_text: Content DSL text

    Returns:
        List of Jinja expressions like ["{{ 'text' | success_text | bold }}"]

    Example:
        expressions = content_to_jinja("δ 'Hello' success bold")
        # Returns: ["{{ 'Hello' | success_text | bold }}"]
    """
    parser = get_dsl_parser()
    result = parser.parse_content_dsl(content_text)
    return result.to_jinja_list()


def content_to_params(content_text: str) -> List[Dict]:
    """
    Convert Content DSL to component parameter dictionaries.

    Useful for directly building card components without going
    through the Jinja template system.

    Args:
        content_text: Content DSL text

    Returns:
        List of dicts with component parameters:
        - component: Component name (e.g., "DecoratedText")
        - text: The text content
        - jinja_text: Jinja-formatted text with filters
        - url: URL if detected (for buttons)
        - styles: List of style modifier names

    Example:
        params = content_to_params('''
            δ 'Server Online' success
            ᵬ Refresh https://api.example.com
        ''')
        # Returns:
        # [
        #   {"component": "DecoratedText", "text": "Server Online", "styles": ["success"], ...},
        #   {"component": "Button", "text": "Refresh", "url": "https://api.example.com", ...}
        # ]
    """
    parser = get_dsl_parser()
    result = parser.parse_content_dsl(content_text)
    return parser.content_to_component_params(result)


def get_available_style_modifiers() -> Dict[str, str]:
    """
    Get available style modifiers for Content DSL.

    Returns:
        Dict mapping modifier name to Jinja filter name

    Example:
        modifiers = get_available_style_modifiers()
        # {'bold': 'bold', 'success': 'success_text', 'yellow': 'color', ...}
    """
    from adapters.module_wrapper.dsl_parser import STYLE_MODIFIERS

    return {name: filter_info[0] for name, filter_info in STYLE_MODIFIERS.items()}
