defmodule Microwaveprop.Weather.UwyoSoundingClientTest do
  use ExUnit.Case, async: true

  alias Microwaveprop.Weather.UwyoSoundingClient

  @fixture File.read!("test/support/fixtures/uwyo_sounding_yyr.html")

  describe "sounding_url/2" do
    test "builds the correct URL for a 3-letter station code + datetime" do
      url = UwyoSoundingClient.sounding_url("YYR", ~U[2026-04-13 00:00:00Z])

      assert url ==
               "https://weather.uwyo.edu/cgi-bin/sounding" <>
                 "?region=naconf&TYPE=TEXT%3ALIST" <>
                 "&YEAR=2026&MONTH=04&FROM=1300&TO=1300&STNM=YYR"
    end

    test "pads month and day-hour to two digits" do
      url = UwyoSoundingClient.sounding_url("WSE", ~U[2026-01-05 12:00:00Z])

      assert url =~ "MONTH=01"
      assert url =~ "FROM=0512"
      assert url =~ "TO=0512"
      assert url =~ "STNM=WSE"
    end
  end

  describe "parse_sounding_html/1" do
    test "returns one sounding entry for a valid response" do
      assert [sounding] = UwyoSoundingClient.parse_sounding_html(@fixture)
      assert sounding.observed_at == ~U[2026-04-13 00:00:00Z]
      assert is_list(sounding.profile)
      assert length(sounding.profile) > 20
    end

    test "parses the surface level with all fields populated" do
      [sounding] = UwyoSoundingClient.parse_sounding_html(@fixture)
      surface = hd(sounding.profile)

      assert surface["pres"] == 1016.0
      assert surface["hght"] == 36.0
      assert surface["tmpc"] == -3.5
      assert surface["dwpc"] == -9.5
      assert surface["drct"] == 145.0
      assert surface["sknt"] == 1.0
    end

    test "each level has the same key set (pres, hght, tmpc, dwpc, drct, sknt)" do
      [sounding] = UwyoSoundingClient.parse_sounding_html(@fixture)

      Enum.each(sounding.profile, fn level ->
        assert Map.has_key?(level, "pres")
        assert Map.has_key?(level, "hght")
        assert Map.has_key?(level, "tmpc")
        assert Map.has_key?(level, "dwpc")
        assert Map.has_key?(level, "drct")
        assert Map.has_key?(level, "sknt")
      end)
    end

    test "levels with missing temp/dewpoint are omitted" do
      # Lines like ' 1000.0     92' (only PRES + HGHT, rest blank) must not
      # appear in the parsed profile because SoundingParams.derive/1 needs temp.
      html = """
      <H2>71119 WSE Edmonton Stony Plain Observations at 00Z 13 Apr 2026</H2>
      <PRE>
      -----------------------------------------------------------------------------
         PRES   HGHT   TEMP   DWPT   RELH   MIXR   DRCT   SKNT   THTA   THTE   THTV
          hPa     m      C      C      %    g/kg    deg   knot     K      K      K
      -----------------------------------------------------------------------------
       1000.0     92
        920.0    766    2.2   -3.8     65   3.15     85      6  282.0  291.1  282.5
      </PRE>
      """

      [sounding] = UwyoSoundingClient.parse_sounding_html(html)
      assert length(sounding.profile) == 1
      assert hd(sounding.profile)["pres"] == 920.0
    end

    test "returns an empty list when the response contains no PRE block" do
      html = "Sorry, the server is too busy to process your request.\n"
      assert UwyoSoundingClient.parse_sounding_html(html) == []
    end

    test "returns an empty list when the header is missing" do
      html = "<HTML><PRE> 1000.0 30 25.0 20.0 50 0.0 180 10 0 0 0</PRE></HTML>"
      assert UwyoSoundingClient.parse_sounding_html(html) == []
    end
  end

  describe "fetch_sounding/2" do
    test "returns parsed sounding on a 200 response" do
      Req.Test.stub(UwyoSoundingClient, fn conn ->
        Req.Test.text(conn, @fixture)
      end)

      assert {:ok, [sounding]} =
               UwyoSoundingClient.fetch_sounding("YYR", ~U[2026-04-13 00:00:00Z])

      assert sounding.observed_at == ~U[2026-04-13 00:00:00Z]
      refute sounding.profile == []
    end

    test "returns an empty list when the server is busy" do
      Req.Test.stub(UwyoSoundingClient, fn conn ->
        Req.Test.text(conn, "Sorry, the server is too busy to process your request.\n")
      end)

      assert {:ok, []} = UwyoSoundingClient.fetch_sounding("YYR", ~U[2026-04-13 00:00:00Z])
    end

    test "returns an error on a non-200 response" do
      Req.Test.stub(UwyoSoundingClient, fn conn ->
        Plug.Conn.send_resp(conn, 503, "Service Unavailable")
      end)

      assert {:error, "UWYO sounding HTTP 503"} =
               UwyoSoundingClient.fetch_sounding("YYR", ~U[2026-04-13 00:00:00Z])
    end
  end
end
