defmodule ExDebugToolbar.ToolbarChannel do
  @moduledoc false

  use ExDebugToolbar.Web, :channel
  alias ExDebugToolbar.{ToolbarView, Endpoint, Logger, Config}
  alias ExDebugToolbar.View.Helpers.TimeHelpers
  alias Phoenix.View

  def join("toolbar:request:" <> request_id = topic, _params, socket) do
    ExDebugToolbar.Endpoint.subscribe(topic)
    case ExDebugToolbar.get_request(request_id) do
      {:ok, %{stopped?: true} = request} ->
        Logger.debug("Request is complete, rendering toolbar")
        {:ok, build_payload(request), socket}
      {:ok, _} ->
        Logger.debug("Request is still being processed, pending")
        {:ok, :pending, socket}
      {:error, reason} ->
        Logger.debug("Error getting request: #{reason}")
        {:error, %{reason: reason}}
    end
  end

  def handle_out("request:ready" = event, %{id: request_id}, socket) do
    {:ok, request} = ExDebugToolbar.get_request(request_id)
    push socket, event, build_payload(request)
    {:noreply, socket}
  end

  def broadcast_request(id \\ self()) do
    case ExDebugToolbar.get_request(id) do
      {:ok, request} ->
        topic = "toolbar:request:#{request.uuid}"
        Logger.debug("Broadcasting that request #{request.uuid} is ready")
        Endpoint.broadcast(topic, "request:ready", %{id: request.uuid})
      _ -> :error
    end
  end

  defp build_payload(request) do
    Logger.debug fn ->
      dump = inspect(request, pretty: true, safe: true, limit: :infinity)
      "Building paylod for request #{dump}"
    end
    {time, payload} = :timer.tc(fn -> do_build_payload(request) end)
    Logger.debug fn ->
      "Toolbar rendered in " <> TimeHelpers.native_time_to_string(time)
    end
    payload
  end

  defp do_build_payload(request) do
    %{
      html: View.render_to_string(ToolbarView, "show.html", request: request, history: history()),
      uuid: request.uuid,
      request: (if Config.debug?(), do: request, else: nil)
    }
  end

  defp history() do
    ExDebugToolbar.get_all_requests()
    |> Enum.sort(&(NaiveDateTime.compare(&2.created_at, &1.created_at) == :lt))
  end
end
