"""Grouping and ordering of search engines in the selector UI.

Single source of truth for which band a search engine falls into and the order
the bands appear in. The available-search-engines API attaches each engine's
band to the response and the frontend renders one header per band, so the
bucketing logic lives here only and is never duplicated in JavaScript.
"""

from dataclasses import dataclass


@dataclass(frozen=True)
class SearchEngineGroup:
    """A band in the search-engine selector dropdown."""

    key: str  # stable identifier, e.g. "academic"
    label: str  # header text shown in the dropdown
    icon: str  # emoji associated with the band


# Bands in display order, top to bottom; the list index IS the sort order
# (lower sorts higher). "favorites" is an overlay band: a starred engine is
# shown here regardless of its category — see effective_group().
SEARCH_ENGINE_GROUPS: tuple[SearchEngineGroup, ...] = (
    SearchEngineGroup("favorites", "Favorites", "⭐"),
    SearchEngineGroup("collections", "Collections", "📁"),
    SearchEngineGroup("academic", "Academic", "🔬"),
    SearchEngineGroup("local_rag", "Local RAG", "📂"),
    SearchEngineGroup("books", "Books", "📚"),
    SearchEngineGroup("code", "Code", "💻"),
    SearchEngineGroup("news", "News", "📰"),
    SearchEngineGroup("no_api_key", "No API key", "🌐"),
    SearchEngineGroup("api_key", "API key", "🔑"),
)

# Not a secret — the band's identifier string; matches SEARCH_ENGINE_GROUPS[0].
FAVORITES_GROUP_KEY = "favorites"  # gitleaks:allow

_GROUP_BY_KEY: dict[str, SearchEngineGroup] = {
    group.key: group for group in SEARCH_ENGINE_GROUPS
}
_GROUP_ORDER: dict[str, int] = {
    group.key: index for index, group in enumerate(SEARCH_ENGINE_GROUPS)
}

# Maps the category label produced by
# settings_routes._get_engine_icon_and_category() to a band. Any category not
# listed here (e.g. "Web Search", "Search") falls through to the API-key split.
# Keep these strings in sync with that function.
_CATEGORY_TO_GROUP: dict[str, str] = {
    "Scientific": "academic",
    "Local RAG": "local_rag",
    "Books": "books",
    "Code": "code",
    "News": "news",
}


def is_collection_engine(engine_id: str) -> bool:
    """True for the document-collection pseudo-engines: the aggregate
    "Search All Collections" entry (``library``) and the per-collection
    ``collection_<id>`` entries."""
    return engine_id == "library" or engine_id.startswith("collection_")


def classify_engine_group(
    engine_id: str, category: str, requires_api_key: bool
) -> str:
    """Return the base band key for an engine, ignoring favorite status.

    Document collections come first, then engines with a distinct category
    (academic / local / books / code / news); everything else (generic web
    search) is split only by whether it needs an API key.
    """
    if is_collection_engine(engine_id):
        return "collections"
    category_group = _CATEGORY_TO_GROUP.get(category)
    if category_group is not None:
        return category_group
    return "api_key" if requires_api_key else "no_api_key"


def effective_group(base_group_key: str, is_favorite: bool) -> str:
    """The band an engine is actually shown in: the Favorites overlay wins over
    the engine's base category so a starred engine floats to the top band."""
    return FAVORITES_GROUP_KEY if is_favorite else base_group_key


def group_order(group_key: str) -> int:
    """Sort index for a band (lower sorts higher); unknown keys sort last."""
    return _GROUP_ORDER.get(group_key, len(SEARCH_ENGINE_GROUPS))


def group_label(group_key: str) -> str:
    """Header text for a band; falls back to the key if unknown."""
    group = _GROUP_BY_KEY.get(group_key)
    return group.label if group is not None else group_key


def group_icon(group_key: str) -> str:
    """Emoji for a band; empty string if unknown."""
    group = _GROUP_BY_KEY.get(group_key)
    return group.icon if group is not None else ""
