defmodule Microwaveprop.Workers.IonosphereFetchWorkerTest do
  use Microwaveprop.DataCase, async: false
  use Oban.Testing, repo: Microwaveprop.Repo

  alias Microwaveprop.Ionosphere
  alias Microwaveprop.Ionosphere.GiroClient
  alias Microwaveprop.Ionosphere.Observation
  alias Microwaveprop.Workers.IonosphereFetchWorker

  @sample File.read!("test/fixtures/giro/mhj45_sample.txt")

  describe "perform/1" do
    test "pulls each configured station and upserts the parsed rows" do
      Req.Test.stub(GiroClient, fn conn ->
        assert conn.method == "GET"
        params = Plug.Conn.fetch_query_params(conn).query_params
        assert params["ursiCode"] in ~w(MHJ45 AL945)

        Plug.Conn.resp(conn, 200, @sample)
      end)

      assert :ok = perform_job(IonosphereFetchWorker, %{})

      # 25 observations per station × 2 stations = 50 rows (fixture has 25
      # data lines; upserts go to each station code separately).
      assert Repo.aggregate(Observation, :count, :id) == 50
      assert Ionosphere.latest_observation("MHJ45")
      assert Ionosphere.latest_observation("AL945")
    end

    test "survives a station's fetch error and still ingests the others" do
      Req.Test.stub(GiroClient, fn conn ->
        params = Plug.Conn.fetch_query_params(conn).query_params

        if params["ursiCode"] == "MHJ45" do
          Plug.Conn.resp(conn, 500, "boom")
        else
          Plug.Conn.resp(conn, 200, @sample)
        end
      end)

      assert :ok = perform_job(IonosphereFetchWorker, %{})

      # MHJ45 failed, AL945 ingested.
      assert Ionosphere.latest_observation("MHJ45") == nil
      assert Ionosphere.latest_observation("AL945")
    end

    test "tolerates an HTTP transport failure with :ok + no rows inserted" do
      Req.Test.stub(GiroClient, fn conn ->
        Req.Test.transport_error(conn, :timeout)
      end)

      assert :ok = perform_job(IonosphereFetchWorker, %{})
      assert Repo.aggregate(Observation, :count, :id) == 0
    end
  end

  describe "stations/0" do
    test "exposes a non-empty list of CONUS ionosonde stations" do
      stations = IonosphereFetchWorker.stations()

      assert is_list(stations)
      assert length(stations) >= 2

      # Every station entry has the shape the fetcher expects, with a
      # plausible CONUS lat/lon envelope.
      for s <- stations do
        assert is_binary(s.code)
        assert is_binary(s.name)
        assert is_float(s.lat) or is_integer(s.lat)
        assert is_float(s.lon) or is_integer(s.lon)
        assert s.lat > 20.0 and s.lat < 75.0
        assert s.lon > -170.0 and s.lon < -60.0
      end
    end
  end

  describe "perform/1 additional HTTP branches" do
    test "tolerates an HTTP 404 with :ok and zero rows inserted" do
      # 404 is returned by GIRO for stations with no data in the window.
      # The worker should treat it as a per-station no-op and return :ok.
      Req.Test.stub(GiroClient, fn conn ->
        Plug.Conn.send_resp(conn, 404, "not found")
      end)

      assert :ok = perform_job(IonosphereFetchWorker, %{})
      assert Repo.aggregate(Observation, :count, :id) == 0
    end

    test "tolerates a non-tabular response body gracefully" do
      # A body that doesn't match the expected comment/columns format
      # should parse to zero rows — no crash, no inserts.
      Req.Test.stub(GiroClient, fn conn ->
        Plug.Conn.resp(conn, 200, "not a tabular response at all")
      end)

      assert :ok = perform_job(IonosphereFetchWorker, %{})
      assert Repo.aggregate(Observation, :count, :id) == 0
    end

    test "tolerates an empty-body 200 response" do
      Req.Test.stub(GiroClient, fn conn ->
        Plug.Conn.resp(conn, 200, "")
      end)

      assert :ok = perform_job(IonosphereFetchWorker, %{})
      assert Repo.aggregate(Observation, :count, :id) == 0
    end
  end
end
