defmodule ToweropsWeb.Plugs.RemoteIpLogger do
  @moduledoc """
  Plug that extracts the remote IP address and adds it to Logger metadata.

  When behind a proxy/load balancer (like Traefik in Kubernetes), the real client IP
  is in the X-Forwarded-For or X-Real-IP headers. This plug extracts the IP and
  adds it to Logger metadata so it appears in all logs for the request.
  """

  import Plug.Conn

  require Logger

  def init(opts), do: opts

  def call(conn, _opts) do
    remote_ip = get_remote_ip(conn)

    # Add to Logger metadata so it appears in all logs for this request
    Logger.metadata(remote_ip: remote_ip)

    conn
  end

  defp get_remote_ip(conn) do
    # Try X-Forwarded-For first (set by Traefik and other proxies)
    case get_req_header(conn, "x-forwarded-for") do
      [forwarded | _] ->
        # X-Forwarded-For can be a comma-separated list; take the first (original client)
        forwarded
        |> String.split(",")
        |> List.first()
        |> String.trim()

      [] ->
        get_fallback_ip(conn)
    end
  end

  defp get_fallback_ip(conn) do
    # Try X-Real-IP header
    case get_req_header(conn, "x-real-ip") do
      [real_ip | _] ->
        real_ip

      [] ->
        format_remote_ip(conn.remote_ip)
    end
  end

  defp format_remote_ip({a, b, c, d}), do: "#{a}.#{b}.#{c}.#{d}"
  defp format_remote_ip({a, b, c, d, e, f, g, h}), do: format_ipv6({a, b, c, d, e, f, g, h})

  defp format_ipv6({a, b, c, d, e, f, g, h}) do
    Enum.map_join([a, b, c, d, e, f, g, h], ":", &Integer.to_string(&1, 16))
  end
end
