defmodule CentralcloudStaff.RouterAgent do
  @moduledoc """
  Streams chat completions from any of the CentralCloud Hermes agents
  (OpenAI-compatible SSE).

  Sends messages to `caller_pid`:
    {:chunk, text}      — incremental token from the stream
    :stream_done        — stream finished cleanly
    {:stream_error, s}  — HTTP or network error
  """

  @agents %{
    "router" => %{
      name: "Dispatcher",
      description: "Routes to the right specialist",
      url: "http://router-agent.router-agent.svc:8642",
      model: "router-agent"
    },
    "incident" => %{
      name: "Incident Commander",
      description: "Server down, pod crash, node failure",
      url: "http://incident-commander.incident-commander.svc:8642",
      model: "incident-commander"
    },
    "ops" => %{
      name: "Ops Agent",
      description: "Oncall, billing, monitoring",
      url: "http://operations-agent.operations-agent.svc:8642",
      model: "operations-agent"
    },
    "comms" => %{
      name: "Comms Agent",
      description: "Customer notifications & status updates",
      url: "http://communications-agent.communications-agent.svc:8642",
      model: "communications-agent"
    }
  }

  @system_prompt """
  You are the CentralCloud Operations Assistant. Identify yourself only by your
  role (e.g. "Dispatcher", "Incident Commander", "Ops Agent", "Comms Agent").
  Never identify yourself as Hermes, Nous Research, or any underlying model.
  If asked who you are or what model you use, answer with your role only.
  """

  def agents, do: @agents
  def agent_keys, do: Map.keys(@agents) |> Enum.sort()
  def agent(key), do: Map.get(@agents, key) || Map.fetch!(@agents, "router")

  def stream_chat(messages, caller_pid, agent_key \\ "router") do
    agent = agent(agent_key)
    url = agent.url <> "/v1/chat/completions"
    key = api_key()

    body = %{
      model: agent.model,
      messages: [%{role: "system", content: @system_prompt} | messages],
      stream: true
    }

    Process.put(:sse_buf, "")

    result =
      Req.post(url,
        json: body,
        headers: [
          {"authorization", "Bearer #{key}"},
          {"accept", "text/event-stream"}
        ],
        receive_timeout: 120_000,
        connect_options: [timeout: 10_000],
        finch: CentralcloudStaff.Finch,
        into: fn {:data, data}, {req, resp} ->
          buf = Process.get(:sse_buf, "")
          {events, leftover} = split_events(buf <> data)
          Process.put(:sse_buf, leftover)
          Enum.each(events, &dispatch_event(&1, caller_pid))
          {:cont, {req, resp}}
        end
      )

    case result do
      {:ok, _} -> send(caller_pid, :stream_done)
      {:error, reason} -> send(caller_pid, {:stream_error, format_error(reason)})
    end
  end

  defp split_events(data) do
    parts = String.split(data, "\n\n")
    {complete, [tail]} = Enum.split(parts, length(parts) - 1)
    {complete, tail}
  end

  defp dispatch_event("", _pid), do: :ok

  defp dispatch_event(event, pid) do
    event
    |> String.split("\n")
    |> Enum.each(fn
      "data: [DONE]" ->
        :ok

      "data: " <> json ->
        with {:ok, payload} <- Jason.decode(json),
             content when is_binary(content) and content != "" <-
               get_in(payload, ["choices", Access.at(0), "delta", "content"]) do
          send(pid, {:chunk, content})
        else
          _ -> :ok
        end

      _ ->
        :ok
    end)
  end

  defp api_key do
    Application.get_env(:centralcloud_staff, :router_agent, [])
    |> Keyword.get(:api_key, "")
  end

  defp format_error(%{reason: reason}), do: inspect(reason)
  defp format_error(exception) when is_exception(exception), do: Exception.message(exception)
  defp format_error(other), do: inspect(other)
end
