defmodule Microwaveprop.Workers.GefsFetchWorkerTest do
  use Microwaveprop.DataCase, async: false

  alias Microwaveprop.Weather
  alias Microwaveprop.Weather.GefsProfile
  alias Microwaveprop.Workers.GefsFetchWorker

  describe "perform/1" do
    test "cancels on malformed (non-empty, not a seed) args" do
      args = %{"some_unrelated" => "garbage"}
      assert {:cancel, _} = GefsFetchWorker.perform(%Oban.Job{args: args})
    end

    test "cancels when run_hour is not a valid GEFS cycle" do
      args = %{
        "run_time" => "2026-04-18T05:00:00Z",
        "forecast_hour" => 24
      }

      assert {:cancel, _} = GefsFetchWorker.perform(%Oban.Job{args: args})
    end
  end

  describe "build_profile_attrs/3" do
    test "merges lat/lon/valid_time onto a GefsClient profile map" do
      run_time = ~U[2026-04-18 12:00:00Z]

      profile = %{
        lat: 32.9,
        lon: -97.0,
        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,
        profile: [%{"pres" => 1000.0, "tmpc" => 20.0, "dwpc" => 10.0, "hght" => 100.0}]
      }

      attrs = GefsFetchWorker.build_profile_attrs(run_time, 24, profile)
      assert attrs.run_time == run_time
      assert attrs.forecast_hour == 24
      assert attrs.valid_time == ~U[2026-04-19 12:00:00Z]
      assert attrs.lat == 32.9
      assert attrs.surface_temp_c == 20.0
    end

    test "derives SoundingParams-style refractivity metrics when profile is populated" do
      run_time = ~U[2026-04-18 12:00:00Z]

      profile = %{
        lat: 32.9,
        lon: -97.0,
        surface_temp_c: 20.0,
        surface_dewpoint_c: 15.0,
        surface_pressure_mb: 1013.0,
        pwat_mm: 25.0,
        profile: [
          %{"pres" => 1000.0, "tmpc" => 20.0, "dwpc" => 15.0, "hght" => 100.0},
          %{"pres" => 925.0, "tmpc" => 18.0, "dwpc" => 12.0, "hght" => 770.0},
          %{"pres" => 850.0, "tmpc" => 14.0, "dwpc" => 8.0, "hght" => 1500.0}
        ]
      }

      attrs = GefsFetchWorker.build_profile_attrs(run_time, 24, profile)
      assert is_float(attrs.surface_refractivity)
      assert is_float(attrs.min_refractivity_gradient)
      assert is_boolean(attrs.ducting_detected)
    end
  end

  describe "extended_horizon_hours/0" do
    test "spans f024 through f168 at 6-hour cadence (25 entries)" do
      hours = GefsFetchWorker.extended_horizon_hours()
      assert List.first(hours) == 24
      assert List.last(hours) == 168
      assert length(hours) == 25
      assert Enum.all?(hours, &(rem(&1, 6) == 0))
    end
  end

  describe "most_recent_available_run/1" do
    test "returns the run cycle 5 or more hours earlier" do
      # 15:00 UTC → 10:00 UTC → snaps to 06Z
      assert GefsFetchWorker.most_recent_available_run(~U[2026-04-18 15:00:00Z]) ==
               ~U[2026-04-18 06:00:00Z]
    end

    test "rolls across midnight when now is near 04Z" do
      # 04:00 UTC → 23:00 the day before → snaps to 18Z previous day
      assert GefsFetchWorker.most_recent_available_run(~U[2026-04-18 04:00:00Z]) ==
               ~U[2026-04-17 18:00:00Z]
    end
  end

  describe "scoring interop" do
    alias Microwaveprop.Propagation
    alias Microwaveprop.Weather.GefsClient

    test "a GEFS-shaped profile produces non-empty band scores" do
      raw = %{
        "TMP:2 m above ground" => 293.15,
        "RH:2 m above ground" => 50.0,
        "PRES:surface" => 101_325.0,
        "PWAT:entire atmosphere (considered as a single layer)" => 20.0,
        "UGRD:10 m above ground" => 3.0,
        "VGRD:10 m above ground" => -1.5,
        "TCDC:entire atmosphere" => 40.0,
        "APCP:surface" => 0.0,
        "TMP:1000 mb" => 293.15,
        "RH:1000 mb" => 50.0,
        "HGT:1000 mb" => 110.0,
        "TMP:925 mb" => 288.15,
        "RH:925 mb" => 40.0,
        "HGT:925 mb" => 780.0,
        "TMP:850 mb" => 283.15,
        "RH:850 mb" => 35.0,
        "HGT:850 mb" => 1500.0
      }

      scores = raw |> GefsClient.build_profile() |> Propagation.score_grid_point(~U[2026-04-20 18:00:00Z], 32.9, -97.0)

      assert is_list(scores)
      assert scores != []

      score = hd(scores)
      assert is_integer(score.band_mhz)
      assert is_integer(score.score)
      assert score.score >= 0
      assert score.score <= 100
    end
  end

  describe "storing profiles" do
    test "round-trips a full batch through the context" do
      run_time = ~U[2026-04-18 12:00:00Z]
      fh = 24
      valid_time = DateTime.add(run_time, fh * 3600, :second)

      batch = [
        %{
          run_time: run_time,
          forecast_hour: fh,
          valid_time: valid_time,
          lat: 32.9,
          lon: -97.0,
          surface_temp_c: 20.0,
          surface_dewpoint_c: 10.0,
          surface_pressure_mb: 1013.0,
          pwat_mm: 20.0,
          profile: []
        },
        %{
          run_time: run_time,
          forecast_hour: fh,
          valid_time: valid_time,
          lat: 33.0,
          lon: -97.0,
          surface_temp_c: 19.5,
          surface_dewpoint_c: 9.5,
          surface_pressure_mb: 1012.5,
          pwat_mm: 19.0,
          profile: []
        }
      ]

      {count, _} = Weather.upsert_gefs_profiles_batch(batch)
      assert count == 2

      persisted = Microwaveprop.Repo.all(GefsProfile)
      assert length(persisted) == 2
    end
  end
end
