"""
External algorithm backends for SkyDiscover.

Thin wrappers that delegate to external evolution packages (OpenEvolve,
ShinkaEvolve, GEPA, etc.) while returning SkyDiscover's unified DiscoveryResult.

To add a new backend:
  1. Create skydiscover/extras/external/<name>_backend.py with an async ``run`` function
  2. Add an entry to ``_BACKENDS`` below
"""

from typing import Dict, Callable, Awaitable

# search_type -> async runner function
_REGISTRY: Dict[str, Callable[..., Awaitable]] = {}

# All backend names we know about, even if the package is not installed.
# Used to give a helpful ImportError instead of "unknown search type".
KNOWN_EXTERNAL = {"openevolve", "shinkaevolve", "gepa"}

# search_type -> actual pip package name (when they differ)
_PACKAGE_NAMES = {
    "shinkaevolve": "shinka",
    "gepa": "gepa[full]",
}


def get_package_name(search_type: str) -> str:
    """Return the pip-installable package name for a search type."""
    return _PACKAGE_NAMES.get(search_type, search_type)

_BACKENDS = [
    ("openevolve", "skydiscover.extras.external.openevolve_backend", "run"),
    ("shinkaevolve", "skydiscover.extras.external.shinkaevolve_backend", "run"),
    ("gepa", "skydiscover.extras.external.gepa_backend", "run"),
]


def is_external(search_type: str) -> bool:
    return search_type in _REGISTRY


def get_runner(search_type: str):
    return _REGISTRY[search_type]


def _register_backends():
    """Attempt to register each backend. Missing packages are silently skipped."""
    import importlib
    import logging
    _logger = logging.getLogger(__name__)
    for name, module_path, func_name in _BACKENDS:
        try:
            mod = importlib.import_module(module_path)
            _REGISTRY[name] = getattr(mod, func_name)
        except ImportError:
            pass  # Package not installed — expected
        except Exception as e:
            _logger.debug("Failed to register backend '%s': %s", name, e)


_register_backends()
