defmodule AprsmeWeb.Live.SharedPacketHandlerTest do
  use ExUnit.Case, async: true

  alias AprsmeWeb.Live.SharedPacketHandler

  defp socket, do: %Phoenix.LiveView.Socket{assigns: %{__changed__: %{}}}

  defp allow_all, do: fn _packet, _socket -> true end
  defp allow_none, do: fn _packet, _socket -> false end

  defp record_process(ref),
    do: fn packet, s ->
      send(ref, {:processed, packet})
      {:noreply, s}
    end

  describe "handle_packet_update/3" do
    test "skips processing when filter returns false" do
      socket = socket()
      {result, ^socket} = {nil, socket}
      _ = result

      assert {:noreply, ^socket} =
               SharedPacketHandler.handle_packet_update(
                 %{sender: "K5ABC"},
                 socket,
                 filter_fn: allow_none(),
                 process_fn: record_process(self())
               )

      refute_received {:processed, _}
    end

    test "processes and enriches packet when filter returns true" do
      {:noreply, _} =
        SharedPacketHandler.handle_packet_update(
          %{sender: "K5ABC"},
          socket(),
          filter_fn: allow_all(),
          process_fn: record_process(self())
        )

      assert_received {:processed, packet}
      # Enrichment added nil device_* keys even for unknown identifier.
      assert Map.has_key?(packet, :device_model)
    end

    test "skips enrichment when enrich_packet: false" do
      {:noreply, _} =
        SharedPacketHandler.handle_packet_update(
          %{sender: "K5ABC"},
          socket(),
          filter_fn: allow_all(),
          process_fn: record_process(self()),
          enrich_packet: false
        )

      assert_received {:processed, packet}
      refute Map.has_key?(packet, :device_model)
    end
  end

  describe "packet_matches_callsign?/2" do
    test "matches on atom :sender key" do
      assert SharedPacketHandler.packet_matches_callsign?(%{sender: "K5ABC"}, "K5ABC")
    end

    test "matches on string 'sender' key" do
      assert SharedPacketHandler.packet_matches_callsign?(%{"sender" => "K5ABC"}, "K5ABC")
    end

    test "string key wins over atom key when both present" do
      # The implementation prefers the string key via Map.get fallback ordering.
      assert SharedPacketHandler.packet_matches_callsign?(
               %{"sender" => "K5ABC", sender: "OTHER"},
               "K5ABC"
             )
    end

    test "comparison is case-insensitive via Callsign.matches?" do
      assert SharedPacketHandler.packet_matches_callsign?(%{sender: "k5abc"}, "K5ABC")
    end

    test "returns false when mismatched" do
      refute SharedPacketHandler.packet_matches_callsign?(%{sender: "K5ABC"}, "W1XYZ")
    end
  end

  describe "has_weather_data?/1" do
    test "returns true when at least one weather field is present" do
      assert SharedPacketHandler.has_weather_data?(%{temperature: 72.0})
      assert SharedPacketHandler.has_weather_data?(%{"humidity" => 50})
    end

    test "returns false when no weather fields are present" do
      refute SharedPacketHandler.has_weather_data?(%{sender: "K5ABC"})
      refute SharedPacketHandler.has_weather_data?(%{})
    end
  end

  describe "enrich_with_device_info/1" do
    test "adds nil device_* keys when no device identifier" do
      result = SharedPacketHandler.enrich_with_device_info(%{sender: "K5ABC"})
      assert result.device_model == nil
      assert result.device_vendor == nil
      assert result.device_contact == nil
      assert result.device_class == nil
    end

    test "still adds nil device_* when device_identifier is empty string" do
      result = SharedPacketHandler.enrich_with_device_info(%{device_identifier: ""})
      assert result.device_model == nil
    end
  end

  describe "enrich_packets_with_device_info/1" do
    test "enriches each packet in the list" do
      packets = [
        %{sender: "A", device_identifier: nil},
        %{sender: "B", device_identifier: ""},
        %{sender: "C", device_identifier: "APXYZ"}
      ]

      enriched = SharedPacketHandler.enrich_packets_with_device_info(packets)
      assert length(enriched) == 3
      assert Enum.all?(enriched, &Map.has_key?(&1, :device_model))
    end

    test "handles empty list" do
      assert SharedPacketHandler.enrich_packets_with_device_info([]) == []
    end
  end

  describe "filter factories" do
    test "callsign_filter matches packets by sender" do
      f = SharedPacketHandler.callsign_filter("K5ABC")
      assert f.(%{sender: "K5ABC"}, :any)
      refute f.(%{sender: "W1XYZ"}, :any)
    end

    test "callsign_and_weather_filter requires both" do
      f = SharedPacketHandler.callsign_and_weather_filter("K5ABC")
      refute f.(%{sender: "K5ABC"}, :any)
      refute f.(%{sender: "OTHER", temperature: 72.0}, :any)
      assert f.(%{sender: "K5ABC", temperature: 72.0}, :any)
    end
  end
end
