defmodule MicrowavepropWeb.Plugs.RemoteIp do
  @moduledoc """
  Extracts the client IP from X-Forwarded-For (set by nginx/dokku proxy)
  and assigns it to conn.remote_ip and Logger metadata.
  """

  @behaviour Plug

  @impl true
  def init(opts), do: opts

  @impl true
  # Cloudflare sets Cf-Connecting-Ip with the real client IP.
  # Fall back to X-Forwarded-For for non-tunneled requests (dev, direct).
  @ip_headers ["cf-connecting-ip", "x-forwarded-for"]

  def call(conn, _opts) do
    ip =
      Enum.find_value(@ip_headers, fn header ->
        case Plug.Conn.get_req_header(conn, header) do
          [value | _] ->
            value
            |> String.split(",")
            |> hd()
            |> String.trim()
            |> parse_ip()

          _ ->
            nil
        end
      end)

    conn =
      if ip do
        %{conn | remote_ip: ip}
      else
        conn
      end

    Logger.metadata(remote_ip: format_ip(conn.remote_ip))
    conn
  end

  defp parse_ip(str) do
    case :inet.parse_address(String.to_charlist(str)) do
      {:ok, ip} -> ip
      _ -> nil
    end
  end

  defp format_ip({a, b, c, d}), do: "#{a}.#{b}.#{c}.#{d}"
  defp format_ip({a, b, c, d, e, f, g, h}), do: "#{a}:#{b}:#{c}:#{d}:#{e}:#{f}:#{g}:#{h}"
  defp format_ip(_), do: "unknown"
end
