defmodule AprsmeWeb.BadPacketsLive.Index do
  @moduledoc false
  use AprsmeWeb, :live_view

  import Ecto.Query

  alias Aprsme.BadPacket
  alias Aprsme.Repo

  @debounce_ms 2_000

  @impl true
  def mount(_params, _session, socket) do
    # Load data unconditionally on the dead render so crawlers and link
    # previews see content — guarding with connected?/1 breaks SEO.
    bad_packets = fetch_bad_packets()
    limited_packets = Enum.take(bad_packets, 100)

    _ =
      if connected?(socket) do
        :ok = Phoenix.PubSub.subscribe(Aprsme.PubSub, "postgres:aprsme_events")
      end

    {:ok,
     assign(socket,
       bad_packets: limited_packets,
       loading: false,
       refresh_timer: nil
     )}
  end

  @impl true
  def handle_params(params, _url, socket) do
    {:noreply, apply_action(socket, socket.assigns.live_action, params)}
  end

  defp apply_action(socket, :index, _params) do
    assign(socket, :page_title, "Bad Packets")
  end

  @impl true
  def handle_event("refresh", _params, socket) do
    _ = cancel_timer(socket.assigns.refresh_timer)
    socket = assign(socket, loading: true)
    {:noreply, do_debounced_refresh(socket)}
  end

  @impl true
  def handle_info({:postgres_notify, _payload}, socket) do
    {:noreply, debounce_refresh(socket)}
  end

  @impl true
  def handle_info(:do_refresh, socket) do
    bad_packets = fetch_bad_packets()
    limited_packets = Enum.take(bad_packets, 100)

    {:noreply,
     assign(socket,
       bad_packets: limited_packets,
       loading: false,
       refresh_timer: nil
     )}
  end

  defp debounce_refresh(socket) do
    _ = cancel_timer(socket.assigns.refresh_timer)
    timer_ref = Process.send_after(self(), :do_refresh, @debounce_ms)
    assign(socket, refresh_timer: timer_ref)
  end

  defp do_debounced_refresh(socket) do
    timer_ref = Process.send_after(self(), :do_refresh, @debounce_ms)
    assign(socket, refresh_timer: timer_ref)
  end

  defp cancel_timer(nil), do: :ok

  defp cancel_timer(ref) do
    Process.cancel_timer(ref)
  end

  defp fetch_bad_packets(limit \\ 100) do
    actual_limit = min(limit, 100)

    BadPacket
    |> order_by([b], desc: b.attempted_at)
    |> limit(^actual_limit)
    |> Repo.all()
  end
end
