"""
Execute defquery / defaction definitions.

Unlike fragment handlers (which produce SX markup via ``async_eval_to_sx``),
query/action defs produce **data** (dicts, lists, scalars) that get
JSON-serialized by the calling blueprint.  Uses ``async_eval()`` with
the I/O primitive pipeline so ``(service ...)`` calls are awaited inline.
"""

from __future__ import annotations

from typing import Any

from .types import QueryDef, ActionDef, NIL


async def execute_query(query_def: QueryDef, params: dict[str, str]) -> Any:
    """Execute a defquery and return a JSON-serializable result.

    Parameters are bound from request query string args.
    """
    from .jinja_bridge import get_component_env, _get_request_context
    import os

    env = dict(get_component_env())
    env.update(query_def.closure)

    # Bind params from request args (try kebab-case and snake_case)
    for param in query_def.params:
        snake = param.replace("-", "_")
        val = params.get(param, params.get(snake, NIL))
        # Coerce type=int for common patterns
        if isinstance(val, str) and val.lstrip("-").isdigit():
            val = int(val)
        env[param] = val

    if os.environ.get("SX_USE_OCAML") == "1":
        from .ocaml_bridge import get_bridge
        from .parser import serialize, parse_all
        from .pages import _wrap_with_env
        bridge = await get_bridge()
        sx_text = _wrap_with_env(query_def.body, env)
        ctx = {"_helper_service": ""}
        raw = await bridge.eval(sx_text, ctx=ctx)
        if raw:
            parsed = parse_all(raw)
            result = parsed[0] if parsed else None
        else:
            result = None
        return _normalize(result)

    from .async_eval import async_eval

    ctx = _get_request_context()
    result = await async_eval(query_def.body, env, ctx)
    return _normalize(result)


async def execute_action(action_def: ActionDef, payload: dict[str, Any]) -> Any:
    """Execute a defaction and return a JSON-serializable result.

    Parameters are bound from the JSON request body.
    """
    from .jinja_bridge import get_component_env, _get_request_context
    import os

    env = dict(get_component_env())
    env.update(action_def.closure)

    # Bind params from JSON payload (try kebab-case and snake_case)
    for param in action_def.params:
        snake = param.replace("-", "_")
        val = payload.get(param, payload.get(snake, NIL))
        env[param] = val

    if os.environ.get("SX_USE_OCAML") == "1":
        from .ocaml_bridge import get_bridge
        from .parser import serialize, parse_all
        from .pages import _wrap_with_env
        bridge = await get_bridge()
        sx_text = _wrap_with_env(action_def.body, env)
        ctx = {"_helper_service": ""}
        raw = await bridge.eval(sx_text, ctx=ctx)
        if raw:
            parsed = parse_all(raw)
            result = parsed[0] if parsed else None
        else:
            result = None
        return _normalize(result)

    from .async_eval import async_eval

    ctx = _get_request_context()
    result = await async_eval(action_def.body, env, ctx)
    return _normalize(result)


def _normalize(value: Any) -> Any:
    """Ensure result is JSON-serializable (strip NIL, convert sets, etc)."""
    if value is NIL or value is None:
        return None
    if isinstance(value, set):
        return list(value)
    return value
