#!/usr/bin/env python3
"""Add Atlas per-turn routing controls to the community Hermes WebUI."""

import os
from pathlib import Path


ROOT = Path(os.environ.get("HERMES_WEBUI_PATCH_ROOT", "/opt/hermes-webui"))


def replace_once(path: Path, before: str, after: str, label: str) -> None:
    """Replace one pinned upstream fragment or fail the image build."""
    source = path.read_text(encoding="utf-8")
    count = source.count(before)
    if count != 1:
        raise SystemExit(f"{label} context changed: expected 1, found {count}")
    path.write_text(source.replace(before, after, 1), encoding="utf-8")


index = ROOT / "static/index.html"
replace_once(
    index,
    '''            <div class="composer-ws-wrap">\n''',
    '''            <div class="composer-routing-wrap" id="composerRoutingWrap">
              <button class="composer-routing-chip" id="composerRoutingChip" type="button" title="Automatic routing priority" aria-haspopup="true" aria-expanded="false" aria-controls="composerRoutingDropdown">
                <span class="composer-routing-label" id="composerRoutingLabel">AUTO</span>
                <span class="composer-routing-chevron" aria-hidden="true">⌄</span>
              </button>
            </div>
            <div class="composer-ws-wrap">\n''',
    "routing chip",
)
replace_once(
    index,
    '''          <div class="composer-reasoning-dropdown" id="composerReasoningDropdown">\n''',
    '''          <div class="composer-routing-dropdown" id="composerRoutingDropdown" role="menu" aria-label="Automatic routing priority">
            <div class="routing-option" data-priority="auto"><strong>Auto</strong><span>Use this service's default</span></div>
            <div class="routing-option" data-priority="fast"><strong>Fast</strong><span>Prefer a quicker capable route</span></div>
            <div class="routing-option" data-priority="balanced"><strong>Balanced</strong><span>Balance latency and depth</span></div>
            <div class="routing-option" data-priority="deep"><strong>Deep</strong><span>Favor careful reasoning</span></div>
            <div class="routing-option" data-priority="maximum"><strong>Maximum</strong><span>Strongest route, capped at xhigh</span></div>
          </div>
          <div class="composer-reasoning-dropdown" id="composerReasoningDropdown">\n''',
    "routing dropdown",
)
replace_once(
    index,
    '''<script src="static/ui.js?v=__WEBUI_VERSION__" defer></script>\n''',
    '''<script src="static/ui.js?v=__WEBUI_VERSION__" defer></script>
<script src="static/atlas-router.js?v=__WEBUI_VERSION__" defer></script>\n''',
    "router script",
)

style = ROOT / "static/style.css"
replace_once(
    style,
    '''  .composer-reasoning-chip{display:inline-flex;align-items:center;gap:5px;max-width:none;padding:8px 10px;border-radius:999px;border:1px solid transparent;background-color:transparent;color:var(--muted);font-weight:500;cursor:pointer;transition:color .15s,background-color .15s,border-color .15s;}\n''',
    '''  .composer-routing-chip{display:inline-flex;align-items:center;gap:5px;max-width:none;padding:8px 10px;border-radius:999px;border:1px solid var(--accent-bg);background:var(--accent-bg);color:var(--accent-text);font-weight:600;cursor:pointer;transition:color .15s,background-color .15s,border-color .15s;}
  .composer-routing-chip:hover,.composer-routing-chip.active{color:var(--text);border-color:var(--accent);}
  .composer-routing-label{font-size:11px;font-weight:700;letter-spacing:.04em;}
  .composer-routing-chevron{font-size:11px;line-height:1;}
  .composer-routing-dropdown{display:none;position:absolute;bottom:calc(100% + 4px);left:0;min-width:250px;background:var(--surface);border:1px solid var(--border2);border-radius:10px;box-shadow:0 -4px 24px rgba(0,0,0,.4);z-index:205;padding:4px;overflow:hidden;}
  .composer-routing-dropdown.open{display:block;}
  .routing-option{display:flex;flex-direction:column;gap:2px;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:13px;color:var(--text);white-space:nowrap;transition:background-color .12s;}
  .routing-option span{font-size:11px;color:var(--muted);font-weight:400;}
  .routing-option:hover{background:rgba(255,255,255,.07);}
  .routing-option.selected{background:var(--accent-bg);}
  .composer-reasoning-chip{display:inline-flex;align-items:center;gap:5px;max-width:none;padding:8px 10px;border-radius:999px;border:1px solid transparent;background-color:transparent;color:var(--muted);font-weight:500;cursor:pointer;transition:color .15s,background-color .15s,border-color .15s;}\n''',
    "routing styles",
)

