defmodule MicrowavepropWeb.PathLiveTest do
  use MicrowavepropWeb.ConnCase, async: false

  import Phoenix.LiveViewTest

  alias Microwaveprop.Ionosphere
  alias Microwaveprop.Propagation
  alias Microwaveprop.Propagation.ScoreCache
  alias Microwaveprop.Terrain.ElevationClient

  describe "propagation_updated handler" do
    setup do
      ScoreCache.clear()

      # Flat-terrain stub so compute_path can build a result without hitting
      # the real elevation API.
      Req.Test.stub(ElevationClient, fn conn ->
        params = Plug.Conn.fetch_query_params(conn).query_params
        lat_count = params["latitude"] |> String.split(",") |> length()
        Req.Test.json(conn, %{"elevation" => List.duplicate(200.0, lat_count)})
      end)

      on_exit(fn -> ScoreCache.clear() end)
      :ok
    end

    test "re-reads and re-renders the forecast when new scores arrive", %{conn: conn} do
      # Path midpoint is (33.0, -97.0) — the point point_forecast queries.
      {mid_lat, mid_lon} = {33.0, -97.0}

      # Seed three hourly valid_times with a LOW score of 30.
      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)

      Enum.each(times, fn t ->
        Propagation.replace_scores(
          [%{lat: mid_lat, lon: mid_lon, valid_time: t, band_mhz: 10_000, score: 30, factors: nil}],
          t
        )
      end)

      {:ok, lv, _html} =
        live(conn, ~p"/path?source=32.5,-97.0&destination=33.5,-97.0&band=10000")

      initial = render(lv)
      assert initial =~ "Propagation Forecast"
      assert initial =~ ~r/Best:.*?<span[^>]*>\s*30\s*<\/span>/s

      # Publish a new forecast: same times, new score of 80.
      Enum.each(times, fn t ->
        Propagation.replace_scores(
          [%{lat: mid_lat, lon: mid_lon, valid_time: t, band_mhz: 10_000, score: 80, factors: nil}],
          t
        )
      end)

      send(lv.pid, {:propagation_updated, times})

      refreshed = render(lv)
      assert refreshed =~ ~r/Best:.*?<span[^>]*>\s*80\s*<\/span>/s
      refute refreshed =~ ~r/Best:.*?<span[^>]*>\s*30\s*<\/span>/s
    end
  end

  describe "ionosphere panel" do
    setup do
      Req.Test.stub(ElevationClient, fn conn ->
        params = Plug.Conn.fetch_query_params(conn).query_params
        lat_count = params["latitude"] |> String.split(",") |> length()
        Req.Test.json(conn, %{"elevation" => List.duplicate(200.0, lat_count)})
      end)

      :ok
    end

    test "renders Es panel with live foEs and score when nearest station has fresh data", %{conn: conn} do
      # Seed Millstone Hill with an extreme-Es observation (foEs = 18 MHz).
      recent = DateTime.truncate(DateTime.utc_now(), :second)

      {:ok, _} =
        Ionosphere.upsert_observations("MHJ45", [
          %{valid_time: recent, fo_es_mhz: 18.0, fo_f2_mhz: 9.0, mufd_mhz: 28.0}
        ])

      # 52.6N, -71.5W to 32.6N, -71.5W → 2220 km pure N-S across
      # Millstone Hill's latitude (midpoint 42.6N).
      {:ok, lv, _} = live(conn, ~p"/path?source=52.6,-71.5&destination=32.6,-71.5&band=144")

      html = render(lv)
      assert html =~ "Ionosphere"
      assert html =~ "MHJ45"
      assert html =~ "foEs"
      assert html =~ ~r/18\.\d/
    end

    test "renders 'tropo only' notice when path is outside the single-hop Es window", %{conn: conn} do
      recent = DateTime.truncate(DateTime.utc_now(), :second)

      {:ok, _} =
        Ionosphere.upsert_observations("MHJ45", [
          %{valid_time: recent, fo_es_mhz: 18.0, fo_f2_mhz: 9.0, mufd_mhz: 28.0}
        ])

      # ~100 km path — way under the 500 km Es minimum.
      {:ok, lv, _} = live(conn, ~p"/path?source=42.6,-71.5&destination=43.5,-71.5&band=144")

      html = render(lv)
      assert html =~ "Ionosphere"
      assert html =~ ~r/out of range|not applicable|tropo only/i
    end

    test "omits the Es panel entirely when nearest ionosonde has no recent data", %{conn: conn} do
      {:ok, lv, _} = live(conn, ~p"/path?source=42.6,-71.5&destination=32.6,-71.5&band=144")

      html = render(lv)
      refute html =~ "Ionosphere"
    end
  end
end
