defmodule CentralcloudOps.RouterAgent do
  @moduledoc """
  Streams chat completions from the router-agent via the OpenAI-compatible SSE API.

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

  @default_url "http://router-agent.router-agent.svc:8642"

  def stream_chat(messages, caller_pid) do
    url = base_url() <> "/v1/chat/completions"
    key = api_key()

    body = %{
      model: "hermes-agent",
      messages: messages,
      stream: true
    }

    # Use process dictionary for SSE line buffering across chunks
    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: CentralcloudOps.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

  # Split on SSE event boundary (\n\n); return {complete_events, incomplete_tail}
  defp split_events(data) do
    parts = String.split(data, "\n\n")
    {complete, [tail]} = Enum.split(parts, length(parts) - 1)
    {complete, tail}
  end

  # Parse a single SSE event (may contain multiple lines)
  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 base_url do
    config(:url, @default_url)
  end

  defp api_key do
    config(:api_key, "")
  end

  defp config(key, default) do
    Application.get_env(:centralcloud_ops, :router_agent, [])
    |> Keyword.get(key, default)
  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
