defmodule Microwaveprop.Weather.GefsProfileTest do
  use Microwaveprop.DataCase, async: true

  alias Microwaveprop.Weather
  alias Microwaveprop.Weather.GefsProfile

  @valid_time ~U[2026-04-18 18:00:00Z]

  defp base_attrs(overrides \\ %{}) do
    Map.merge(
      %{
        valid_time: @valid_time,
        lat: 32.9,
        lon: -97.0,
        run_time: ~U[2026-04-18 12:00:00Z],
        forecast_hour: 6,
        surface_temp_c: 20.0,
        surface_dewpoint_c: 10.0,
        surface_pressure_mb: 1013.0,
        pwat_mm: 20.0,
        wind_u_mps: 2.0,
        wind_v_mps: -1.0,
        cloud_cover_pct: 50.0,
        precip_mm: 0.0
      },
      overrides
    )
  end

  describe "changeset/2" do
    test "is valid with the minimum required fields" do
      cs = GefsProfile.changeset(%GefsProfile{}, base_attrs())
      assert cs.valid?
    end

    test "requires valid_time, lat, and lon" do
      cs = GefsProfile.changeset(%GefsProfile{}, %{})
      refute cs.valid?
      assert %{valid_time: _, lat: _, lon: _} = errors_on(cs)
    end

    test "accepts an optional pressure-level profile array" do
      profile = [%{"pres" => 1000.0, "tmpc" => 20.0, "dwpc" => 10.0, "hght" => 100.0}]
      cs = GefsProfile.changeset(%GefsProfile{}, base_attrs(%{profile: profile}))
      assert cs.valid?
      assert Ecto.Changeset.get_field(cs, :profile) == profile
    end
  end

  describe "Weather.upsert_gefs_profile/1" do
    test "inserts a new profile" do
      assert {:ok, %GefsProfile{} = profile} = Weather.upsert_gefs_profile(base_attrs())
      assert profile.lat == 32.9
      assert profile.surface_temp_c == 20.0
    end

    test "is a no-op on conflict (lat, lon, valid_time)" do
      {:ok, _} = Weather.upsert_gefs_profile(base_attrs())

      {:ok, second} =
        Weather.upsert_gefs_profile(base_attrs(%{surface_temp_c: 999.0}))

      # on_conflict: :nothing leaves the existing row alone; the returned
      # struct just carries the changeset values, but the DB keeps the old
      # record.
      persisted = Microwaveprop.Repo.get_by(GefsProfile, lat: 32.9, lon: -97.0, valid_time: @valid_time)
      assert persisted.surface_temp_c == 20.0
      assert second.lat == 32.9
    end

    test "rejects invalid attributes with a changeset error" do
      assert {:error, %Ecto.Changeset{valid?: false}} = Weather.upsert_gefs_profile(%{})
    end
  end

  describe "Weather.upsert_gefs_profiles_batch/1" do
    test "inserts many rows at once" do
      attrs =
        for fh <- [3, 6, 9] do
          valid = DateTime.add(~U[2026-04-18 12:00:00Z], fh * 3600, :second)
          base_attrs(%{valid_time: valid, forecast_hour: fh})
        end

      {count, _} = Weather.upsert_gefs_profiles_batch(attrs)
      assert count == 3

      rows = Microwaveprop.Repo.all(GefsProfile)
      assert length(rows) == 3
    end

    test "skips conflicting rows quietly" do
      attrs = [base_attrs()]
      {1, _} = Weather.upsert_gefs_profiles_batch(attrs)
      {0, _} = Weather.upsert_gefs_profiles_batch(attrs)

      assert Microwaveprop.Repo.aggregate(GefsProfile, :count, :id) == 1
    end
  end
end
