lib/slurpee_web/live/blockchain_live.ex

defmodule SlurpeeWeb.BlockchainLive do
  use SlurpeeWeb, :live_view
  import SlurpeeWeb.ViewHelpers.SearchQueryHelper, only: [assign_search_query: 2]
  import SlurpeeWeb.ViewHelpers.ExplorerUrlHelper, only: [explorer_url: 1]

  @impl true
  def mount(_params, _session, socket) do
    Phoenix.PubSub.subscribe(Slurpee.PubSub, "new_head_received")

    socket =
      socket
      |> assign(:query, nil)
      |> assign(latest_blocks: %{})

    {:ok, socket}
  end

  @impl true
  def handle_params(params, _uri, socket) do
    socket =
      socket
      |> assign_search_query(params)
      |> assign_search()

    {:noreply, socket}
  end

  @impl true
  def handle_event("search", params, socket) do
    socket =
      socket
      |> assign_search_query(params)
      |> send_search_after(200)

    {:noreply, socket}
  end

  @impl true
  def handle_event("start", %{"id" => blockchain_id}, socket) do
    Slurp.Commander.start_blockchains(where: [id: blockchain_id])
    blockchains = Slurp.Commander.blockchains([])

    socket =
      socket
      |> assign(blockchains: blockchains)

    {:noreply, socket}
  end

  @impl true
  def handle_event("stop", %{"id" => blockchain_id}, socket) do
    Slurp.Commander.stop_blockchains(where: [id: blockchain_id])
    blockchains = Slurp.Commander.blockchains([])

    socket =
      socket
      |> assign(blockchains: blockchains)

    {:noreply, socket}
  end

  @impl true
  def handle_event("start-all", _, socket) do
    Slurp.Commander.start_blockchains([])
    blockchains = Slurp.Commander.blockchains([])

    socket =
      socket
      |> assign(blockchains: blockchains)

    {:noreply, socket}
  end

  @impl true
  def handle_event("stop-all", _, socket) do
    Slurp.Commander.stop_blockchains([])
    blockchains = Slurp.Commander.blockchains([])

    socket =
      socket
      |> assign(blockchains: blockchains)

    {:noreply, socket}
  end

  @impl true
  def handle_info(:search, socket) do
    socket =
      socket
      |> assign(:search_timer, nil)
      |> assign_search()

    {:noreply, socket}
  end

  @impl true
  def handle_info({"new_head_received", blockchain_id, block_number}, socket) do
    latest_blocks =
      socket.assigns.latest_blocks
      |> Map.put(blockchain_id, block_number)

    {:noreply, assign(socket, latest_blocks: latest_blocks)}
  end

  defp send_search_after(socket, after_ms) do
    if socket.assigns[:search_timer] do
      socket
    else
      timer = Process.send_after(self(), :search, after_ms)
      assign(socket, :search_timer, timer)
    end
  end

  defp assign_search(socket) do
    socket
    |> assign(blockchains: search_blockchains(socket.assigns.query))
  end

  defp search_blockchains(search_term) do
    []
    |> Slurp.Commander.blockchains()
    |> search_blockchains(search_term)
  end

  defp search_blockchains(blockchains, nil) do
    blockchains
  end

  defp search_blockchains(blockchains, search_term) do
    blockchains
    |> Enum.filter(fn b ->
      String.contains?(b.id, search_term) ||
        String.contains?(b.name, search_term) ||
        String.contains?(b.status |> Atom.to_string(), search_term) ||
        String.contains?(b.network_id |> to_string(), search_term) ||
        String.contains?(b.chain_id |> to_string(), search_term) ||
        String.contains?(b.chain, search_term) ||
        String.contains?(b.testnet |> to_string(), search_term)
    end)
  end

  defp running?(%Slurp.Commander.Blockchains.ListItem{status: :running}), do: true
  defp running?(_), do: false

  defp none_running?(blockchains), do: running_count(blockchains) == 0

  defp all_running?(blockchains), do: running_count(blockchains) == length(blockchains)

  defp running_count(blockchains) do
    blockchains
    |> Enum.filter(&running?/1)
    |> Enum.count()
  end
end