defmodule MicrowavepropWeb.WeatherMapLiveTest do
  # async: false because we write to the shared ProfilesFile directory
  # which the WeatherMapLive reads at mount.
  use MicrowavepropWeb.ConnCase, async: false

  import Phoenix.LiveViewTest

  alias Microwaveprop.Propagation.ProfilesFile
  alias Microwaveprop.Weather.GridCache

  setup do
    GridCache.clear()

    on_exit(fn ->
      GridCache.clear()
      # Scrub any profile files our tests wrote so other async tests
      # that enumerate the ProfilesFile dir (e.g. latest_valid_time)
      # aren't affected.
      ProfilesFile.prune_older_than(~U[2099-12-31 23:59:59Z])
    end)

    :ok
  end

  defp write_profile(valid_time, lat, lon) do
    profile =
      for level <- [1000, 925, 850, 700], do: %{"pres" => level * 1.0, "tmpc" => 15.0, "dwpc" => 5.0}

    grid_data = %{
      {lat, lon} => %{
        surface_temp_c: 22.0,
        surface_dewpoint_c: 12.0,
        surface_pressure_mb: 1010.0,
        hpbl_m: 800.0,
        pwat_mm: 25.0,
        profile: profile
      }
    }

    ProfilesFile.write!(valid_time, grid_data)
  end

  describe "layer description in sidebar" do
    test "renders Group · Label header and description in the desktop sidebar", %{conn: conn} do
      {:ok, _lv, html} = live(conn, ~p"/weather")

      # Default layer is refractivity_gradient → group Surface, label N-Gradient.
      assert html =~ "Surface"
      assert html =~ "N-Gradient"
      assert html =~ "Minimum refractivity gradient"
    end
  end

  describe "forecast timeline" do
    test "mount pushes data-valid-times for every persisted profile", %{conn: conn} do
      base =
        DateTime.utc_now()
        |> DateTime.truncate(:second)
        |> Map.put(:minute, 0)
        |> Map.put(:second, 0)

      times = for h <- 0..2, do: DateTime.add(base, h * 3600, :second)
      # Seed a profile file at each forecast hour.
      Enum.each(times, &write_profile(&1, 33.0, -97.0))

      {:ok, _lv, html} = live(conn, ~p"/weather")

      # Timeline container exists.
      assert html =~ ~s(id="weather-forecast-timeline")

      # Each valid_time is present in the map element's data attribute.
      Enum.each(times, fn t ->
        iso = DateTime.to_iso8601(t)
        assert html =~ iso
      end)
    end

    test "mount defaults to the valid_time closest to now, not the latest forecast hour",
         %{conn: conn} do
      base =
        DateTime.utc_now()
        |> DateTime.truncate(:second)
        |> Map.put(:minute, 0)
        |> Map.put(:second, 0)

      # Seed f00 (now) and f11 (+11h). The worker walks forecast hours
      # sequentially so `latest_grid_valid_time` can point to f11 even
      # though the user expects the map to show "Now" by default.
      now_t = base
      future_t = DateTime.add(base, 11 * 3600, :second)

      write_profile(now_t, 33.0, -97.0)
      write_profile(future_t, 33.0, -97.0)

      {:ok, _lv, html} = live(conn, ~p"/weather")

      now_iso = DateTime.to_iso8601(now_t)
      future_iso = DateTime.to_iso8601(future_t)

      # The map element's data-selected-time attribute should be f00, not f11.
      assert html =~ ~s(data-selected-time="#{now_iso}")
      refute html =~ ~s(data-selected-time="#{future_iso}")
    end

    test "select_time replaces the rendered weather data with the new hour's grid", %{conn: conn} do
      base =
        DateTime.utc_now()
        |> DateTime.truncate(:second)
        |> Map.put(:minute, 0)
        |> Map.put(:second, 0)

      now_t = base
      future_t = DateTime.add(base, 3 * 3600, :second)

      # Seed f00 with a 22°C surface and f03 with a 10°C surface so we
      # can tell them apart in the push_event payload.
      write_profile(now_t, 33.0, -97.0)

      grid_data = %{
        {33.0, -97.0} => %{
          surface_temp_c: 10.0,
          surface_dewpoint_c: 5.0,
          surface_pressure_mb: 1010.0,
          hpbl_m: 500.0,
          pwat_mm: 20.0,
          profile: []
        }
      }

      ProfilesFile.write!(future_t, grid_data)

      {:ok, lv, _html} = live(conn, ~p"/weather")

      # Select the future hour.
      render_hook(lv, "select_time", %{"time" => DateTime.to_iso8601(future_t)})

      assert_push_event(lv, "update_weather", %{data: data})
      assert Enum.any?(data, fn r -> r.temperature == 10.0 end)
    end
  end
end
