defmodule TarakanWeb.LeaderboardLive do
  use TarakanWeb, :live_view

  alias Tarakan.Leaderboard
  alias Tarakan.Profiles

  @sorts %{
    "reputation" => :reputation,
    "reviews" => :reviews,
    "findings" => :findings,
    "verdicts" => :verdicts
  }

  @severities ~w(critical high medium low info)
  @windows %{"7" => 7, "30" => 30, "90" => 90, "all" => :all}

  @impl true
  def mount(_params, _session, socket) do
    {:ok,
     socket
     |> assign(:page_title, "Leaderboard")
     |> assign(
       :meta_description,
       "Who's putting work on the public record: reports, findings, checks."
     )
     |> assign(:canonical_path, ~p"/leaderboard")}
  end

  @impl true
  def handle_params(params, _uri, socket) do
    sort = if is_map_key(@sorts, params["sort"]), do: params["sort"], else: "reputation"
    severity = if params["severity"] in @severities, do: params["severity"], else: nil
    window = if is_map_key(@windows, params["window"]), do: params["window"], else: "all"

    {:noreply,
     socket
     |> assign(:sort, sort)
     |> assign(:severity, severity)
     |> assign(:window, window)
     |> load_entries()}
  end

  @impl true
  def handle_event("sort", %{"by" => by}, socket) when is_map_key(@sorts, by) do
    {:noreply, patch_filters(socket, %{sort: by})}
  end

  def handle_event("filter_severity", %{"severity" => severity}, socket) do
    severity = if severity in @severities, do: severity, else: nil
    {:noreply, patch_filters(socket, %{severity: severity})}
  end

  def handle_event("filter_window", %{"window" => window}, socket)
      when is_map_key(@windows, window) do
    {:noreply, patch_filters(socket, %{window: window})}
  end

  defp patch_filters(socket, updates) do
    params =
      %{
        sort: socket.assigns.sort,
        severity: socket.assigns.severity,
        window: socket.assigns.window
      }
      |> Map.merge(updates)
      # Keep the URL clean: defaults are implicit.
      |> Map.reject(fn {key, value} ->
        value in [nil, ""] or {key, value} in [sort: "reputation", window: "all"]
      end)

    push_patch(socket, to: ~p"/leaderboard?#{params}")
  end

  defp load_entries(socket) do
    opts = [
      severity: socket.assigns.severity,
      window: Map.fetch!(@windows, socket.assigns.window)
    ]

    entries = Leaderboard.top(Map.fetch!(@sorts, socket.assigns.sort), opts)

    socket
    |> assign(:entries, entries)
    |> assign(:badges, Profiles.badges_for(Enum.map(entries, & &1.account.id)))
  end

  @doc false
  def tier_label("reviewer"), do: "Reviewer"
  def tier_label("contributor"), do: "Contributor"
  def tier_label(_new), do: "New"

  @doc false
  def badge_label(:first_blood), do: "First blood"
  def badge_label(:century), do: "Century"
  def badge_label(:eradicator), do: "Eradicator"
  def badge_label(:exorcist), do: "Exorcist"
  def badge_label(:sharpshooter), do: "Sharpshooter"
  def badge_label(badge), do: badge |> to_string() |> String.replace("_", " ")

  @doc false
  def rank_style(1), do: "text-signal"
  def rank_style(2), do: "text-ink"
  def rank_style(3), do: "text-ink-muted"
  def rank_style(_), do: "text-ink-faint"
end
