"""Request handlers for EB1-GNN server."""

from __future__ import annotations

import asyncio
import logging
import random
import time
import uuid
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

import numpy as np

from ember.eb1_gnn.errors import GatewayTimeoutError, RequestValidationError
from ember.eb1_gnn.executor import (
    TIMEOUT_CONDUCTOR_SECONDS,
    TIMEOUT_ENSEMBLE_SECONDS,
    TIMEOUT_PREVIEW_SECONDS,
    _ENSEMBLE_SENTINELS,
)
from ember.takumi.model_pool import CLAUDE_OPUS

if TYPE_CHECKING:
    from embergraphrouter.inference.tiered_router import TieredRoutingResult

    from ember.eb1_gnn.optimizations import OptimizedQwen3Embedding
    from ember.eb1_gnn.routing import EmbeddedRouter
    from ember.eb1_gnn.server import ServerState
    from ember.takumi.eb1_gnn_conductor import EB1Conductor

logger = logging.getLogger(__name__)

_EXECUTION_PLAN_OVERRIDES: dict[str, tuple[str, bool, str | None]] = {
    "eb1": ("eb1", True, "DEFAULT_PLAN"),
    "eb1-pro": ("eb1-pro", True, "DEFAULT_PRO_PLAN"),
    "eb1-pro-high": ("eb1-pro", True, "DEFAULT_PRO_PLAN"),
    "ensemble": ("eb1-pro", True, "DEFAULT_PRO_PLAN"),
    "eb1-codex": ("eb1-codex", True, "DEFAULT_CODEX_PLAN"),
}

_ROUTED_EB1_RESPONSES_MODELS = frozenset(
    {
        "eb1-preview",
        "eb1-fast-preview",
        "eb1-frontier-preview",
    }
)


def _extract_text_from_content(content: Any) -> str:
    """Extract text from a message content field (str or list of dicts)."""
    if isinstance(content, str):
        return content
    if isinstance(content, list):
        # Responses API format: [{"type": "input_text", "text": "..."}, ...]
        texts = []
        for part in content:
            if isinstance(part, dict):
                if part.get("type") in ("input_text", "output_text", "text"):
                    text = part.get("text", "")
                    if text:
                        texts.append(text)
        return " ".join(texts)
    return ""


def _extract_query_text(input_content: Any) -> str:
    """Extract query text from the last user message for GNN routing."""
    if isinstance(input_content, str):
        return input_content

    if not isinstance(input_content, list) or not input_content:
        return str(input_content) if input_content else ""

    for item in reversed(input_content):
        if not isinstance(item, dict):
            continue

        item_type = item.get("type", "")
        role = item.get("role", "")

        # Responses API message items have type="message" and role
        if item_type == "message" and role == "user":
            content = item.get("content", "")
            text = _extract_text_from_content(content)
            if text:
                return text

        # Chat Completions format: no "type" field, just role
        if not item_type and role == "user":
            content = item.get("content", "")
            text = _extract_text_from_content(content)
            if text:
                return text

    # Fallback: try to extract from first item
    first_item = input_content[0]
    if isinstance(first_item, dict):
        # Check for direct text content
        content = first_item.get("content", "")
        text = _extract_text_from_content(content)
        if text:
            return text
        # Check for input field (simple string input wrapped in list)
        if "input" in first_item:
            return str(first_item["input"])
    elif isinstance(first_item, str):
        return first_item

    return ""


def _normalize_target_model(model_id: str) -> str:
    """Map ensemble sentinels to a concrete model for gateway dispatch."""
    if model_id in _ENSEMBLE_SENTINELS:
        return CLAUDE_OPUS
    return model_id


def _timeout_for_model(model_id: str) -> float:
    """Select timeout based on whether model_id is an ensemble sentinel."""
    if model_id in _ENSEMBLE_SENTINELS:
        return TIMEOUT_ENSEMBLE_SECONDS
    return TIMEOUT_PREVIEW_SECONDS


def _enrich_with_model_params(body: dict[str, Any], model: str) -> dict[str, Any]:
    """Merge per-model defaults from MODEL_PARAMS into body (user params win)."""
    from ember.takumi.eb1_swe import MODEL_PARAMS

    params = MODEL_PARAMS.get(model)
    if not params:
        return body
    for key, value in params.items():
        body.setdefault(key, value)
    return body


