defmodule AprsmeWeb.MobileUserSocketTest do
  # The rate limiter uses a process-wide ETS table; we're careful to use unique
  # IPs so tests don't interfere with each other, but we still mark async:false
  # to avoid clashing with anything else that flips the :aprsme, :env flag.
  use ExUnit.Case, async: false

  alias AprsmeWeb.MobileUserSocket

  defp unique_ip, do: "203.0.113." <> to_string(System.unique_integer([:positive]))

  setup do
    original_env = Application.get_env(:aprsme, :env)
    on_exit(fn -> Application.put_env(:aprsme, :env, original_env) end)
    :ok
  end

  describe "connect/3 in :test env" do
    test "always succeeds regardless of rate-limit key" do
      Application.put_env(:aprsme, :env, :test)
      socket = %Phoenix.Socket{assigns: %{}}

      connect_info = %{x_headers: [{"cf-connecting-ip", "198.51.100.1"}]}

      assert {:ok, result} = MobileUserSocket.connect(%{}, socket, connect_info)
      assert result.assigns.peer_ip == "198.51.100.1"
    end

    test "extracts IP from x-forwarded-for, splitting on comma" do
      Application.put_env(:aprsme, :env, :test)
      socket = %Phoenix.Socket{assigns: %{}}

      connect_info = %{
        x_headers: [{"x-forwarded-for", "198.51.100.5, 10.0.0.1"}]
      }

      assert {:ok, result} = MobileUserSocket.connect(%{}, socket, connect_info)
      assert result.assigns.peer_ip == "198.51.100.5"
    end

    test "extracts IP from x-real-ip" do
      Application.put_env(:aprsme, :env, :test)
      socket = %Phoenix.Socket{assigns: %{}}
      connect_info = %{x_headers: [{"x-real-ip", "198.51.100.7"}]}
      assert {:ok, result} = MobileUserSocket.connect(%{}, socket, connect_info)
      assert result.assigns.peer_ip == "198.51.100.7"
    end

    test "falls back to 'unknown' when x_headers list contains no known IP header" do
      Application.put_env(:aprsme, :env, :test)
      socket = %Phoenix.Socket{assigns: %{}}
      connect_info = %{x_headers: [{"user-agent", "curl"}]}
      assert {:ok, result} = MobileUserSocket.connect(%{}, socket, connect_info)
      assert result.assigns.peer_ip == "unknown"
    end

    test "uses peer_data when no x_headers" do
      Application.put_env(:aprsme, :env, :test)
      socket = %Phoenix.Socket{assigns: %{}}
      connect_info = %{peer_data: %{address: {127, 0, 0, 1}}}
      assert {:ok, result} = MobileUserSocket.connect(%{}, socket, connect_info)
      assert result.assigns.peer_ip == "127.0.0.1"
    end

    test "falls back to 'unknown' for empty connect_info" do
      Application.put_env(:aprsme, :env, :test)
      socket = %Phoenix.Socket{assigns: %{}}
      assert {:ok, result} = MobileUserSocket.connect(%{}, socket, %{})
      assert result.assigns.peer_ip == "unknown"
    end
  end

  describe "connect/3 in non-test env with rate limits" do
    test "allows first connection from an IP" do
      Application.put_env(:aprsme, :env, :prod)
      socket = %Phoenix.Socket{assigns: %{}}
      ip = unique_ip()
      connect_info = %{x_headers: [{"cf-connecting-ip", ip}]}

      assert {:ok, _} = MobileUserSocket.connect(%{}, socket, connect_info)
    end

    test "denies once the per-IP limit is exceeded" do
      Application.put_env(:aprsme, :env, :prod)
      socket = %Phoenix.Socket{assigns: %{}}
      ip = unique_ip()
      connect_info = %{x_headers: [{"cf-connecting-ip", ip}]}

      # The configured limit is 30 in-module; burn through it, then one more.
      for _ <- 1..30, do: MobileUserSocket.connect(%{}, socket, connect_info)
      assert :error == MobileUserSocket.connect(%{}, socket, connect_info)
    end
  end

  describe "id/1" do
    test "always returns nil (anonymous socket)" do
      assert MobileUserSocket.id(%Phoenix.Socket{assigns: %{}}) == nil
    end
  end
end
