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

  alias Microwaveprop.Weather.IemClient

  describe "parse_asos_csv/1" do
    test "parses valid CSV rows including precipitation" do
      csv = """
      station,valid,tmpf,dwpf,relh,sknt,drct,mslp,alti,skyc1,p01i,wxcodes
      KDFW,2026-03-28 18:53,75.0,55.0,49.12,12,180,1013.2,29.92,SCT,0.03,RA
      KDFW,2026-03-28 19:53,76.0,54.0,46.00,10,190,1013.0,29.91,FEW,null,null
      """

      rows = IemClient.parse_asos_csv(csv)
      assert length(rows) == 2

      first = hd(rows)
      assert first.temp_f == 75.0
      assert first.dewpoint_f == 55.0
      assert first.relative_humidity == 49.12
      assert first.wind_speed_kts == 12.0
      assert first.wind_direction_deg == 180
      assert first.sea_level_pressure_mb == 1013.2
      assert first.altimeter_setting == 29.92
      assert first.sky_condition == "SCT"
      assert first.observed_at == ~U[2026-03-28 18:53:00Z]
      assert first.precip_1h_in == 0.03
      assert first.wx_codes == "RA"

      second = Enum.at(rows, 1)
      assert second.precip_1h_in == nil
      assert second.wx_codes == nil
    end

    test "handles null values in CSV" do
      csv = """
      station,valid,tmpf,dwpf,relh,sknt,drct,mslp,alti,skyc1,p01i,wxcodes
      KDFW,2026-03-28 18:53,null,null,null,null,null,null,null,null,null,null
      """

      rows = IemClient.parse_asos_csv(csv)
      assert length(rows) == 1

      first = hd(rows)
      assert first.temp_f == nil
      assert first.dewpoint_f == nil
      assert first.sky_condition == nil
      assert first.precip_1h_in == nil
      assert first.wx_codes == nil
    end

    test "skips comment lines and empty lines" do
      csv = """
      #DEBUG something
      station,valid,tmpf,dwpf,relh,sknt,drct,mslp,alti,skyc1,p01i,wxcodes
      KDFW,2026-03-28 18:53,75.0,55.0,49.0,12,180,1013.2,29.92,SCT,0.01,

      """

      rows = IemClient.parse_asos_csv(csv)
      assert length(rows) == 1
    end
  end

  describe "parse_raob_json/1" do
    test "parses valid RAOB JSON response" do
      json = %{
        "profiles" => [
          %{
            "station" => "KFWD",
            "valid" => "2026-03-28 12:00:00+00:00",
            "profile" => [
              %{"pres" => 1013.0, "hght" => 171.0, "tmpc" => 25.0, "dwpc" => 15.0, "drct" => 180.0, "sknt" => 10.0},
              %{"pres" => 925.0, "hght" => 800.0, "tmpc" => 20.0, "dwpc" => 12.0, "drct" => 200.0, "sknt" => 15.0}
            ]
          }
        ]
      }

      result = IemClient.parse_raob_json(json)
      assert length(result) == 1

      sounding = hd(result)
      assert sounding.observed_at == ~U[2026-03-28 12:00:00Z]
      assert length(sounding.profile) == 2
      assert hd(sounding.profile)["pres"] == 1013.0
    end

    test "returns empty list for empty profiles" do
      json = %{"profiles" => []}
      assert IemClient.parse_raob_json(json) == []
    end

    test "handles missing profile key gracefully" do
      json = %{}
      assert IemClient.parse_raob_json(json) == []
    end
  end

  describe "asos_url/3" do
    test "builds correct URL for date range" do
      url =
        IemClient.asos_url(
          "KDFW",
          ~U[2026-03-28 12:00:00Z],
          ~U[2026-03-28 18:00:00Z]
        )

      assert url =~ "station=KDFW"
      assert url =~ "year1=2026"
      assert url =~ "month1=3"
      assert url =~ "day1=28"
      assert url =~ "hour1=12"
      assert url =~ "year2=2026"
      assert url =~ "hour2=18"
      assert url =~ "format=onlycomma"
      assert url =~ "data=p01i"
      assert url =~ "data=wxcodes"
    end
  end

  describe "raob_url/2" do
    test "builds correct URL for sounding fetch" do
      url = IemClient.raob_url("FWD", ~U[2026-03-28 12:00:00Z])
      assert url =~ "station=FWD"
      assert url =~ "ts=202603281200"
    end
  end

  describe "network_url/1" do
    test "builds correct URL for state ASOS network" do
      url = IemClient.network_url("NY_ASOS")
      assert url == "https://mesonet.agron.iastate.edu/json/network.py?network=NY_ASOS"
    end

    test "builds correct URL for RAOB network" do
      url = IemClient.network_url("RAOB")
      assert url == "https://mesonet.agron.iastate.edu/json/network.py?network=RAOB"
    end
  end

  describe "fetch_network/1" do
    test "returns parsed stations on success" do
      Req.Test.stub(IemClient, fn conn ->
        Req.Test.json(conn, %{
          "stations" => [
            %{"id" => "KJFK", "name" => "JFK Intl", "lat" => 40.64, "lon" => -73.78}
          ]
        })
      end)

      assert {:ok, [station]} = IemClient.fetch_network("NY_ASOS")
      assert station.station_code == "KJFK"
      assert station.lat == 40.64
    end

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

      assert {:error, "IEM network HTTP 503"} = IemClient.fetch_network("NY_ASOS")
    end
  end

  describe "fetch_asos/3" do
    test "returns parsed observations on success" do
      csv = """
      station,valid,tmpf,dwpf,relh,sknt,drct,mslp,alti,skyc1,p01i,wxcodes
      KDFW,2026-03-28 18:53,75.0,55.0,49.12,12,180,1013.2,29.92,SCT,0.05,RA BR
      """

      Req.Test.stub(IemClient, fn conn ->
        Req.Test.text(conn, csv)
      end)

      assert {:ok, [obs]} =
               IemClient.fetch_asos("KDFW", ~U[2026-03-28 16:00:00Z], ~U[2026-03-28 20:00:00Z])

      assert obs.temp_f == 75.0
      assert obs.observed_at == ~U[2026-03-28 18:53:00Z]
      assert obs.precip_1h_in == 0.05
      assert obs.wx_codes == "RA BR"
    end

    test "returns error on non-200 status" do
      Req.Test.stub(IemClient, fn conn ->
        Plug.Conn.send_resp(conn, 500, "error")
      end)

      assert {:error, "IEM ASOS HTTP 500"} =
               IemClient.fetch_asos("KDFW", ~U[2026-03-28 16:00:00Z], ~U[2026-03-28 20:00:00Z])
    end
  end

  describe "fetch_raob/2" do
    test "returns parsed soundings on success" do
      Req.Test.stub(IemClient, fn conn ->
        Req.Test.json(conn, %{
          "profiles" => [
            %{
              "station" => "FWD",
              "valid" => "2026-03-28 12:00:00+00:00",
              "profile" => [
                %{"pres" => 1013.0, "hght" => 171.0, "tmpc" => 25.0, "dwpc" => 15.0, "drct" => 180.0, "sknt" => 10.0}
              ]
            }
          ]
        })
      end)

      assert {:ok, [sounding]} = IemClient.fetch_raob("FWD", ~U[2026-03-28 12:00:00Z])
      assert sounding.observed_at == ~U[2026-03-28 12:00:00Z]
      assert length(sounding.profile) == 1
    end

    test "returns error on non-200 status" do
      Req.Test.stub(IemClient, fn conn ->
        Plug.Conn.send_resp(conn, 404, "not found")
      end)

      assert {:error, "IEM RAOB HTTP 404"} = IemClient.fetch_raob("FWD", ~U[2026-03-28 12:00:00Z])
    end
  end

  describe "fetch_current_asos/1" do
    test "returns parsed observations from multiple networks" do
      Req.Test.stub(IemClient, fn conn ->
        Req.Test.json(conn, %{
          "data" => [
            %{
              "station" => "KDFW",
              "lat" => 32.9,
              "lon" => -97.0,
              "utc_valid" => "2026-03-28T18:00:00Z",
              "tmpf" => 75.0,
              "dwpf" => 55.0,
              "sknt" => 10,
              "skyc1" => "CLR",
              "mslp" => 1013.2,
              "alti" => 29.92,
              "phour" => 0.0
            }
          ]
        })
      end)

      assert {:ok, observations} = IemClient.fetch_current_asos(["TX_ASOS"])
      assert length(observations) == 1
      assert hd(observations).station_code == "KDFW"
      assert hd(observations).temp_f == 75.0
    end

    test "skips observations with missing core data" do
      Req.Test.stub(IemClient, fn conn ->
        Req.Test.json(conn, %{
          "data" => [
            %{"station" => "KDFW", "lat" => nil, "lon" => nil, "tmpf" => nil, "dwpf" => nil}
          ]
        })
      end)

      assert {:ok, []} = IemClient.fetch_current_asos(["TX_ASOS"])
    end
  end

  describe "parse_network_json/1" do
    test "parses station list from network JSON response" do
      json = %{
        "stations" => [
          %{
            "id" => "KJFK",
            "name" => "New York/JFK Intl",
            "lat" => 40.6399,
            "lon" => -73.7787
          },
          %{
            "id" => "KLGA",
            "name" => "New York/LaGuardia",
            "lat" => 40.7772,
            "lon" => -73.8726
          }
        ]
      }

      stations = IemClient.parse_network_json(json)
      assert length(stations) == 2

      first = hd(stations)
      assert first.station_code == "KJFK"
      assert first.name == "New York/JFK Intl"
      assert first.lat == 40.6399
      assert first.lon == -73.7787
    end

    test "returns empty list when no stations" do
      assert IemClient.parse_network_json(%{"stations" => []}) == []
    end

    test "returns empty list when stations key missing" do
      assert IemClient.parse_network_json(%{}) == []
    end

    test "handles stations with extra fields gracefully" do
      json = %{
        "stations" => [
          %{
            "id" => "KDFW",
            "name" => "Dallas/Fort Worth",
            "lat" => 32.897,
            "lon" => -97.038,
            "elevation" => 171.0,
            "state" => "TX",
            "country" => "US",
            "network" => "TX_ASOS"
          }
        ]
      }

      stations = IemClient.parse_network_json(json)
      assert length(stations) == 1

      station = hd(stations)
      assert station.station_code == "KDFW"
      assert station.lat == 32.897
      assert station.lon == -97.038
    end
  end

  describe "iemre_url/3" do
    test "builds correct URL for lat/lon and date" do
      url = IemClient.iemre_url(32.875, -97.0, ~D[2026-03-28])
      assert url == "https://mesonet.agron.iastate.edu/iemre/hourly/2026-03-28/32.875/-97.0/json"
    end
  end

  describe "parse_iemre_json/1" do
    test "extracts data array from JSON response" do
      json = %{
        "data" => [
          %{"utc_hour" => 0, "p01m_mm" => 0.0, "skyc_pct" => 25.0},
          %{"utc_hour" => 1, "p01m_mm" => 0.5, "skyc_pct" => 50.0}
        ]
      }

      result = IemClient.parse_iemre_json(json)
      assert length(result) == 2
      assert hd(result)["utc_hour"] == 0
    end

    test "returns empty list for missing data key" do
      assert IemClient.parse_iemre_json(%{}) == []
    end

    test "returns empty list for empty data array" do
      assert IemClient.parse_iemre_json(%{"data" => []}) == []
    end
  end

  describe "fetch_iemre/3" do
    test "returns parsed hourly data on success" do
      Req.Test.stub(IemClient, fn conn ->
        Req.Test.json(conn, %{
          "data" => [
            %{"utc_hour" => 0, "p01m_mm" => 0.0},
            %{"utc_hour" => 1, "p01m_mm" => 1.2}
          ]
        })
      end)

      assert {:ok, data} = IemClient.fetch_iemre(32.875, -97.0, ~D[2026-03-28])
      assert length(data) == 2
      assert Enum.at(data, 1)["p01m_mm"] == 1.2
    end

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

      assert {:error, "IEM IEMRE HTTP 503"} = IemClient.fetch_iemre(32.875, -97.0, ~D[2026-03-28])
    end

    test "returns error on 404" do
      Req.Test.stub(IemClient, fn conn ->
        Plug.Conn.send_resp(conn, 404, "not found")
      end)

      assert {:error, "IEM IEMRE HTTP 404"} = IemClient.fetch_iemre(32.875, -97.0, ~D[2026-03-28])
    end
  end
end
