"""
Factory for creating and managing explode-implode variants.

This module provides a centralized factory for instantiating variants,
auto-detecting appropriate variants, and managing variant registration.
"""

from pathlib import Path
from typing import Dict, List, Optional, Type, Any

from .base_variant import BaseVariant
from .enums import ExplodeVariant, DetectionConfidence
from .flat_variant import FlatVariant
from .hierarchical_variant import HierarchicalVariant
from .semantic_variant import SemanticVariant
from .variant_detector import VariantDetector, DetectionResult

# Packaging variants are imported lazily to avoid circular imports
_MDZ_AVAILABLE = None  # Lazy evaluation
_MDZ_IMPORT_ERROR = None
_MdzVariant = None  # Cached import


def _check_mdz_availability():
    """Check if MDZ variant is available, with lazy import."""
    global _MDZ_AVAILABLE, _MDZ_IMPORT_ERROR, _MdzVariant

    if _MDZ_AVAILABLE is not None:
        return _MDZ_AVAILABLE

    try:
        from ..packaging.mdz_variant import MdzVariant
        _MdzVariant = MdzVariant
        _MDZ_AVAILABLE = True
        return True
    except ImportError as e:
        _MDZ_AVAILABLE = False
        _MDZ_IMPORT_ERROR = str(e)
        return False
    except Exception as e:
        _MDZ_AVAILABLE = False
        _MDZ_IMPORT_ERROR = f"Unexpected error: {e}"
        return False


