defmodule Forum.PublicSiteRouter do
  @moduledoc """
  Serves one network's public website on its own port.
  """
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, opts) do
    network = Keyword.fetch!(opts, :network)
    started_at = System.monotonic_time(:microsecond)

    response =
      with {:ok, pid} <- Forum.PublicSiteSupervisor.get_or_start(network) do
        Forum.PublicSite.serve(pid, %{
          method: conn.method,
          path: conn.request_path,
          query_string: conn.query_string,
          headers: conn.req_headers
        })
      else
        {:error, reason} ->
          %{
            status: 500,
            content_type: "text/plain",
            body: "Unable to start public site: #{inspect(reason)}"
          }
      end

    duration_us = System.monotonic_time(:microsecond) - started_at

    Forum.LogStore.add(%{
      "source_ip" => remote_ip(conn),
      "host" => conn.host || "",
      "network" => network,
      "method" => conn.method,
      "path" => conn.request_path,
      "query_string" => conn.query_string,
      "status" => response.status,
      "duration_ms" => Float.round(duration_us / 1_000, 2)
    })

    conn
    |> put_resp_content_type(response.content_type)
    |> send_resp(response.status, response.body)
  end

  defp remote_ip(conn) do
    case Plug.Conn.get_req_header(conn, "x-real-ip") do
      [ip | _] -> ip
      [] -> fallback_ip(conn)
    end
  end

  defp fallback_ip(%{remote_ip: remote_ip}) when is_tuple(remote_ip) do
    remote_ip |> :inet.ntoa() |> to_string()
  end

  defp fallback_ip(_), do: ""
end