def _resolve_execution_plan(model_id: str) -> tuple[str, bool, str | None]:
    """Resolve executor inputs from a target model ID.

    Checks for ensemble sentinels BEFORE normalization so that ensemble
    routing is preserved for the chat completions path (where
    ``execute_model`` runs ensembles inline via ``_execute_ensemble_inline``).
    """
    override = _EXECUTION_PLAN_OVERRIDES.get(model_id)
    if override is not None:
        return override
    # Non-ensemble: normalize to concrete gateway model ID.
    return _normalize_target_model(model_id), False, None


def _resolve_swe_model_map(variant: str) -> dict[str, str]:
    """Resolve SWE complexity map for the requested variant."""
    from ember.takumi.eb1_swe import (
        COMPLEXITY_MODEL_MAP,
        FAST_COMPLEXITY_MODEL_MAP,
        PRO_COMPLEXITY_MODEL_MAP,
    )

    if variant == "fast":
        return FAST_COMPLEXITY_MODEL_MAP
    if variant == "pro":
        return PRO_COMPLEXITY_MODEL_MAP
    return COMPLEXITY_MODEL_MAP


def _swe_variant_from_model_id(model_id: str) -> str:
    """Extract SWE variant from a model id."""
    if "-fast-" in model_id:
        return "fast"
    if "-pro-" in model_id:
        return "pro"
    return "balanced"


@dataclass(frozen=True, slots=True)
class ServingPolicy:
    """Configuration for how to serve eb1-preview variants.

    This separates the GNN routing logic from the fallback policy decisions,
    allowing multiple model variants to share the same underlying router.

    Attributes:
        name: Model name returned to clients (e.g., "eb1-preview").
        ood_fallback: Model for out-of-distribution queries.
        uncertain_fallback: Model for uncertain/hard queries (low confidence).
        ensemble_for_uncertain: Whether to use eb1-pro ensemble for uncertain.
        ensemble_confidence_threshold: Below this confidence, escalate to ensemble.
    """

    name: str
    ood_fallback: str
    uncertain_fallback: str
    ensemble_for_uncertain: bool = False
    ensemble_confidence_threshold: float = 0.15


# Predefined serving policies
BALANCED_POLICY = ServingPolicy(
    name="eb1-preview",
    ood_fallback=CLAUDE_OPUS,
    uncertain_fallback=CLAUDE_OPUS,
    ensemble_for_uncertain=False,
)

FAST_POLICY = ServingPolicy(
    name="eb1-fast-preview",
    ood_fallback="gemini-3-flash-preview",
    uncertain_fallback="gemini-3-flash-preview",
    ensemble_for_uncertain=False,
)

FRONTIER_POLICY = ServingPolicy(
    name="eb1-frontier-preview",
    ood_fallback="eb1-pro",
    uncertain_fallback="eb1-pro",
    ensemble_for_uncertain=True,
    ensemble_confidence_threshold=0.20,
)


