"""Logical App Block UI-slot hosts. Framework paths live in the Starter host."""

from src.domain.app_blocks.registry import AppBlockRegistry, load_default_registry


def _host_key(block: str, slot: str) -> str:
    return f"{block}#{slot}"


def host_map_payload(registry: AppBlockRegistry | None = None) -> dict:
    registry = registry or load_default_registry()
    hosts = []
    for manifest in registry.all():
        if not manifest.implemented:
            continue
        for slot in manifest.slots:
            if slot.type != "ui_slot":
                continue
            hosts.append(
                {
                    "block": manifest.id,
                    "slot": slot.id,
                    "component_contract": slot.component_contract,
                    "presentation": slot.presentation or "default",
                    "query_source": slot.query_source,
                    "query_params": slot.query_params,
                    "implementation": None,
                }
            )
    return {"version": 1, "hosts": hosts}


def merge_host_map(existing: dict | None, catalog: dict | None = None) -> dict:
    """Preserve host implementation paths when Core refreshes the slot catalog."""
    catalog = catalog or host_map_payload()
    impls: dict[str, str] = {}
    if isinstance(existing, dict):
        for item in existing.get("hosts") or []:
            if not isinstance(item, dict):
                continue
            path = item.get("implementation")
            block = item.get("block")
            slot = item.get("slot")
            if block and slot and isinstance(path, str) and path.strip():
                impls[_host_key(str(block), str(slot))] = path.strip()
    merged = []
    for item in catalog.get("hosts") or []:
        row = dict(item)
        key = _host_key(str(row.get("block") or ""), str(row.get("slot") or ""))
        if key in impls:
            row["implementation"] = impls[key]
        merged.append(row)
    return {**catalog, "hosts": merged}
