defmodule Microwaveprop.Weather.MrmsCacheTest do
  # async: false — MrmsCache is started at the application level and owns a
  # named ETS table. Tests mutate shared state and must not race.
  use ExUnit.Case, async: false

  alias Microwaveprop.Weather.MrmsCache

  @topic "mrms:cache"
  @pubsub Microwaveprop.PubSub

  setup do
    MrmsCache.clear()
    :ok
  end

  describe "fetch/0" do
    test "returns :miss when nothing is cached" do
      assert MrmsCache.fetch() == :miss
    end

    test "returns {:ok, valid_time, grid} after put/2" do
      valid_time = ~U[2026-04-12 19:24:00Z]
      grid = %{{32.0, -97.0} => 1.2, {32.125, -97.0} => 0.0}

      assert :ok = MrmsCache.put(valid_time, grid)
      assert {:ok, ^valid_time, ^grid} = MrmsCache.fetch()
    end

    test "put/2 overwrites the single current entry" do
      first_vt = ~U[2026-04-12 19:24:00Z]
      second_vt = ~U[2026-04-12 19:26:00Z]

      MrmsCache.put(first_vt, %{{32.0, -97.0} => 1.0})
      MrmsCache.put(second_vt, %{{32.0, -97.0} => 2.0})

      assert {:ok, ^second_vt, %{{32.0, -97.0} => 2.0}} = MrmsCache.fetch()
    end
  end

  describe "valid_time/0" do
    test "returns nil when cache is empty" do
      assert MrmsCache.valid_time() == nil
    end

    test "returns the cached valid_time when present" do
      vt = ~U[2026-04-12 19:24:00Z]
      MrmsCache.put(vt, %{{32.0, -97.0} => 5.4})

      assert MrmsCache.valid_time() == vt
    end
  end

  describe "broadcast_put/2" do
    test "inserts locally and broadcasts on the mrms:cache topic" do
      Phoenix.PubSub.subscribe(@pubsub, @topic)

      vt = ~U[2026-04-12 19:28:00Z]
      grid = %{{32.0, -97.0} => 3.3}

      assert :ok = MrmsCache.broadcast_put(vt, grid)
      assert {:ok, ^vt, ^grid} = MrmsCache.fetch()

      # The cache subscribes to its own topic, so we should observe the
      # message via our own subscription too.
      assert_receive {:mrms_cache_refresh, ^vt, ^grid}
    end
  end

  describe "clear/0" do
    test "empties the cache" do
      MrmsCache.put(~U[2026-04-12 19:24:00Z], %{{32.0, -97.0} => 1.0})
      assert {:ok, _, _} = MrmsCache.fetch()

      assert :ok = MrmsCache.clear()
      assert MrmsCache.fetch() == :miss
      assert MrmsCache.valid_time() == nil
    end
  end

  describe "peer broadcast handling" do
    test "applies refreshes received from peers on the PubSub topic" do
      # Simulate a peer node pushing a refresh. The cache GenServer is
      # subscribed to @topic in init/1; broadcasting directly exercises the
      # handle_info({:mrms_cache_refresh, ...}) clause.
      vt = ~U[2026-04-12 19:30:00Z]
      grid = %{{32.0, -97.0} => 7.7}

      Phoenix.PubSub.broadcast(@pubsub, @topic, {:mrms_cache_refresh, vt, grid})

      # Round-trip through the MrmsCache GenServer mailbox: send it a sync
      # call so we know the previous broadcast has been processed.
      _ = :sys.get_state(MrmsCache)

      assert {:ok, ^vt, ^grid} = MrmsCache.fetch()
    end
  end
end
