"""
Agent patterns for CAI.

This module exports both swarm patterns (for handoff-based collaboration)
and parallel patterns (for simultaneous execution).
"""

import importlib
import pkgutil
from typing import Dict, Any, Optional, List, Union

__all__ = [
    "Pattern",
    "PatternType",
    "get_pattern",
    "get_patterns_by_type",
    "get_parallel_patterns",
    "get_swarm_patterns",
    "create_pattern",
    "parallel_pattern",
    "swarm_pattern",
    "hierarchical_pattern",
    "sequential_pattern",
    "conditional_pattern",
    "PATTERNS",
    "is_swarm_pattern",
]

# Pattern registry for easy access
PATTERNS = {}


def discover_patterns() -> Dict[str, "Pattern"]:
    """Discover all patterns in the patterns directory.

    Automatically identifies and loads both swarm and parallel patterns,
    wrapping them in appropriate Pattern classes.

    Returns:
        Dictionary mapping pattern names to Pattern instances.
    """
    # Import Pattern here to avoid circular imports
    from .pattern import Pattern, PatternType

    patterns = {}

    # Get the current package
    package = __name__
    prefix = package + "."

    # Iterate through all modules in this package
    for importer, modname, ispkg in pkgutil.iter_modules(__path__, prefix):
        if ispkg:
            continue

        # Skip special modules
        module_name = modname.replace(prefix, "")
        if module_name in ["__init__", "pattern", "utils"]:
            continue

        try:
            module = importlib.import_module(modname)

            # Look for Pattern class instances
            for attr_name in dir(module):
                # Skip private attributes
                if attr_name.startswith("_"):
                    continue

                attr = getattr(module, attr_name)

                # Check if it's a Pattern instance
                if isinstance(attr, Pattern):
                    # Use the pattern's name or the attribute name
                    pattern_name = attr.name or attr_name
                    patterns[pattern_name] = attr

                    # Add to __all__ if not already there
                    if attr_name not in __all__:
                        __all__.append(attr_name)

                # Check for legacy swarm patterns
                elif hasattr(attr, "pattern") and getattr(attr, "pattern") == "swarm":
                    # Always use the attribute name as the key to avoid duplicates
                    # The pattern's display name is stored in pattern.name
                    pattern_key = attr_name
                    pattern_display_name = getattr(attr, "name", attr_name)

                    # Create swarm pattern wrapper
                    pattern = Pattern(
                        name=pattern_display_name,
                        type=PatternType.SWARM,
                        description=getattr(attr, "description", ""),
                        entry_agent=attr,
                    )
                    pattern.agents = [attr]  # Add to agents list
                    patterns[pattern_key] = pattern

                    if attr_name not in __all__:
                        __all__.append(attr_name)

                # Check if it's a Pattern class (not instance)
                elif isinstance(attr, type) and issubclass(attr, Pattern) and attr is not Pattern:
                    # Create an instance of the pattern class
                    try:
                        pattern_instance = attr()
                        pattern_name = pattern_instance.name
                        patterns[pattern_name] = pattern_instance

                        # Add class name to __all__
                        if attr_name not in __all__:
                            __all__.append(attr_name)
                    except Exception:
                        # Skip if we can't instantiate
                        continue

                # Check for dict-based pattern definitions
                elif (
                    isinstance(attr, dict)
                    and "name" in attr
                    and "type" in attr
                    and attr_name.endswith("_pattern")
                ):
                    # Convert dict to Pattern instance
                    try:
                        pattern_config = attr.copy()
                        pattern_name = pattern_config.pop("name")
                        pattern_type = pattern_config.pop("type")

                        pattern = Pattern(name=pattern_name, type=pattern_type, **pattern_config)
                        patterns[pattern_name] = pattern

                        if attr_name not in __all__:
                            __all__.append(attr_name)
                    except Exception:
                        # Skip if we can't create pattern
                        continue

        except Exception as e:
            # Skip modules that cannot be imported
            # Silently ignore circular import errors for pattern files
            if "circular import" not in str(e):
                import sys

                print(f"Error importing {module_name}: {e}", file=sys.stderr)
            continue

    return patterns


# Defer pattern discovery until after all imports are done
def _initialize_patterns():
    """Initialize patterns after all imports are complete."""
    global PATTERNS
    if not PATTERNS:  # Only initialize once
        PATTERNS.update(discover_patterns())


# Import Pattern and related items after defining functions to avoid circular imports
from .pattern import (
    Pattern,
    PatternType,
    parallel_pattern,
    swarm_pattern,
    hierarchical_pattern,
    sequential_pattern,
    conditional_pattern,
)

# Initialize patterns after imports
_initialize_patterns()


def get_pattern(pattern_name: str) -> Optional["Pattern"]:
    """Get a pattern by name.

    Args:
        pattern_name: Name of the pattern to retrieve

    Returns:
        Pattern instance if found, None otherwise
    """
    return PATTERNS.get(pattern_name)


def get_patterns_by_type(pattern_type: Union[str, "PatternType"]) -> Dict[str, "Pattern"]:
    """Get all available patterns of a specific type.

    Args:
        pattern_type: Type of patterns to retrieve (e.g., "swarm", "parallel")

    Returns:
        Dictionary mapping pattern names to Pattern instances
    """
    from .pattern import PatternType

    if isinstance(pattern_type, str):
        try:
            pattern_type = PatternType(pattern_type)
        except ValueError:
            return {}  # Invalid type

    result = {}
    for name, pattern in PATTERNS.items():
        if pattern.type == pattern_type:
            result[name] = pattern

    return result


def get_parallel_patterns() -> Dict[str, "Pattern"]:
    """Get all available parallel patterns.

    Returns:
        Dictionary of pattern name to Pattern instances of type PARALLEL
    """
    from .pattern import PatternType

    return get_patterns_by_type(PatternType.PARALLEL)


def get_swarm_patterns() -> Dict[str, "Pattern"]:
    """Get all available swarm patterns.

    Returns:
        Dictionary of pattern name to Pattern instances of type SWARM
    """
    from .pattern import PatternType

    return get_patterns_by_type(PatternType.SWARM)


def create_pattern(
    name: str, pattern_type: Union[str, "PatternType"], description: str = "", **kwargs
) -> "Pattern":
    """Create a new pattern programmatically.

    Args:
        name: Pattern name
        pattern_type: Type of pattern (parallel, swarm, etc.)
        description: Pattern description
        **kwargs: Additional pattern-specific arguments

    Returns:
        New Pattern instance
    """
    from .pattern import Pattern

    return Pattern(name=name, type=pattern_type, description=description, **kwargs)


# Import utility functions
from .utils import is_swarm_pattern

# Import core pattern classes
from .pattern import Pattern, PatternType

# Import factory functions for creating patterns
from .pattern import (
    parallel_pattern,
    swarm_pattern,
    hierarchical_pattern,
    sequential_pattern,
    conditional_pattern,
)
