defmodule ToweropsWeb.Plugs.RemoteIpLoggerTest do
  use ToweropsWeb.ConnCase, async: true

  alias ToweropsWeb.Plugs.RemoteIpLogger

  require Logger

  describe "init/1" do
    test "returns options unchanged" do
      assert RemoteIpLogger.init([]) == []
      assert RemoteIpLogger.init(some: :option) == [some: :option]
    end
  end

  describe "call/2" do
    test "extracts IP from X-Forwarded-For header" do
      build_conn()
      |> put_req_header("x-forwarded-for", "203.0.113.195")
      |> RemoteIpLogger.call([])

      assert Logger.metadata()[:remote_ip] == "203.0.113.195"
    end

    test "extracts first IP from comma-separated X-Forwarded-For" do
      build_conn()
      |> put_req_header("x-forwarded-for", "203.0.113.195, 10.0.0.1, 192.168.1.1")
      |> RemoteIpLogger.call([])

      assert Logger.metadata()[:remote_ip] == "203.0.113.195"
    end

    test "trims whitespace from X-Forwarded-For IP" do
      build_conn()
      |> put_req_header("x-forwarded-for", "  203.0.113.195  , 10.0.0.1")
      |> RemoteIpLogger.call([])

      assert Logger.metadata()[:remote_ip] == "203.0.113.195"
    end

    test "falls back to X-Real-IP when X-Forwarded-For is not present" do
      build_conn()
      |> put_req_header("x-real-ip", "198.51.100.42")
      |> RemoteIpLogger.call([])

      assert Logger.metadata()[:remote_ip] == "198.51.100.42"
    end

    test "formats IPv4 from conn.remote_ip when no proxy headers present" do
      # ConnCase builds a conn with remote_ip {127, 0, 0, 1}
      RemoteIpLogger.call(build_conn(), [])

      assert Logger.metadata()[:remote_ip] == "127.0.0.1"
    end

    test "formats IPv6 from conn.remote_ip" do
      build_conn()
      |> Map.put(:remote_ip, {8193, 3512, 0, 0, 0, 0, 0, 1})
      |> RemoteIpLogger.call([])

      # IPv6 formatted as hex segments separated by colons (uppercase)
      assert Logger.metadata()[:remote_ip] == "2001:DB8:0:0:0:0:0:1"
    end

    test "prefers X-Forwarded-For over X-Real-IP" do
      build_conn()
      |> put_req_header("x-forwarded-for", "203.0.113.195")
      |> put_req_header("x-real-ip", "198.51.100.42")
      |> RemoteIpLogger.call([])

      assert Logger.metadata()[:remote_ip] == "203.0.113.195"
    end

    test "returns conn unchanged" do
      original_conn = build_conn()
      result_conn = RemoteIpLogger.call(original_conn, [])

      # Conn should pass through unchanged (only metadata is modified)
      assert result_conn.adapter == original_conn.adapter
      assert result_conn.method == original_conn.method
      assert result_conn.path_info == original_conn.path_info
    end
  end
end
