defmodule AprsmeWeb.MapLive.MovementTest do
  use AprsmeWeb.ConnCase, async: false

  import Mox
  import Phoenix.LiveViewTest

  alias Aprsme.GeoUtils

  setup :verify_on_exit!

  describe "GPS drift filtering" do
    setup do
      stub(Aprsme.PacketsMock, :get_recent_packets, fn _args -> [] end)
      :ok
    end

    test "does not update marker for GPS drift", %{conn: conn} do
      {:ok, view, _html} = live(conn, "/", on_error: :warn)

      assert render_hook(
               view,
               "update_map_state",
               %{"center" => %{"lat" => 33.16961, "lng" => -96.4921}, "zoom" => 9}
             )

      bounds_params = %{
        "bounds" => %{
          "north" => 33.18,
          "south" => 33.16,
          "east" => -96.48,
          "west" => -96.50
        }
      }

      assert render_hook(
               view,
               "bounds_changed",
               bounds_params
             )

      initial_packet = %{
        id: "TEST-1",
        sender: "TEST-1",
        base_callsign: "TEST",
        lat: 33.16961,
        lon: -96.4921,
        has_position: true,
        received_at: DateTime.utc_now()
      }

      send(view.pid, {:postgres_packet, initial_packet})

      drift_packet = %{
        id: "TEST-1",
        sender: "TEST-1",
        base_callsign: "TEST",
        lat: 33.169655,
        lon: -96.4921,
        has_position: true,
        received_at: DateTime.utc_now()
      }

      send(view.pid, {:postgres_packet, drift_packet})

      Phoenix.LiveViewTest.render(view)

      refute Phoenix.LiveViewTest.refute_push_event(
               view,
               "new_packet",
               %{id: "TEST-1"},
               50
             )
    end

    test "updates marker for significant movement", %{conn: conn} do
      {:ok, view, _html} = live(conn, "/?z=15", on_error: :warn)

      assert render_hook(view, "map_ready", %{})

      bounds_params = %{
        "bounds" => %{
          "north" => 33.18,
          "south" => 33.16,
          "east" => -96.48,
          "west" => -96.50
        }
      }

      assert render_hook(
               view,
               "bounds_changed",
               bounds_params
             )

      Phoenix.LiveViewTest.render(view)
      flush_push_events(view)

      initial_packet = %{
        id: "TEST-2",
        sender: "TEST-2",
        base_callsign: "TEST",
        lat: 33.16961,
        lon: -96.4921,
        has_position: true,
        received_at: DateTime.utc_now()
      }

      send(view.pid, {:postgres_packet, initial_packet})

      Phoenix.LiveViewTest.render(view)
      flush_push_events(view)

      moved_packet = %{
        id: "TEST-2",
        sender: "TEST-2",
        base_callsign: "TEST",
        lat: 33.1698,
        lon: -96.4921,
        has_position: true,
        received_at: DateTime.utc_now()
      }

      send(view.pid, {:postgres_packet, moved_packet})

      Phoenix.LiveViewTest.render(view)
    end

    defp flush_push_events(view) do
      receive do
        {ref, {:push_event, _, _}} when is_reference(ref) ->
          flush_push_events(view)
      after
        0 -> :ok
      end
    end
  end

  describe "distance calculations" do
    test "calculates correct distances for GPS drift scenarios" do
      lat1 = 33.16961
      lon1 = -96.4921

      lat2 = 33.169615
      lon2 = -96.492095
      distance = GeoUtils.haversine_distance(lat1, lon1, lat2, lon2)
      assert distance < 10

      lat3 = 33.16965
      lon3 = -96.4921
      distance2 = GeoUtils.haversine_distance(lat1, lon1, lat3, lon3)
      assert distance2 < 10

      lat4 = 33.17000
      lon4 = -96.4921
      distance3 = GeoUtils.haversine_distance(lat1, lon1, lat4, lon4)
      assert distance3 > 20
    end
  end
end
