from __future__ import annotations

from typing import Any

# Import modules for registration side effects.
from . import _register_defaults  # noqa: F401
from .registry import get_backend, get_surrogate, get_acquisition


def create_surrogate(name: str, **params: Any):
    cls = get_surrogate(name)
    return cls(**params)


def create_acquisition(name: str, **params: Any):
    cls = get_acquisition(name)
    return cls(**params)


def create_optimizer(
    model: str = "custom",
    backend: str | None = None,
    surrogate: str = "sklearn_gpr",
    acquisition: str = "ei",
    **params: Any,
):
    """Create optimizer.

    Parameters
    ----------
    model, backend:
        Backend selector. ``model`` is accepted for user-facing CLI compatibility.
        Examples: ``physbo``, ``custom``, ``sklearn_gpr``, ``random``, ``grid``.
    surrogate:
        Surrogate used by custom backend. Examples: ``sklearn_gpr``, ``physbo_gp``.
    acquisition:
        Acquisition used by custom backend. Examples: ``ei``, ``pi``, ``ucb``, ``stein``.
    params:
        Passed to backend. For CustomOptimizer, use prefixes:
        ``surrogate__alpha=...`` and ``acquisition__kappa=...``.
    """
    if backend is None:
        backend = model
    cls = get_backend(backend)

    # Custom convenience: model="sklearn_gpr" means custom backend + sklearn_gpr surrogate.
    if backend in {"sklearn_gpr", "physbo_gp"}:
        surrogate = backend
        backend = "custom"
        cls = get_backend("custom")

    if backend == "custom":
        surrogate_params = {}
        acquisition_params = {}
        backend_params = {}
        for key, val in params.items():
            if key.startswith("surrogate__"):
                surrogate_params[key.split("__", 1)[1]] = val
            elif key.startswith("acquisition__"):
                acquisition_params[key.split("__", 1)[1]] = val
            else:
                backend_params[key] = val
        s = create_surrogate(surrogate, **surrogate_params)
        a = create_acquisition(acquisition, **acquisition_params)
        return cls(surrogate=s, acquisition=a, **backend_params)

    return cls(**params)