async def handle_eb1_with_policy(
    state: "ServerState",
    messages: list[dict],
    query: str,
    request_body: dict,
    request_id: str,
    policy: ServingPolicy,
) -> dict[str, Any]:
    """Route and execute an eb1 request using TieredRouter with the given policy."""
    from embergraphrouter.inference import InferenceTier

    start_time = time.perf_counter()

    route_start = time.perf_counter()
    routing_result = await _route_off_thread(state, query)
    route_time_ms = (time.perf_counter() - route_start) * 1000

    tier = routing_result.tier
    signals = routing_result.signals
    selected_model = routing_result.selected_model
    use_ensemble = routing_result.selected_config.use_ensemble
    ensemble_plan = routing_result.selected_config.ensemble_plan

    policy_override = None

    if tier == InferenceTier.OUT_OF_DISTRIBUTION:
        selected_model, use_ensemble, ensemble_plan = _resolve_execution_plan(policy.ood_fallback)
        policy_override = "ood_fallback"

    elif tier == InferenceTier.UNCERTAIN:
        if policy.ensemble_for_uncertain:
            if signals.max_prob < policy.ensemble_confidence_threshold:
                selected_model, use_ensemble, ensemble_plan = _resolve_execution_plan(
                    policy.uncertain_fallback
                )
                policy_override = "ensemble_escalation"
        elif policy.uncertain_fallback != CLAUDE_OPUS:
            selected_model = policy.uncertain_fallback
            use_ensemble = False
            ensemble_plan = None
            policy_override = "uncertain_fallback"

    logger.info(
        "Request %s [%s]: tier=%s, model=%s, conf=%.3f, route=%.1fms%s",
        request_id,
        policy.name,
        tier.name,
        selected_model,
        signals.max_prob,
        route_time_ms,
        f", override={policy_override}" if policy_override else "",
    )

    from ember.eb1_gnn.executor import execute_model

    timeout = TIMEOUT_ENSEMBLE_SECONDS if use_ensemble else _timeout_for_model(selected_model)

    exec_start = time.perf_counter()
    response = await execute_model(
        model=selected_model,
        messages=messages,
        request_body=request_body,
        use_ensemble=use_ensemble,
        ensemble_plan=ensemble_plan,
        gateway_url=state.config.gateway_url,
        session=state.session,
        timeout_seconds=timeout,
        request_id=request_id,
    )
    exec_time_ms = (time.perf_counter() - exec_start) * 1000

    total_time_ms = (time.perf_counter() - start_time) * 1000

    logger.info(
        "Request %s [%s]: completed in %.1fms (route=%.1fms, exec=%.1fms)",
        request_id,
        policy.name,
        total_time_ms,
        route_time_ms,
        exec_time_ms,
    )

    response["_tier"] = tier.name
    response["_selected_model"] = selected_model
    response["_route_time_ms"] = route_time_ms
    response["_exec_time_ms"] = exec_time_ms
    response["_total_time_ms"] = total_time_ms
    response["_policy"] = policy.name
    response["_confidence"] = signals.max_prob
    response["_entropy"] = signals.entropy
    if policy_override:
        response["_policy_override"] = policy_override

    response["_top2_model"] = signals.top2_model
    response["_logit_margin"] = signals.logit_margin
    response["_logit_std"] = signals.logit_std
    response["_mean_logit"] = signals.mean_logit
    if signals.all_probs:
        response["_distribution"] = signals.all_probs

    response["model"] = policy.name

    return response


async def handle_eb1_preview(
    state: "ServerState",
    messages: list[dict],
    query: str,
    request_body: dict,
    request_id: str,
) -> dict[str, Any]:
    """Handle eb1-preview request using balanced policy (Claude Opus OOD fallback)."""
    return await handle_eb1_with_policy(
        state=state,
        messages=messages,
        query=query,
        request_body=request_body,
        request_id=request_id,
        policy=BALANCED_POLICY,
    )


async def handle_eb1_fast_preview(
    state: "ServerState",
    messages: list[dict],
    query: str,
    request_body: dict,
    request_id: str,
) -> dict[str, Any]:
    """Handle eb1-fast-preview request using fast policy (Gemini 3 Flash OOD fallback)."""
    return await handle_eb1_with_policy(
        state=state,
        messages=messages,
        query=query,
        request_body=request_body,
        request_id=request_id,
        policy=FAST_POLICY,
    )


async def handle_eb1_frontier_preview(
    state: "ServerState",
    messages: list[dict],
    query: str,
    request_body: dict,
    request_id: str,
) -> dict[str, Any]:
    """Handle eb1-frontier-preview using frontier policy (eb1-pro for hard queries)."""
    return await handle_eb1_with_policy(
        state=state,
        messages=messages,
        query=query,
        request_body=request_body,
        request_id=request_id,
        policy=FRONTIER_POLICY,
    )


