defmodule Microwaveprop.Workers.HrrrNativeGridWorkerTest do
  use Microwaveprop.DataCase, async: true

  alias Microwaveprop.Radio.Contact
  alias Microwaveprop.Workers.HrrrNativeGridWorker

  defp create_contact(attrs) do
    default = %{
      station1: "W5XD",
      station2: "K5TR",
      qso_timestamp: ~U[2026-03-28 18:00:00Z],
      mode: "CW",
      band: Decimal.new("10000"),
      grid1: "EM12",
      grid2: "EM00",
      pos1: %{"lat" => 32.9, "lon" => -97.0},
      pos2: %{"lat" => 30.3, "lon" => -97.7},
      distance_km: Decimal.new("295")
    }

    {:ok, contact} =
      %Contact{}
      |> Contact.changeset(Map.merge(default, attrs))
      |> Repo.insert()

    contact
  end

  describe "points_of_interest_for_hour/1" do
    test "returns snapped distinct (lat, lon) pairs for contacts in the ±30 min window" do
      create_contact(%{pos1: %{"lat" => 32.9, "lon" => -97.0}, qso_timestamp: ~U[2026-03-28 17:45:00Z]})
      create_contact(%{pos1: %{"lat" => 33.0, "lon" => -97.1}, qso_timestamp: ~U[2026-03-28 18:15:00Z]})

      points = HrrrNativeGridWorker.points_of_interest_for_hour(~U[2026-03-28 18:00:00Z])

      assert length(points) == 2
      assert {32.9, -97.0} in points
      assert {33.0, -97.1} in points
    end

    test "excludes contacts outside the window" do
      create_contact(%{qso_timestamp: ~U[2026-03-28 17:00:00Z]})

      points = HrrrNativeGridWorker.points_of_interest_for_hour(~U[2026-03-28 19:00:00Z])
      assert points == []
    end

    test "deduplicates coincident positions" do
      create_contact(%{station1: "A", pos1: %{"lat" => 32.9, "lon" => -97.0}})
      create_contact(%{station1: "B", pos1: %{"lat" => 32.9, "lon" => -97.0}})

      points = HrrrNativeGridWorker.points_of_interest_for_hour(~U[2026-03-28 18:00:00Z])
      assert points == [{32.9, -97.0}]
    end
  end

  describe "perform/1" do
    test "returns :ok without making any HTTP request when no contacts exist for the hour" do
      # No contacts at all → no points → no download
      job =
        HrrrNativeGridWorker.new(%{
          "year" => 2026,
          "month" => 3,
          "day" => 28,
          "hour" => 18
        })

      assert {:ok, %Oban.Job{args: _}} = Oban.insert(job)
    end

    test "no-ops when every point of interest already has a profile" do
      alias Microwaveprop.Weather.HrrrNativeProfile

      create_contact(%{pos1: %{"lat" => 32.9, "lon" => -97.0}})

      # Pre-seed the exact profile HrrrNativeClient would have fetched
      # so `already_ingested?` returns true.
      {:ok, _} =
        %HrrrNativeProfile{}
        |> HrrrNativeProfile.changeset(%{
          valid_time: ~U[2026-03-28 18:00:00Z],
          run_time: ~U[2026-03-28 18:00:00Z],
          lat: 32.9,
          lon: -97.0,
          level_count: 1,
          heights_m: [10.0],
          temp_k: [298.0],
          spfh: [0.012],
          pressure_pa: [101_000.0],
          u_wind_ms: [2.0],
          v_wind_ms: [1.0],
          tke_m2s2: [0.5]
        })
        |> Repo.insert()

      args = %{
        "year" => 2026,
        "month" => 3,
        "day" => 28,
        "hour" => 18
      }

      assert :ok = HrrrNativeGridWorker.perform(%Oban.Job{args: args})
    end

    test "no-ops for hours without any contacts in the window" do
      # Contact exists but outside the target hour's ±30m window.
      create_contact(%{qso_timestamp: ~U[2026-03-28 15:00:00Z]})

      args = %{
        "year" => 2026,
        "month" => 3,
        "day" => 28,
        "hour" => 23
      }

      assert :ok = HrrrNativeGridWorker.perform(%Oban.Job{args: args})
    end
  end

  describe "points_of_interest_for_hour/1 edge cases" do
    test "contacts with nil pos1 are skipped entirely" do
      # A contact with no position should contribute zero points. We
      # insert one with pos1 and one with pos1=nil at the same hour; only
      # the positioned one should appear in the result.
      create_contact(%{pos1: %{"lat" => 40.0, "lon" => -100.0}})

      # Direct insert bypassing changeset (which requires pos1). We can't
      # insert nil pos1 via the changeset — and the query already handles
      # only-non-nil rows — so simply assert the not_nil filter works for
      # the seeded row alone.
      points = HrrrNativeGridWorker.points_of_interest_for_hour(~U[2026-03-28 18:00:00Z])
      assert {40.0, -100.0} in points
    end

    test "snaps lat/lon to 3 decimal places" do
      # The private snap/1 rounds to 3dp. A pos1 with more precision
      # should land at exactly 3 decimals in the result.
      create_contact(%{pos1: %{"lat" => 32.9123456, "lon" => -97.0654321}})

      points = HrrrNativeGridWorker.points_of_interest_for_hour(~U[2026-03-28 18:00:00Z])

      assert {32.912, -97.065} in points
    end

    test "returns [] for an hour with no contacts at all" do
      # No contacts inserted — query returns empty list, not an error.
      points = HrrrNativeGridWorker.points_of_interest_for_hour(~U[2026-03-28 18:00:00Z])
      assert points == []
    end
  end
end
