defmodule Symbiont.LedgerTest do
  use ExUnit.Case, async: false
  import Symbiont.TestHelpers

  @moduletag :capture_log

  setup do
    tmp_dir = Path.join(System.tmp_dir!(), "symbiont_test_#{:rand.uniform(999_999)}")
    File.mkdir_p!(tmp_dir)

    safe_stop(Symbiont.Ledger)
    {:ok, _pid} = Symbiont.Ledger.start_link(data_dir: tmp_dir)

    on_exit(fn ->
      safe_stop(Symbiont.Ledger)
      File.rm_rf!(tmp_dir)
    end)

    %{tmp_dir: tmp_dir}
  end

  test "starts with empty ledger" do
    assert Symbiont.Ledger.recent() == []
    assert Symbiont.Ledger.stats() == %{
             "total_calls" => 0,
             "total_cost_estimated_usd" => 0.0,
             "by_model" => %{},
             "by_date" => %{}
           }
  end

  test "appending entries and reading them back" do
    Symbiont.Ledger.append(%{
      model: "haiku",
      success: true,
      input_tokens: 100,
      output_tokens: 50,
      estimated_cost_usd: 0.008,
      elapsed_seconds: 1.2
    })

    Process.sleep(50)

    entries = Symbiont.Ledger.recent()
    assert length(entries) == 1

    [entry] = entries
    assert entry["model"] == "haiku"
    assert entry["success"] == true
    assert entry["input_tokens"] == 100
    assert entry["estimated_cost_usd"] == 0.008
    assert entry["timestamp"] != nil
  end

  test "stats aggregate correctly across multiple entries" do
    entries = [
      %{model: "haiku", estimated_cost_usd: 0.008, success: true, input_tokens: 50, output_tokens: 25},
      %{model: "haiku", estimated_cost_usd: 0.006, success: true, input_tokens: 40, output_tokens: 20},
      %{model: "sonnet", estimated_cost_usd: 0.04, success: true, input_tokens: 200, output_tokens: 100},
      %{model: "opus", estimated_cost_usd: 0.15, success: true, input_tokens: 500, output_tokens: 300}
    ]

    Enum.each(entries, &Symbiont.Ledger.append/1)
    # Ensure all async casts are processed
    _ = Symbiont.Ledger.stats()
    Process.sleep(50)

    stats = Symbiont.Ledger.stats()
    assert stats["total_calls"] == 4
    assert stats["total_cost_estimated_usd"] == 0.204

    assert stats["by_model"]["haiku"]["calls"] == 2
    assert stats["by_model"]["sonnet"]["calls"] == 1
    assert stats["by_model"]["opus"]["calls"] == 1
  end

  test "recent limits the number of entries returned" do
    for i <- 1..10 do
      Symbiont.Ledger.append(%{model: "haiku", estimated_cost_usd: 0.001 * i, success: true})
    end

    Process.sleep(100)

    assert length(Symbiont.Ledger.recent(3)) == 3
    assert length(Symbiont.Ledger.recent(50)) == 10
  end

  test "ledger persists to JSONL file", %{tmp_dir: tmp_dir} do
    Symbiont.Ledger.append(%{model: "sonnet", estimated_cost_usd: 0.04, success: true})
    Process.sleep(50)

    path = Path.join(tmp_dir, "ledger.jsonl")
    content = File.read!(path)
    assert String.contains?(content, "sonnet")

    lines = content |> String.split("\n", trim: true)
    assert length(lines) == 1

    {:ok, decoded} = Jason.decode(hd(lines))
    assert decoded["model"] == "sonnet"
  end

end