async def handle_eb1_delta_preview(
    state: "ServerState",
    messages: list[dict],
    query: str,
    request_body: dict,
    request_id: str,
) -> dict[str, Any]:
    """Handle eb1-delta-preview: tiered routing with conductor orchestration.

    OOD queries use probabilistic exploration (ood_explore_rate) to route
    a fraction through the conductor instead of always bypassing. Non-OOD
    queries check conductor bypass thresholds before invoking the conductor.
    """
    from embergraphrouter.inference import InferenceTier
    from embergraphrouter.inference.conductor_bypass import (
        BALANCED_THRESHOLDS,
        estimate_perf_range,
        should_bypass_conductor,
    )

    start_time = time.perf_counter()
    ood_explore_rate = state.config.ood_explore_rate
    ood_fallback_model = state.config.ood_fallback_model

    system_message = ""
    for msg in messages:
        if msg.get("role") == "system":
            content = msg.get("content", "")
            if isinstance(content, str):
                system_message = content
            break

    routing_result = await _route_off_thread(state, query)
    tier = routing_result.tier
    signals = routing_result.signals

    ood_explore_decision = None
    if tier == InferenceTier.OUT_OF_DISTRIBUTION:
        explore_roll = random.random()
        should_explore_ood = explore_roll < ood_explore_rate

        if should_explore_ood:
            logger.info(
                "Request %s (delta): OOD EXPLORE (roll=%.3f < rate=%.2f, conf=%.3f, "
                "logit_std=%.3f)",
                request_id,
                explore_roll,
                ood_explore_rate,
                signals.max_prob,
                signals.logit_std,
            )
            ood_explore_decision = "explore"
            bypass_reason = "OOD_EXPLORATION"
            should_bypass = False
        else:
            logger.info(
                "Request %s (delta): OOD BYPASS (roll=%.3f >= rate=%.2f, conf=%.3f, "
                "logit_std=%.3f)",
                request_id,
                explore_roll,
                ood_explore_rate,
                signals.max_prob,
                signals.logit_std,
            )
            ood_explore_decision = "exploit"
            target_model = ood_fallback_model
            bypass_reason = "OUT_OF_DISTRIBUTION"
            fallback_action = "use_top_tier"
            should_bypass = True

    else:
        predicted_perf_range = estimate_perf_range(signals)
        should_bypass, bypass_reason, fallback_action = should_bypass_conductor(
            signals, BALANCED_THRESHOLDS, predicted_perf_range
        )

        if should_bypass:
            logger.info(
                "Request %s (delta): %s (conf=%.3f, margin=%.3f, perf_range=%.3f)",
                request_id,
                bypass_reason,
                signals.max_prob,
                signals.logit_margin,
                predicted_perf_range,
            )

            if fallback_action == "use_gnn_pick":
                target_model = routing_result.selected_model
            elif fallback_action == "use_top_tier":
                target_model = CLAUDE_OPUS
            else:
                raise ValueError(
                    f"Unknown fallback_action from bypass: {fallback_action!r}"
                )
        else:
            logger.info(
                "Request %s (delta): %s (conf=%.3f, margin=%.3f)",
                request_id,
                bypass_reason,
                signals.max_prob,
                signals.logit_margin,
            )

    if should_bypass:
        try:
            from ember.eb1_gnn.executor import execute_model

            execution_model, use_ensemble, ensemble_plan = _resolve_execution_plan(target_model)
            response = await execute_model(
                model=execution_model,
                messages=messages,
                request_body=request_body,
                use_ensemble=use_ensemble,
                ensemble_plan=ensemble_plan,
                gateway_url=state.config.gateway_url,
                session=state.session,
                timeout_seconds=_timeout_for_model(execution_model),
                request_id=request_id,
            )

            total_time_ms = (time.perf_counter() - start_time) * 1000

            response["model"] = "eb1-delta-preview"
            response["_bypass_reason"] = bypass_reason
            response["_bypass_action"] = fallback_action
            response["_selected_model"] = execution_model
            response["_routing_confidence"] = signals.max_prob
            response["_routing_entropy"] = signals.entropy
            response["_routing_logit_std"] = signals.logit_std
            response["_routing_logit_margin"] = signals.logit_margin
            response["_routing_mean_logit"] = signals.mean_logit
            response["_routing_top2_model"] = signals.top2_model
            response["_routing_distribution"] = signals.all_probs
            response["_total_time_ms"] = total_time_ms
            response["_conductor_skipped"] = True
            response["_tier"] = tier.name
            response["_ood_explore_decision"] = ood_explore_decision
            response["_ood_explore_rate"] = (
                ood_explore_rate if ood_explore_decision else None
            )

            logger.info(
                "Request %s (delta): bypassed in %.1fms, model=%s",
                request_id,
                total_time_ms,
                execution_model,
            )

            return response

        except Exception as e:
            logger.exception("Request %s (delta): bypass execution failed: %s", request_id, e)
            logger.warning(
                "Request %s (delta): falling back to conductor after bypass failure",
                request_id,
            )

    try:
        conductor_result = await _run_conductor_async(
            state.conductor,
            query=query,
            system=system_message,
            messages=messages,
            request_body=request_body,
            state=state,
            request_id=request_id,
        )

        total_time_ms = (time.perf_counter() - start_time) * 1000

        if ood_explore_decision == "explore":
            conductor_result["_ood_explore_decision"] = "explore"
            conductor_result["_ood_explore_rate"] = ood_explore_rate
            conductor_result["_ood_explore_outcome"] = "conductor_success"
            conductor_result["_routing_confidence"] = signals.max_prob
            conductor_result["_routing_logit_std"] = signals.logit_std
            conductor_result["_routing_logit_margin"] = signals.logit_margin
            conductor_result["_routing_mean_logit"] = signals.mean_logit
            conductor_result["_routing_top2_model"] = signals.top2_model
            if signals.all_probs:
                conductor_result["_routing_distribution"] = signals.all_probs

        logger.info(
            "Request %s (delta): completed in %.1fms, actions=%d%s",
            request_id,
            total_time_ms,
            conductor_result.get("_action_count", 1),
            " [OOD_EXPLORE]" if ood_explore_decision == "explore" else "",
        )

        return conductor_result

    except Exception as e:
        logger.exception("Request %s (delta): conductor failed: %s", request_id, e)

        # OOD exploration fallback: use top-tier model if conductor fails
        if ood_explore_decision == "explore":
            logger.warning(
                "Request %s (delta): OOD exploration failed, falling back to %s",
                request_id,
                ood_fallback_model,
            )
            try:
                from ember.eb1_gnn.executor import execute_model

                fallback_model, use_ensemble, ensemble_plan = _resolve_execution_plan(
                    ood_fallback_model
                )
                response = await execute_model(
                    model=fallback_model,
                    messages=messages,
                    request_body=request_body,
                    use_ensemble=use_ensemble,
                    ensemble_plan=ensemble_plan,
                    gateway_url=state.config.gateway_url,
                    session=state.session,
                    timeout_seconds=_timeout_for_model(fallback_model),
                    request_id=request_id,
                )

                total_time_ms = (time.perf_counter() - start_time) * 1000

                response["model"] = "eb1-delta-preview"
                response["_tier"] = tier.name
                response["_selected_model"] = fallback_model
                response["_ood_explore_decision"] = "explore"
                response["_ood_explore_rate"] = ood_explore_rate
                response["_ood_explore_outcome"] = "conductor_failed_fallback"
                response["_conductor_error"] = str(e)
                response["_routing_confidence"] = signals.max_prob
                response["_routing_logit_std"] = signals.logit_std
                response["_routing_logit_margin"] = signals.logit_margin
                response["_routing_mean_logit"] = signals.mean_logit
                response["_routing_top2_model"] = signals.top2_model
                response["_routing_distribution"] = signals.all_probs
                response["_total_time_ms"] = total_time_ms
                return response
            except Exception as fallback_error:
                logger.exception(
                    "Request %s (delta): OOD fallback also failed: %s",
                    request_id,
                    fallback_error,
                )
                raise e from fallback_error

        raise


