lib/comm_bus/budget.ex

defmodule CommBus.Budget do
  @moduledoc "Token budget management."

  alias CommBus.Entry

  @doc """
  Selects entries that fit within a token budget using priority-based greedy selection.

  Entries are sorted by `(priority, weight, id)` in descending order, then
  accumulated until the token limit is reached. Entries whose `token_count`
  would exceed the remaining budget are skipped.

  ## Parameters

    - `entries` — List of `%CommBus.Entry{}` structs with `token_count` populated.
    - `limit` — Maximum number of tokens allowed.

  ## Returns

  A list of `%CommBus.Entry{}` structs that fit within the budget, preserving
  the priority-based selection order.
  """
  @spec fit_budget([Entry.t()], non_neg_integer()) :: [Entry.t()]
  def fit_budget(entries, limit) do
    entries
    |> Enum.sort_by(&sort_key/1, :desc)
    |> Enum.reduce({[], 0}, fn entry, {kept, total} ->
      entry_tokens = entry.token_count || 0

      if total + entry_tokens <= limit do
        {[entry | kept], total + entry_tokens}
      else
        {kept, total}
      end
    end)
    |> elem(0)
    |> Enum.reverse()
  end

  defp sort_key(%Entry{} = entry) do
    {entry.priority, entry.weight, entry.id || entry.content}
  end
end