///|
/// An optional string field as the wire carries it: the value itself, or
/// `null`.
///
/// Not the field's own `ToJson` — MoonBit encodes `Some(v)` as the one-element
/// array `[v]`, which every decoder's string lookup rejects, silently turning a
/// present field into an absent one.
fn or_null(value : String?) -> Json {
  match value {
    Some(value) => Json(value)
    None => Json::null()
  }
}

///|
/// The event as one line's worth of JSON: its `"event"` name plus its payload,
/// flat.
///
/// This is the wire shape, and the only place it is written. `emit` logs this;
/// the desktop host forwards it for a compaction its engine died before
/// reporting. Two writers of the same event must not spell it two ways — they
/// did once, and nothing noticed until a decoder was asked to be exhaustive.
///
/// Flat, not nested under a payload key, because `@xlog`'s handler hoists an
/// entry's structured fields to the top level; `parse` reads them from there.
pub fn Event::to_json(self : Event) -> Json {
  match self {
    AgentSetupFailed(error~) =>
      { "event": "agent_setup_failed", "error": error }
    AgentStep(step~) => { "event": "agent_step", "step": step }
    AgentAborted(reason~) => { "event": "agent_aborted", "reason": reason }
    AgentFinished(answer~) => { "event": "agent_finished", "answer": answer }
    MaxStepsExhausted => { "event": "max_steps_exhausted" }
    TurnFailed(error~) => { "event": "turn_failed", "error": error }
    AssistantDelta(content~) =>
      { "event": "assistant_delta", "content": content }
    ReasoningDelta(content~) =>
      { "event": "reasoning_delta", "content": content }
    AssistantMessage(content~) =>
      { "event": "assistant_message", "content": content }
    ReasoningMessage(content~) =>
      { "event": "reasoning_message", "content": content }
    Usage(usage~) => { "event": "usage", "usage": ToJson::to_json(usage) }
    ToolResult(tool_call_id~, tool_name~, is_error~, content~, brief~) =>
      {
        "event": "tool_result",
        "tool_call_id": tool_call_id,
        "tool_name": tool_name,
        "is_error": is_error,
        "content": content,
        "brief": or_null(brief),
      }
    ToolCallDecodeError(tool_call_id~, tool_name~, error~) =>
      {
        "event": "tool_call_decode_error",
        "tool_call_id": tool_call_id,
        "tool_name": tool_name,
        "error": error,
      }
    ApprovalRequested(id~, tool_name~, detail~, body~) =>
      {
        "event": "approval_requested",
        "id": id,
        "tool_name": tool_name,
        "detail": detail,
        "body": or_null(body),
      }
    ApprovalResolved(id~, outcome~) =>
      { "event": "approval_resolved", "id": id, "outcome": outcome }
    SteerApplied(kind~, content~) =>
      { "event": "steer_applied", "kind": kind, "content": content }
    SteerDropped(content~) => { "event": "steer_dropped", "content": content }
    BackgroundNotice(content~) =>
      { "event": "background_notice", "content": content }
    GoalUpdated(goal~) => { "event": "goal_updated", "goal": or_null(goal) }
    GoalBlocked(reason~) => { "event": "goal_blocked", "reason": reason }
    GoalUnblocked => { "event": "goal_unblocked" }
    GoalCheck(content~) => { "event": "goal_check", "content": content }
    GoalReminder(content~) => { "event": "goal_reminder", "content": content }
    GoalContinue(remaining~) =>
      { "event": "goal_continue", "remaining": remaining }
    GoalBudgetExhausted(turns~) =>
      { "event": "goal_budget_exhausted", "turns": turns }
    PlanReminder(content~) => { "event": "plan_reminder", "content": content }
    CompactionStarted(from_sequence~, to_sequence~) =>
      {
        "event": "compaction_started",
        "from_sequence": from_sequence,
        "to_sequence": to_sequence,
      }
    CompactionFinished(from_sequence~, to_sequence~, summary~) =>
      {
        "event": "compaction_finished",
        "from_sequence": from_sequence,
        "to_sequence": to_sequence,
        "summary": summary,
      }
    CompactionFailed(error~) => { "event": "compaction_failed", "error": error }
    SubrunStarted(id~, kind~, label~) =>
      { "event": "subrun_started", "id": id, "kind": kind, "label": label }
    SubrunFinished(id~, status~, steps~, prompt_tokens~, completion_tokens~) =>
      {
        "event": "subrun_finished",
        "id": id,
        "status": status,
        "steps": steps,
        "prompt_tokens": prompt_tokens,
        "completion_tokens": completion_tokens,
      }
    AutoCompactionStarted(from_sequence~, to_sequence~) =>
      {
        "event": "auto_compaction_started",
        "from_sequence": from_sequence,
        "to_sequence": to_sequence,
      }
    AutoCompactionFinished(from_sequence~, to_sequence~, summary~) =>
      {
        "event": "auto_compaction_finished",
        "from_sequence": from_sequence,
        "to_sequence": to_sequence,
        "summary": summary,
      }
    AutoCompactionFailed(error~) =>
      { "event": "auto_compaction_failed", "error": error }
    ContextYield(to_sequence~, answer~) =>
      { "event": "context_yield", "to_sequence": to_sequence, "answer": answer }
    SessionStarted(session~, session_root~, workspace_root~) =>
      {
        "event": "session_started",
        "session": session,
        "session_root": session_root,
        "workspace_root": or_null(workspace_root),
      }
    SessionError(error~) => { "event": "session_error", "error": error }
    WorkspaceCreated(dir~) => { "event": "workspace_created", "dir": dir }
    CommandError(error~) => { "event": "command_error", "error": error }
    FleetStarted(runs~, task~) =>
      { "event": "fleet_started", "runs": runs, "task": task }
    McpConfigIgnored(reason~) =>
      { "event": "mcp_config_ignored", "reason": reason }
    McpConfigUnreadable(path~, error~) =>
      { "event": "mcp_config_unreadable", "path": path, "error": error }
    McpConfigInvalid(path~, error~) =>
      { "event": "mcp_config_invalid", "path": path, "error": error }
    McpToolsRegistered(servers~, tools~, names~) =>
      {
        "event": "mcp_tools_registered",
        "servers": servers,
        "tools": tools,
        "names": names.to_json(),
      }
    McpToolDuplicate(server~, tool~) =>
      { "event": "mcp_tool_duplicate", "server": server, "tool": tool }
    McpToolRenamed(from~, to~) =>
      { "event": "mcp_tool_renamed", "from": from, "to": to }
    McpToolsCapped(server~, kept~) =>
      { "event": "mcp_tools_capped", "server": server, "kept": kept }
    McpServerSkippedOverCap(server~) =>
      { "event": "mcp_server_skipped_over_cap", "server": server }
    McpConnectFailed(server~, error~) =>
      {
        "event": "mcp_connect_failed",
        "server": server,
        "error": or_null(error),
      }
    McpListToolsFailed(server~, error~) =>
      { "event": "mcp_list_tools_failed", "server": server, "error": error }
    McpListToolsTimeout(server~) =>
      { "event": "mcp_list_tools_timeout", "server": server }
    McpNoTools(server~) => { "event": "mcp_no_tools", "server": server }
  }
}