@dataclass
class _PendingRoute:
    """A routing request waiting to be batch-processed."""

    query: str
    future: "asyncio.Future[TieredRoutingResult]"


class BatchRouteCollector:
    """Collects concurrent routing requests and batch-embeds them on GPU.

    Blocks on the first request, then drains the queue for up to
    ``max_wait_ms`` before submitting a batch to ``embed_batch()``.
    """

    __slots__ = (
        "_embedder",
        "_router",
        "_pool",
        "_semaphore",
        "_queue",
        "_task",
        "_max_batch",
        "_max_wait_s",
    )

    def __init__(
        self,
        embedder: "OptimizedQwen3Embedding",
        router: "EmbeddedRouter",
        pool: ThreadPoolExecutor,
        semaphore: asyncio.Semaphore,
        max_batch_size: int = 16,
        max_wait_ms: float = 5.0,
    ) -> None:
        self._embedder = embedder
        self._router = router
        self._pool = pool
        self._semaphore = semaphore
        self._queue: asyncio.Queue[_PendingRoute] = asyncio.Queue()
        self._task: asyncio.Task[None] | None = None
        self._max_batch = max_batch_size
        self._max_wait_s = max_wait_ms / 1000.0

    def start(self) -> None:
        """Start the background drain loop."""
        self._task = asyncio.create_task(self._drain_loop())

    async def stop(self) -> None:
        """Cancel the drain loop and wait for it to finish."""
        if self._task is not None:
            self._task.cancel()
            try:
                await self._task
            except asyncio.CancelledError:
                pass

    async def route(self, query: str) -> "TieredRoutingResult":
        """Submit a query for batch routing.

        Returns:
            TieredRoutingResult from EmbeddedRouter.
        """
        loop = asyncio.get_running_loop()
        future: asyncio.Future[TieredRoutingResult] = loop.create_future()
        await self._queue.put(_PendingRoute(query=query, future=future))
        return await future

    async def _drain_loop(self) -> None:
        """Block on first request, drain queue, batch-embed, route each."""
        while True:
            batch: list[_PendingRoute] = []

            first = await self._queue.get()
            batch.append(first)

            while len(batch) < self._max_batch:
                try:
                    item = self._queue.get_nowait()
                    batch.append(item)
                except asyncio.QueueEmpty:
                    break

            # Timed collection window only when concurrent load detected
            if len(batch) > 1:
                loop = asyncio.get_running_loop()
                deadline = loop.time() + self._max_wait_s
                while len(batch) < self._max_batch:
                    remaining = deadline - loop.time()
                    if remaining <= 0:
                        break
                    try:
                        item = await asyncio.wait_for(
                            self._queue.get(), timeout=remaining
                        )
                        batch.append(item)
                    except asyncio.TimeoutError:
                        break

            await self._process_batch(batch)

    async def _process_batch(self, batch: list[_PendingRoute]) -> None:
        """Embed and route a batch, then resolve each request's future."""
        queries = [req.query for req in batch]
        loop = asyncio.get_running_loop()

        async with self._semaphore:
            outcomes = await loop.run_in_executor(
                self._pool, self._embed_and_route, queries
            )

        for req, (result, error) in zip(batch, outcomes):
            if error is not None:
                req.future.set_exception(error)
            else:
                req.future.set_result(result)

    def _embed_and_route(
        self, queries: list[str],
    ) -> list[tuple["TieredRoutingResult | None", Exception | None]]:
        """Batch-embed queries then route each. Runs in the thread pool."""
        embed_start = time.perf_counter()
        try:
            embeddings: np.ndarray = self._embedder.embed_batch(queries)
        except Exception as exc:
            return [(None, exc)] * len(queries)
        embed_ms = (time.perf_counter() - embed_start) * 1000
        per_query_ms = embed_ms / len(queries)

        logger.debug(
            "Batch embed: %d queries in %.1fms (%.1fms/query)",
            len(queries),
            embed_ms,
            per_query_ms,
        )

        outcomes: list[tuple[TieredRoutingResult | None, Exception | None]] = []
        for i, _query in enumerate(queries):
            try:
                result = self._router.route_with_embedding(
                    embeddings[i], embed_latency_ms=per_query_ms
                )
                outcomes.append((result, None))
            except Exception as exc:
                outcomes.append((None, exc))
        return outcomes


