defmodule Mix.Tasks.Weather.RebatchAsosTest do
  use Microwaveprop.DataCase, async: false

  alias Microwaveprop.Weather.IemClient
  alias Microwaveprop.Weather.RebatchAsos
  alias Microwaveprop.Workers.WeatherFetchWorker

  setup do
    # Oban runs inline in tests, so the batch job the task inserts
    # actually executes. Stub IEM so that execution is a quiet no-op
    # (stations don't exist, so the batch handler just logs "missing"
    # and returns :ok).
    Req.Test.stub(IemClient, fn conn ->
      Req.Test.text(conn, "#DEBUG\nstation,valid,tmpf\n")
    end)

    :ok
  end

  # Oban runs `testing: :inline` in this project, so Oban.insert! would
  # execute the job immediately. We stash raw Oban.Job rows so we can
  # assert on the queue state the rebatch task would see in prod.
  defp insert_asos_job(station_id, station_code, start_dt, end_dt) do
    changeset =
      WeatherFetchWorker.new(%{
        "fetch_type" => "asos",
        "station_id" => station_id,
        "station_code" => station_code,
        "start_dt" => start_dt,
        "end_dt" => end_dt
      })

    # Force state=available so the task picks it up; Oban.Job defaults
    # to :available already but we set it explicitly for clarity.
    changeset
    |> Ecto.Changeset.put_change(:state, "available")
    |> Repo.insert!()
  end

  defp pending_count_by_type do
    import Ecto.Query

    from(j in Oban.Job,
      where: j.state in ["available", "scheduled", "retryable"],
      select: {fragment("?->>'fetch_type'", j.args), count(j.id)},
      group_by: fragment("?->>'fetch_type'", j.args)
    )
    |> Repo.all()
    |> Map.new()
  end

  test "collapses N single-station jobs sharing a window into one batch" do
    insert_asos_job(Ecto.UUID.generate(), "KDFW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")
    insert_asos_job(Ecto.UUID.generate(), "KFTW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")
    insert_asos_job(Ecto.UUID.generate(), "KAFW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")

    output = ExUnit.CaptureIO.capture_io(fn -> RebatchAsos.run([]) end)

    assert output =~ "3 pending `asos` jobs across 1 time windows"
    assert output =~ "inserted 1 batch jobs"
    assert output =~ "cancelled 3 single-station jobs"
  end

  test "groups per (start_dt, end_dt) window" do
    insert_asos_job(Ecto.UUID.generate(), "KDFW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")
    insert_asos_job(Ecto.UUID.generate(), "KFTW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")
    insert_asos_job(Ecto.UUID.generate(), "KAUS", "2026-03-29T10:00:00Z", "2026-03-29T14:00:00Z")

    output = ExUnit.CaptureIO.capture_io(fn -> RebatchAsos.run([]) end)

    assert output =~ "3 pending `asos` jobs across 2 time windows"
    assert output =~ "inserted 2 batch jobs"
  end

  test "dry-run does not mutate the queue" do
    insert_asos_job(Ecto.UUID.generate(), "KDFW", "2026-03-28T16:00:00Z", "2026-03-28T20:00:00Z")

    ExUnit.CaptureIO.capture_io(fn -> RebatchAsos.run(dry_run: true) end)

    counts = pending_count_by_type()
    assert counts["asos"] == 1
    assert counts["asos_batch"] in [nil, 0]
  end

  test "no pending asos jobs — short-circuits" do
    output = ExUnit.CaptureIO.capture_io(fn -> RebatchAsos.run([]) end)
    assert output =~ "Nothing to do"
  end
end
