defmodule MicrowavepropWeb.Plugs.RemoteIpTest do
  use ExUnit.Case, async: true

  import Plug.Conn
  import Plug.Test

  alias MicrowavepropWeb.Plugs.RemoteIp

  defp call_plug(conn) do
    RemoteIp.call(conn, RemoteIp.init([]))
  end

  describe "call/2" do
    test "extracts IP from cf-connecting-ip header" do
      conn =
        :get
        |> conn("/")
        |> put_req_header("cf-connecting-ip", "1.2.3.4")
        |> call_plug()

      assert conn.remote_ip == {1, 2, 3, 4}
    end

    test "falls back to x-forwarded-for when no cf-connecting-ip" do
      conn =
        :get
        |> conn("/")
        |> put_req_header("x-forwarded-for", "5.6.7.8")
        |> call_plug()

      assert conn.remote_ip == {5, 6, 7, 8}
    end

    test "prefers cf-connecting-ip over x-forwarded-for" do
      conn =
        :get
        |> conn("/")
        |> put_req_header("cf-connecting-ip", "1.2.3.4")
        |> put_req_header("x-forwarded-for", "5.6.7.8")
        |> call_plug()

      assert conn.remote_ip == {1, 2, 3, 4}
    end

    test "takes first IP from comma-separated x-forwarded-for" do
      conn =
        :get
        |> conn("/")
        |> put_req_header("x-forwarded-for", "10.0.0.1, 10.0.0.2, 10.0.0.3")
        |> call_plug()

      assert conn.remote_ip == {10, 0, 0, 1}
    end

    test "preserves original remote_ip when no headers present" do
      original_ip = {127, 0, 0, 1}

      conn =
        :get
        |> conn("/")
        |> Map.put(:remote_ip, original_ip)
        |> call_plug()

      assert conn.remote_ip == original_ip
    end

    test "handles IPv6 addresses" do
      conn =
        :get
        |> conn("/")
        |> put_req_header("cf-connecting-ip", "::1")
        |> call_plug()

      assert conn.remote_ip == {0, 0, 0, 0, 0, 0, 0, 1}
    end

    test "ignores malformed IP addresses" do
      original_ip = {127, 0, 0, 1}

      conn =
        :get
        |> conn("/")
        |> Map.put(:remote_ip, original_ip)
        |> put_req_header("cf-connecting-ip", "not-an-ip")
        |> call_plug()

      assert conn.remote_ip == original_ip
    end
  end
end