async def _route_off_thread(
    state: "ServerState", query: str,
) -> "TieredRoutingResult":
    """Run router.route() off the event loop via batch collector or thread pool."""
    if state.batch_collector is not None:
        return await state.batch_collector.route(query)

    loop = asyncio.get_running_loop()

    if state.embedding_semaphore is not None and state.embedding_pool is not None:
        async with state.embedding_semaphore:
            return await loop.run_in_executor(
                state.embedding_pool,
                state.router.route,
                query,
            )

    # No pool configured (backward compat / tests)
    return await loop.run_in_executor(None, state.router.route, query)


async def _run_conductor_async(
    conductor: "EB1Conductor",
    query: str,
    system: str,
    messages: list[dict],
    request_body: dict,
    *,
    state: "ServerState",
    request_id: str,
) -> dict[str, Any]:
    """Run the synchronous EB1Conductor in a thread pool with timeout."""
    loop = asyncio.get_running_loop()

    def run_sync() -> tuple:
        return conductor(
            query,
            system=system,
        )

    executor = state.conductor_pool
    try:
        result = await asyncio.wait_for(
            loop.run_in_executor(executor, run_sync),
            timeout=TIMEOUT_CONDUCTOR_SECONDS,
        )
    except asyncio.TimeoutError as exc:
        raise GatewayTimeoutError(
            detail=f"Conductor execution timed out after {TIMEOUT_CONDUCTOR_SECONDS}s",
            request_id=request_id,
        ) from exc

    if not isinstance(result, tuple) or len(result) != 2:
        raise TypeError(
            f"EB1Conductor returned unexpected type {type(result).__name__}; "
            f"expected tuple[ChatResponse, ConductorTrace]"
        )

    chat_response, conductor_trace = result
    response_content = chat_response.data or chat_response.text or ""
    action_count = max(
        len(conductor_trace.subtask_results),
        len(conductor_trace.directives),
        1,
    )
    routing_path = "conductor"
    actual_model = "eb1-delta-preview"
    aggregated_usage = conductor_trace.aggregate_usage()

    completion_id = f"chatcmpl-{uuid.uuid4().hex[:24]}"

    response = {
        "id": completion_id,
        "object": "chat.completion",
        "created": int(time.time()),
        "model": "eb1-delta-preview",
        "choices": [
            {
                "index": 0,
                "message": {
                    "role": "assistant",
                    "content": response_content,
                },
                "finish_reason": "stop",
            }
        ],
        "usage": {
            "prompt_tokens": aggregated_usage.prompt_tokens,
            "completion_tokens": aggregated_usage.completion_tokens,
            "total_tokens": aggregated_usage.total_tokens,
        },
        "_tier": routing_path.upper(),
        "_selected_model": actual_model,
        "_action_count": action_count,
        "_routing_path": routing_path,
    }

    return response


