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

  alias Microwaveprop.Weather
  alias Microwaveprop.Weather.HrrrProfile
  alias Microwaveprop.Workers.HrrrFetchWorker

  @sample_profile [
    %{"pres" => 1000.0, "hght" => 110.0, "tmpc" => 25.0, "dwpc" => 18.0},
    %{"pres" => 975.0, "hght" => 350.0, "tmpc" => 23.0, "dwpc" => 16.0},
    %{"pres" => 950.0, "hght" => 590.0, "tmpc" => 21.0, "dwpc" => 14.0}
  ]

  @sample_client_result %{
    surface_temp_c: 25.0,
    surface_dewpoint_c: 18.0,
    surface_pressure_mb: 1013.0,
    hpbl_m: 1500.0,
    pwat_mm: 25.0,
    run_time: ~U[2026-03-28 18:00:00Z],
    profile: @sample_profile
  }

  describe "perform/1" do
    test "skips if HRRR profile already exists" do
      Weather.upsert_hrrr_profile(%{
        valid_time: ~U[2026-03-28 18:00:00Z],
        lat: 32.90,
        lon: -97.04,
        profile: @sample_profile
      })

      job =
        HrrrFetchWorker.new(%{
          "lat" => 32.90,
          "lon" => -97.04,
          "valid_time" => "2026-03-28T18:00:00Z"
        })

      assert :ok = HrrrFetchWorker.perform(%Oban.Job{args: job.changes.args})
    end

    test "stores profile when client returns success" do
      # We test the perform logic by mocking at the module level
      # The actual HTTP calls are tested in HrrrClientTest
      # Here we verify the worker's coordination logic

      lat = 32.90
      lon = -97.04
      valid_time = ~U[2026-03-28 18:00:00Z]

      # Verify no profile exists initially
      refute Weather.has_hrrr_profile?(lat, lon, valid_time)

      # Simulate what perform does after a successful fetch
      attrs = build_profile_attrs(lat, lon, valid_time, @sample_client_result)
      {:ok, profile} = Weather.upsert_hrrr_profile(attrs)

      assert profile.lat == lat
      assert profile.lon == lon
      assert profile.hpbl_m == 1500.0
      assert profile.surface_temp_c == 25.0
      assert Weather.has_hrrr_profile?(lat, lon, valid_time)
    end

    test "rounds lat/lon before checking existence" do
      # Profile at rounded coordinates
      Weather.upsert_hrrr_profile(%{
        valid_time: ~U[2026-03-28 18:00:00Z],
        lat: 32.90,
        lon: -97.04,
        profile: []
      })

      # Worker called with slightly different coords that round to same
      job =
        HrrrFetchWorker.new(%{
          "lat" => 32.904,
          "lon" => -97.039,
          "valid_time" => "2026-03-28T18:00:00Z"
        })

      # Should skip because rounded coords match
      assert :ok = HrrrFetchWorker.perform(%Oban.Job{args: job.changes.args})

      # Only one profile should exist
      count = Repo.aggregate(HrrrProfile, :count)
      assert count == 1
    end
  end

  describe "perform/1 batch mode" do
    test "skips all points when profiles already exist" do
      Weather.upsert_hrrr_profile(%{
        valid_time: ~U[2026-03-28 18:00:00Z],
        lat: 32.90,
        lon: -97.04,
        profile: @sample_profile
      })

      job = %Oban.Job{
        args: %{
          "points" => [%{"lat" => 32.90, "lon" => -97.04}],
          "valid_time" => "2026-03-28T18:00:00Z"
        }
      }

      assert :ok = HrrrFetchWorker.perform(job)
    end
  end

  describe "backoff/1" do
    test "returns exponential backoff capped at 6 hours" do
      assert HrrrFetchWorker.backoff(%Oban.Job{attempt: 1}) == 120
      assert HrrrFetchWorker.backoff(%Oban.Job{attempt: 2}) == 240
      assert HrrrFetchWorker.backoff(%Oban.Job{attempt: 10}) <= 21_600
    end
  end

  describe "permanent_failure?/1" do
    test "cancels on 404 idx" do
      job = %Oban.Job{
        args: %{
          "lat" => 32.90,
          "lon" => -97.04,
          "valid_time" => "1995-06-15T12:00:00Z"
        }
      }

      Req.Test.stub(Microwaveprop.Weather.HrrrClient, fn conn ->
        Plug.Conn.send_resp(conn, 404, "not found")
      end)

      assert {:cancel, "HRRR idx HTTP 404"} = HrrrFetchWorker.perform(job)
    end
  end

  defp build_profile_attrs(lat, lon, valid_time, client_result) do
    %{
      valid_time: valid_time,
      lat: lat,
      lon: lon,
      run_time: client_result.run_time,
      profile: client_result.profile,
      hpbl_m: client_result.hpbl_m,
      pwat_mm: client_result.pwat_mm,
      surface_temp_c: client_result.surface_temp_c,
      surface_dewpoint_c: client_result.surface_dewpoint_c,
      surface_pressure_mb: client_result.surface_pressure_mb
    }
  end
end
