defmodule Symbiont.API do
  @moduledoc """
  HTTP API for Symbiont — drop-in replacement for the Python FastAPI server.

  Endpoints:
    POST /task          — Submit and execute a task immediately
    POST /queue         — Add a task to the persistent queue
    GET  /status        — Health check + system overview
    GET  /ledger        — Recent inference calls
    GET  /ledger/stats  — Aggregate cost & usage
    GET  /health        — Simple liveness probe
  """
  use Plug.Router

  plug(Plug.Logger)
  plug(:match)
  plug(Plug.Parsers, parsers: [:json], json_decoder: Jason)
  plug(:dispatch)

  # -- POST /task --
  post "/task" do
    task = conn.body_params["task"]
    force_tier = conn.body_params["force_tier"]

    if is_nil(task) or task == "" do
      send_json(conn, 400, %{error: "missing 'task' field"})
    else
      opts = if force_tier, do: [force_tier: force_tier], else: []

      case Symbiont.Router.route_and_execute(task, opts) do
        {:ok, result} ->
          response = %{
            id: "task-#{System.system_time(:second)}",
            task: task,
            model: result.model,
            result: result.result,
            elapsed_seconds: result.elapsed_seconds,
            input_tokens: result.input_tokens,
            output_tokens: result.output_tokens,
            estimated_cost_usd: result.estimated_cost_usd,
            timestamp: DateTime.utc_now() |> DateTime.to_iso8601()
          }

          send_json(conn, 200, response)

        {:error, reason} ->
          send_json(conn, 500, %{error: inspect(reason)})
      end
    end
  end

  # -- POST /queue --
  post "/queue" do
    task = conn.body_params["task"]
    priority = conn.body_params["priority"] || "normal"

    if is_nil(task) or task == "" do
      send_json(conn, 400, %{error: "missing 'task' field"})
    else
      {:ok, task_id} = Symbiont.Queue.enqueue(task, priority)
      queue_size = Symbiont.Queue.size()

      send_json(conn, 200, %{
        id: task_id,
        status: "queued",
        position: queue_size
      })
    end
  end

  # -- GET /status --
  get "/status" do
    ledger_stats = Symbiont.Ledger.stats()
    queue_size = Symbiont.Queue.size()
    last_heartbeat = Symbiont.Heartbeat.last_snapshot()

    response = %{
      status: "healthy",
      runtime: "elixir/otp",
      queue_size: queue_size,
      last_heartbeat: last_heartbeat && last_heartbeat["timestamp"],
      total_calls: ledger_stats["total_calls"],
      total_cost_estimated_usd: ledger_stats["total_cost_estimated_usd"],
      by_model: ledger_stats["by_model"]
    }

    send_json(conn, 200, response)
  end

  # -- GET /ledger --
  get "/ledger" do
    entries = Symbiont.Ledger.recent(50)
    send_json(conn, 200, %{entries: entries, count: length(entries)})
  end

  # -- GET /ledger/stats --
  get "/ledger/stats" do
    stats = Symbiont.Ledger.stats()
    send_json(conn, 200, stats)
  end

  # -- GET /health --
  get "/health" do
    send_json(conn, 200, %{status: "ok", runtime: "elixir/otp"})
  end

  # -- Fallback --
  match _ do
    send_json(conn, 404, %{error: "not found"})
  end

  # -- Helpers --

  defp send_json(conn, status, body) do
    conn
    |> put_resp_content_type("application/json")
    |> send_resp(status, Jason.encode!(body))
  end
end