async def handle_eb1_responses(
    state: "ServerState",
    payload: dict[str, Any],
    request_id: str,
) -> dict[str, Any]:
    """Route and forward a Responses API request through the GNN."""
    from ember.eb1_gnn.executor import MODEL_ID_MAPPING, execute_responses

    start_time = time.perf_counter()

    requested_model = payload.get("model", "eb1-preview")
    if requested_model == "eb1-delta-preview":
        raise RequestValidationError(
            detail="Model 'eb1-delta-preview' is only supported on /v1/chat/completions",
            param="model",
        )
    input_content = payload.get("input", "")

    query = _extract_query_text(input_content)

    is_swe_model = requested_model.startswith("eb1-swe-")
    routing_result = None
    complexity_result = None

    if is_swe_model:
        from ember.takumi.eb1_swe import classify_complexity

        route_start = time.perf_counter()
        complexity, signals = classify_complexity(query)
        route_time_ms = (time.perf_counter() - route_start) * 1000

        variant = _swe_variant_from_model_id(requested_model)
        model_map = _resolve_swe_model_map(variant)

        routed_model = model_map.get(complexity, model_map["standard"])
        selected_model = _normalize_target_model(
            MODEL_ID_MAPPING.get(routed_model, routed_model)
        )
        complexity_result = (complexity, signals, variant)

        logger.info(
            "Request %s [responses/%s]: complexity=%s, model=%s, variant=%s, route=%.1fms",
            request_id,
            requested_model,
            complexity,
            selected_model,
            variant,
            route_time_ms,
        )

    elif requested_model in _ROUTED_EB1_RESPONSES_MODELS:
        route_start = time.perf_counter()
        routing_result = await _route_off_thread(state, query)
        route_time_ms = (time.perf_counter() - route_start) * 1000

        selected_model = _normalize_target_model(
            MODEL_ID_MAPPING.get(routing_result.selected_model, routing_result.selected_model)
        )

        logger.info(
            "Request %s [responses/%s]: tier=%s, model=%s, conf=%.3f, route=%.1fms",
            request_id,
            requested_model,
            routing_result.tier.name,
            selected_model,
            routing_result.signals.max_prob,
            route_time_ms,
        )
    else:
        selected_model = _normalize_target_model(
            MODEL_ID_MAPPING.get(requested_model, requested_model)
        )
        route_time_ms = 0.0
        routing_result = None

        logger.info(
            "Request %s [responses]: direct model=%s",
            request_id,
            selected_model,
        )

    if is_swe_model:
        _enrich_with_model_params(payload, selected_model)

    exec_start = time.perf_counter()
    response = await execute_responses(
        model=selected_model,
        payload=payload,
        gateway_url=state.config.gateway_url,
        session=state.session,
        timeout_seconds=_timeout_for_model(selected_model),
        request_id=request_id,
    )
    exec_time_ms = (time.perf_counter() - exec_start) * 1000

    total_time_ms = (time.perf_counter() - start_time) * 1000

    response["_route_time_ms"] = route_time_ms
    response["_exec_time_ms"] = exec_time_ms
    response["_total_time_ms"] = total_time_ms
    response["_selected_model"] = selected_model

    if complexity_result:
        complexity, signals, variant = complexity_result
        response["_complexity"] = complexity
        response["_signals"] = signals
        response["_variant"] = variant

    if routing_result:
        response["_tier"] = routing_result.tier.name
        response["_confidence"] = routing_result.signals.max_prob
        response["_top2_model"] = routing_result.signals.top2_model
        response["_logit_margin"] = routing_result.signals.logit_margin
        response["_logit_std"] = routing_result.signals.logit_std
        response["_mean_logit"] = routing_result.signals.mean_logit
        response["_entropy"] = routing_result.signals.entropy
        if hasattr(routing_result.signals, "all_probs") and routing_result.signals.all_probs:
            response["_distribution"] = routing_result.signals.all_probs

    if requested_model in _ROUTED_EB1_RESPONSES_MODELS or is_swe_model:
        response["model"] = requested_model

    logger.info(
        "Request %s [responses]: completed in %.1fms (route=%.1fms, exec=%.1fms)",
        request_id,
        total_time_ms,
        route_time_ms,
        exec_time_ms,
    )

    return response