class VariantFactory:
    """
    Factory for creating and managing explode-implode variants.

    Provides a centralized interface for:
    - Creating variant instances
    - Auto-detecting variants from directory structures
    - Registering new variant types
    - Getting variant information and capabilities
    """

    def __init__(self):
        """Initialize the variant factory."""
        self._variants: Dict[ExplodeVariant, Type[BaseVariant]] = {}
        self._detector = VariantDetector()
        self._register_builtin_variants()

    def _register_builtin_variants(self) -> None:
        """Register all built-in variants."""
        self.register_variant(ExplodeVariant.FLAT, FlatVariant)
        self.register_variant(ExplodeVariant.HIERARCHICAL, HierarchicalVariant)
        self.register_variant(ExplodeVariant.SEMANTIC, SemanticVariant)

        # Register packaging variants if available (lazy loading)
        if _check_mdz_availability():
            self.register_variant(ExplodeVariant.MDZ, _MdzVariant)

    def register_variant(self, variant_type: ExplodeVariant, variant_class: Type[BaseVariant]) -> None:
        """
        Register a variant class with the factory.

        Args:
            variant_type: The variant enum type
            variant_class: The variant implementation class

        Raises:
            ValueError: If variant_class is not a subclass of BaseVariant
        """
        if not issubclass(variant_class, BaseVariant):
            raise ValueError(f"Variant class {variant_class} must inherit from BaseVariant")

        self._variants[variant_type] = variant_class

    def create_variant(self, variant_type: ExplodeVariant) -> BaseVariant:
        """
        Create an instance of the specified variant.

        Args:
            variant_type: The type of variant to create

        Returns:
            Instance of the specified variant

        Raises:
            ValueError: If variant_type is not registered
        """
        if variant_type not in self._variants:
            raise ValueError(f"Unknown variant type: {variant_type}")

        variant_class = self._variants[variant_type]
        return variant_class()

    def detect_variant(self, directory: Path) -> DetectionResult:
        """
        Auto-detect the variant used for a directory structure.

        Args:
            directory: Directory to analyze

        Returns:
            Detection result with variant, confidence, and evidence
        """
        return self._detector.detect_variant(directory)

    def create_variant_for_directory(self, directory: Path) -> BaseVariant:
        """
        Create the appropriate variant instance for a directory structure.

        Args:
            directory: Directory to analyze

        Returns:
            Variant instance best suited for the directory

        Raises:
            ValueError: If no suitable variant can be determined
        """
        detection_result = self.detect_variant(directory)

        if detection_result.variant is None:
            # Fallback to flat variant
            return self.create_variant(ExplodeVariant.FLAT)

        return self.create_variant(detection_result.variant)

    def get_variant_info(self, variant_type: ExplodeVariant) -> Dict[str, Any]:
        """
        Get information about a variant type.

        Args:
            variant_type: The variant type to get info for

        Returns:
            Dictionary with variant information

        Raises:
            ValueError: If variant_type is not registered
        """
        if variant_type not in self._variants:
            raise ValueError(f"Unknown variant type: {variant_type}")

        variant_instance = self.create_variant(variant_type)
        detection_patterns = variant_instance.get_detection_patterns()

        return {
            'type': variant_type,
            'name': variant_instance.name,
            'description': variant_instance.description,
            'detection_patterns': detection_patterns,
            'class_name': self._variants[variant_type].__name__
        }

    def list_available_variants(self) -> List[Dict[str, Any]]:
        """
        Get information about all registered variants.

        Returns:
            List of variant information dictionaries
        """
        variants_info = []
        for variant_type in self._variants:
            try:
                info = self.get_variant_info(variant_type)
                variants_info.append(info)
            except Exception as e:
                # Skip variants that fail to load
                continue

        # Sort by variant order (flat, hierarchical, semantic)
        order_map = {
            ExplodeVariant.FLAT: 1,
            ExplodeVariant.HIERARCHICAL: 2,
            ExplodeVariant.SEMANTIC: 3
        }

        variants_info.sort(key=lambda x: order_map.get(x['type'], 999))
        return variants_info

    def get_best_variant_for_content(self, content: str) -> ExplodeVariant:
        """
        Analyze content and suggest the best variant for explosion.

        Args:
            content: Markdown content to analyze

        Returns:
            Recommended variant type
        """
        # Simple content analysis to suggest variants
        lines = content.split('\n')
        heading_count = sum(1 for line in lines if line.strip().startswith('#'))
        h1_count = sum(1 for line in lines if line.strip().startswith('# '))
        h2_count = sum(1 for line in lines if line.strip().startswith('## '))

        # Check for numbered headings (hierarchical indicator)
        numbered_headings = sum(1 for line in lines
                              if re.match(r'^#+\s*\d+[\.\)]\s+', line.strip()))

        # Check for semantic keywords
        content_lower = content.lower()
        semantic_keywords = [
            'chapter', 'section', 'introduction', 'conclusion',
            'appendix', 'reference', 'tutorial', 'guide'
        ]
        semantic_score = sum(1 for keyword in semantic_keywords
                           if keyword in content_lower)

        # Decision logic
        if numbered_headings > heading_count * 0.3:
            return ExplodeVariant.HIERARCHICAL
        elif semantic_score > 3 and h1_count > 2:
            return ExplodeVariant.SEMANTIC
        else:
            return ExplodeVariant.FLAT

    def validate_variant_for_directory(self, variant_type: ExplodeVariant, directory: Path) -> bool:
        """
        Validate if a variant can handle a specific directory structure.

        Args:
            variant_type: The variant type to validate
            directory: Directory to check

        Returns:
            True if the variant can handle the directory
        """
        try:
            variant_instance = self.create_variant(variant_type)
            return variant_instance.can_handle_directory(directory)
        except Exception:
            return False

    def get_compatible_variants(self, directory: Path) -> List[ExplodeVariant]:
        """
        Get all variants that can handle a directory structure.

        Args:
            directory: Directory to check

        Returns:
            List of compatible variant types
        """
        compatible = []
        for variant_type in self._variants:
            if self.validate_variant_for_directory(variant_type, directory):
                compatible.append(variant_type)

        return compatible

    def is_exploded_directory(self, directory: Path) -> bool:
        """
        Check if a directory appears to be an exploded markdown structure.

        Args:
            directory: Directory to check

        Returns:
            True if directory appears to be exploded markdown content
        """
        return self._detector.is_exploded_directory(directory)

    def get_variant_statistics(self) -> Dict[str, Any]:
        """
        Get statistics about registered variants.

        Returns:
            Dictionary with variant statistics
        """
        return {
            'total_variants': len(self._variants),
            'variant_types': list(self._variants.keys()),
            'builtin_variants': [
                ExplodeVariant.FLAT,
                ExplodeVariant.HIERARCHICAL,
                ExplodeVariant.SEMANTIC
            ],
            'custom_variants': [
                vt for vt in self._variants.keys()
                if vt not in [ExplodeVariant.FLAT, ExplodeVariant.HIERARCHICAL, ExplodeVariant.SEMANTIC]
            ]
        }


# Global factory instance
_factory_instance: Optional[VariantFactory] = None


def get_variant_factory() -> VariantFactory:
    """
    Get the global variant factory instance.

    Returns:
        The global VariantFactory instance
    """
    global _factory_instance
    if _factory_instance is None:
        _factory_instance = VariantFactory()
    return _factory_instance


def create_variant(variant_type: ExplodeVariant) -> BaseVariant:
    """
    Convenience function to create a variant instance.

    Args:
        variant_type: The type of variant to create

    Returns:
        Instance of the specified variant
    """
    return get_variant_factory().create_variant(variant_type)


def detect_variant(directory: Path) -> DetectionResult:
    """
    Convenience function to detect variant from directory.

    Args:
        directory: Directory to analyze

    Returns:
        Detection result
    """
    return get_variant_factory().detect_variant(directory)


def auto_create_variant(directory: Path) -> BaseVariant:
    """
    Convenience function to auto-create variant for directory.

    Args:
        directory: Directory to analyze

    Returns:
        Appropriate variant instance
    """
    return get_variant_factory().create_variant_for_directory(directory)


# Import required for content analysis
import re