lib/bb/mcp/resources/robot_state.ex

# SPDX-FileCopyrightText: 2026 James Harton
#
# SPDX-License-Identifier: Apache-2.0

defmodule BB.MCP.Resources.RobotState do
  @moduledoc """
  Current operational and safety state of a robot.

  Read via `bb://robots/{robot}/state`.
  """

  use Anubis.Server.Component,
    type: :resource,
    uri_template: "bb://robots/{robot}/state",
    name: "robot_state"

  alias Anubis.Server.Response
  alias BB.MCP.Resources
  alias BB.Robot.Runtime
  alias BB.Safety

  @impl true
  def description, do: "Current safety and operational state of the robot"

  @impl true
  def mime_type, do: "application/json"

  @impl true
  def read(params, frame) do
    case Resources.fetch_robot(params) do
      {:ok, module} ->
        payload = %{
          "safety_state" => Safety.state(module),
          "operational_state" => Runtime.operational_state(module),
          "executing" => Runtime.executing?(module),
          "executing_commands" => Enum.map(Runtime.executing_commands(module), &format_command/1)
        }

        {:reply, Response.json(Response.resource(), payload), frame}

      {:error, error} ->
        {:error, error, frame}
    end
  end

  defp format_command(%_{} = struct) do
    struct |> Map.from_struct() |> stringify_keys()
  end

  defp format_command(map) when is_map(map), do: stringify_keys(map)
  defp format_command(other), do: inspect(other)

  defp stringify_keys(map) when is_map(map) do
    Map.new(map, fn {k, v} -> {to_string(k), stringify_value(v)} end)
  end

  defp stringify_value(pid) when is_pid(pid), do: inspect(pid)
  defp stringify_value(value), do: value
end