async def handle_eb1_swe_preview(
    state: "ServerState",
    messages: list[dict],
    query: str,
    request_body: dict,
    request_id: str,
    variant: str = "balanced",
) -> dict[str, Any]:
    """Handle eb1-swe-preview with complexity-based routing."""
    from ember.eb1_gnn.executor import execute_via_gateway
    from ember.takumi.eb1_swe import classify_complexity

    start_time = time.perf_counter()

    complexity, signals = classify_complexity(query)

    model_map = _resolve_swe_model_map(variant)
    routed_model = model_map.get(complexity, model_map["standard"])
    selected_model = _normalize_target_model(routed_model)

    _enrich_with_model_params(request_body, selected_model)

    response = await execute_via_gateway(
        model=selected_model,
        messages=messages,
        request_body=request_body,
        gateway_url=state.config.gateway_url,
        session=state.session,
        timeout_seconds=_timeout_for_model(selected_model),
        request_id=request_id,
    )

    total_time_ms = (time.perf_counter() - start_time) * 1000
    model_name = f"eb1-swe-{variant}-preview" if variant != "balanced" else "eb1-swe-preview"

    response["model"] = model_name
    response["_complexity"] = complexity
    response["_selected_model"] = selected_model
    response["_signals"] = signals
    response["_variant"] = variant
    response["_total_time_ms"] = total_time_ms

    logger.info(
        "Request %s [%s]: complexity=%s, model=%s, %.1fms",
        request_id,
        model_name,
        complexity,
        selected_model,
        total_time_ms,
    )

    return response
