"""
Module Auto-Discovery.

Scans installed Django apps to find modules implementing ModuleInterface
and automatically registers them with the Module Registry.
"""
import importlib
import logging
from typing import Dict, List, Optional, Type

from django.apps import apps

from .interface import ModuleInterface

logger = logging.getLogger(__name__)


class ModuleDiscovery:
    """
    Auto-discovers and validates modules implementing ModuleInterface.
    
    Scans Django apps for a 'module.py' file containing a class
    that extends ModuleInterface.
    
    Convention:
    - Each module app should have a 'module.py' file
    - The file should contain a class extending ModuleInterface
    - The class name should end with 'Module' (e.g., PLMModule)
    
    Example:
        discovery = ModuleDiscovery()
        modules = discovery.discover_all()
        
        for module in modules:
            info = module.get_module_info()
            print(f"Found: {info['name']} v{info['version']}")
    """

    # Cache of discovered modules
    _discovered: Dict[str, ModuleInterface] = {}
    _is_scanned: bool = False

    @classmethod
    def discover_all(cls) -> List[ModuleInterface]:
        """
        Discover all modules in the project.
        
        Scans all installed Django apps for 'module.py' files
        containing ModuleInterface implementations.
        
        Returns:
            List of ModuleInterface instances
        """
        if cls._is_scanned:
            return list(cls._discovered.values())

        cls._discovered = {}
        
        for app_config in apps.get_app_configs():
            module_instance = cls._try_discover_module(app_config)
            if module_instance:
                info = module_instance.get_module_info()
                module_name = info.get('name', app_config.label)
                cls._discovered[module_name] = module_instance
                logger.info(
                    f"Discovered module: {module_name} "
                    f"v{info.get('version', 'unknown')}"
                )

        cls._is_scanned = True
        logger.info(f"Module discovery complete: {len(cls._discovered)} modules found")
        return list(cls._discovered.values())

    @classmethod
    def _try_discover_module(cls, app_config) -> Optional[ModuleInterface]:
        """Try to discover a ModuleInterface in an app."""
        module_path = f"{app_config.name}.module"
        
        try:
            module = importlib.import_module(module_path)
        except ImportError:
            return None

        # Find classes that implement ModuleInterface
        for attr_name in dir(module):
            attr = getattr(module, attr_name)
            if (
                isinstance(attr, type)
                and issubclass(attr, ModuleInterface)
                and attr is not ModuleInterface
            ):
                try:
                    instance = attr()
                    return instance
                except Exception as e:
                    logger.error(
                        f"Failed to instantiate {attr_name} "
                        f"from {module_path}: {e}"
                    )

        return None

    @classmethod
    def get_module(cls, name: str) -> Optional[ModuleInterface]:
        """Get a discovered module by name."""
        if not cls._is_scanned:
            cls.discover_all()
        return cls._discovered.get(name)

    @classmethod
    def validate_module(
        cls,
        module: ModuleInterface,
    ) -> List[str]:
        """
        Validate that a module correctly implements the interface.
        
        Checks:
        - get_module_info returns required fields
        - get_searchable_entities returns valid definitions
        - subscribe_to_events returns a list of strings
        - health_check returns HealthStatus
        - get_api_endpoints returns valid definitions
        
        Returns:
            List of validation errors (empty if valid)
        """
        errors = []

        # Validate module info
        try:
            info = module.get_module_info()
            if not isinstance(info, dict):
                errors.append("get_module_info() must return a dict")
            else:
                for required in ['name', 'display_name', 'version']:
                    if required not in info:
                        errors.append(
                            f"get_module_info() missing required field: {required}"
                        )
        except Exception as e:
            errors.append(f"get_module_info() raised: {e}")

        # Validate searchable entities
        try:
            entities = module.get_searchable_entities()
            if not isinstance(entities, list):
                errors.append("get_searchable_entities() must return a list")
        except Exception as e:
            errors.append(f"get_searchable_entities() raised: {e}")

        # Validate event subscriptions
        try:
            events = module.subscribe_to_events()
            if not isinstance(events, list):
                errors.append("subscribe_to_events() must return a list")
            elif not all(isinstance(e, str) for e in events):
                errors.append("subscribe_to_events() must return List[str]")
        except Exception as e:
            errors.append(f"subscribe_to_events() raised: {e}")

        # Validate health check
        try:
            health = module.health_check()
            from .interface import HealthStatus
            if not isinstance(health, HealthStatus):
                errors.append("health_check() must return HealthStatus")
        except Exception as e:
            errors.append(f"health_check() raised: {e}")

        # Validate API endpoints
        try:
            endpoints = module.get_api_endpoints()
            if not isinstance(endpoints, list):
                errors.append("get_api_endpoints() must return a list")
        except Exception as e:
            errors.append(f"get_api_endpoints() raised: {e}")

        return errors

    @classmethod
    def register_all_with_registry(cls) -> Dict[str, bool]:
        """
        Discover all modules and register them with the Module Registry service.
        
        Returns:
            Dict mapping module name to registration success
        """
        from ..services import ModuleService

        results = {}
        modules = cls.discover_all()

        for module_instance in modules:
            info = module_instance.get_module_info()
            name = info.get('name', '')

            try:
                validation_errors = cls.validate_module(module_instance)
                if validation_errors:
                    logger.warning(
                        f"Module {name} has validation issues: {validation_errors}"
                    )

                manifest = {
                    **info,
                    'provides': {
                        'events': [],
                        'permissions': module_instance.get_provided_permissions(),
                    },
                    'consumes': {
                        'events': module_instance.subscribe_to_events(),
                    },
                    'settings': module_instance.get_default_settings(),
                    'api': {
                        'prefix': f'/api/v1/{name}',
                        'endpoints': [
                            {
                                'path': ep.path,
                                'method': ep.method,
                                'name': ep.name,
                                'description': ep.description,
                            }
                            for ep in module_instance.get_api_endpoints()
                        ],
                    },
                }

                ModuleService.register_module(manifest)
                results[name] = True
                logger.info(f"Registered module: {name}")

            except Exception as e:
                results[name] = False
                logger.error(f"Failed to register module {name}: {e}")

        return results

    @classmethod
    def reset(cls) -> None:
        """Reset discovery cache (useful for testing)."""
        cls._discovered = {}
        cls._is_scanned = False