messages = ROOT / "static/messages.js"
replace_once(
    messages,
    '''      explicit_model_pick:_explicitPick||undefined,
      attachments:uploaded.length?uploaded:undefined,
''',
    '''      explicit_model_pick:_explicitPick||undefined,
      ...((typeof window.hermesRoutingRequest==='function')?window.hermesRoutingRequest():{}),
      attachments:uploaded.length?uploaded:undefined,
''',
    "chat routing payload",
)

ui = ROOT / "static/ui.js"
replace_once(
    ui,
    '''  _currentReasoningEffort=effort;
''',
    '''  _currentReasoningEffort=effort;
  window._atlasCurrentReasoningEffort=effort;
''',
    "reasoning effort bridge",
)

routes = ROOT / "api/routes.py"
replace_once(
    routes,
    '''    gateway_chat_enabled: bool | None = None,
):
''',
    '''    gateway_chat_enabled: bool | None = None,
    routing_priority: str = "",
    explicit_model_pick: bool = False,
    explicit_reasoning_effort: str = "",
):
''',
    "start run routing arguments",
)
replace_once(
    routes,
    '''                external_runtime_owned=gateway_chat_enabled,
            )
''',
    '''                external_runtime_owned=gateway_chat_enabled,
                routing_priority=routing_priority,
                explicit_model_pick=explicit_model_pick,
                explicit_reasoning_effort=explicit_reasoning_effort,
            )
''',
    "adapter routing forwarding",
)
replace_once(
    routes,
    '''        external_runtime_owned=gateway_chat_enabled,
    )


def _process_wakeup_revalidation_provider''',
    '''        external_runtime_owned=gateway_chat_enabled,
        routing_priority=routing_priority,
        explicit_model_pick=explicit_model_pick,
        explicit_reasoning_effort=explicit_reasoning_effort,
    )


def _process_wakeup_revalidation_provider''',
    "direct routing forwarding",
)
replace_once(
    routes,
    '''    external_runtime_owned: bool | None = None,
):
''',
    '''    external_runtime_owned: bool | None = None,
    routing_priority: str = "",
    explicit_model_pick: bool = False,
    explicit_reasoning_effort: str = "",
):
''',
    "stream routing arguments",
)
replace_once(
    routes,
    '''    worker_kwargs = {"model_provider": model_provider, "goal_related": goal_related}
''',
    '''    worker_kwargs = {"model_provider": model_provider, "goal_related": goal_related}
    if backend_is_gateway:
        worker_kwargs.update({
            "routing_priority": routing_priority,
            "explicit_model_pick": explicit_model_pick,
            "explicit_reasoning_effort": explicit_reasoning_effort,
        })
''',
    "gateway worker routing arguments",
)
replace_once(
    routes,
    '''        explicit_model_pick = bool(body.get("explicit_model_pick"))
        moa_config = None
''',
    '''        explicit_model_pick = bool(body.get("explicit_model_pick"))
        routing_priority = str(body.get("routing_priority") or "").strip().lower()
        if routing_priority not in {"", "auto", "fast", "balanced", "deep", "maximum"}:
            return bad(handler, "invalid routing priority", 400)
        if routing_priority == "auto":
            routing_priority = ""
        explicit_reasoning_effort = str(
            body.get("explicit_reasoning_effort") or ""
        ).strip().lower()
        if explicit_reasoning_effort not in {
            "", "none", "minimal", "low", "medium", "high", "xhigh"
        }:
            return bad(handler, "invalid explicit reasoning effort", 400)
        moa_config = None
''',
    "chat routing validation",
)
replace_once(
    routes,
    '''            "gateway_chat_enabled": gateway_chat_enabled,
        }
''',
    '''            "gateway_chat_enabled": gateway_chat_enabled,
            "routing_priority": routing_priority,
            "explicit_model_pick": explicit_model_pick,
            "explicit_reasoning_effort": explicit_reasoning_effort,
        }
''',
    "chat routing start arguments",
)

gateway = ROOT / "api/gateway_chat.py"
replace_once(
    gateway,
    '''    model_provider=None,
    goal_related=False,
):
''',
    '''    model_provider=None,
    goal_related=False,
    routing_priority="",
    explicit_model_pick=False,
    explicit_reasoning_effort="",
):
''',
    "gateway routing arguments",
)
replace_once(
    gateway,
    '''            if _gw_overrides.get("service_tier"):
                body_extras["service_tier"] = _gw_overrides["service_tier"]
''',
    '''            if _gw_overrides.get("service_tier"):
                body_extras["service_tier"] = _gw_overrides["service_tier"]
            body_extras["routing_priority"] = routing_priority
            body_extras["explicit_model_pick"] = bool(explicit_model_pick)
            body_extras["explicit_reasoning_effort"] = explicit_reasoning_effort
''',
    "runs API routing body",
)
