defmodule Forum.Admin do
  @moduledoc """
  HTML fragments served to the HTMX admin page (/admin_htmx). Two
  panels (processes, modules) plus their row-only partials.
  """

  # ── processes panel ───────────────────────────────────────────────

  def processes_panel do
    """
    <section>
      <form class="toolbar"
            hx-get="/admin_htmx/processes/rows"
            hx-target="#p-rows"
            hx-trigger="change">
        <label><input type="checkbox" name="mine"> mine</label>
        <span class="htmx-indicator">…</span>
      </form>
      <table>
        <thead><tr>
          <th>pid</th><th>name</th><th>initial call</th>
          <th class="num">memory (KB)</th><th class="num">msgs</th><th>status</th>
        </tr></thead>
        <tbody id="p-rows"
               hx-get="/admin_htmx/processes/rows"
               hx-trigger="load, every 5s"
               hx-include="closest section form"></tbody>
      </table>
    </section>
    """
  end

  def processes_rows(rows) do
    rows
    |> build_tree()
    |> render_tree(0)
    |> IO.iodata_to_binary()
  end

  defp build_tree(rows) do
    by_parent = Enum.group_by(rows, & &1["parent"])
    Map.get(by_parent, nil, []) |> Enum.map(&attach_children(&1, by_parent))
  end

  defp attach_children(node, by_parent) do
    children = Map.get(by_parent, node["pid"], [])
    Map.put(node, "children", Enum.map(children, &attach_children(&1, by_parent)))
  end

  defp render_tree(nodes, depth) do
    Enum.map(nodes, fn node ->
      [process_row(node, depth) | render_tree(node["children"], depth + 1)]
    end)
  end

  defp process_row(node, depth) do
    row_cls = if node["mine"], do: ~s| class="mine"|, else: ""
    pad = 8 + depth * 16
    name = node["name"]
    name_cls = if name, do: ~s| class="name"|, else: ""

    [
      ~s|<tr#{row_cls}>|,
      ~s|<td style="padding-left:#{pad}px">#{h(node["pid"])}</td>|,
      ~s|<td#{name_cls}>#{h(name || "-")}</td>|,
      ~s|<td>#{h(node["initial_call"])}</td>|,
      ~s|<td class="num">#{node["memory_kb"]}</td>|,
      ~s|<td class="num">#{node["msgs"]}</td>|,
      ~s|<td>#{h(node["status"])}</td>|,
      "</tr>"
    ]
  end

  # ── modules panel ─────────────────────────────────────────────────

  def modules_panel do
    """
    <section>
      <form class="toolbar"
            hx-get="/admin_htmx/modules/rows"
            hx-target="#m-rows"
            hx-trigger="change, input changed delay:200ms from:input[name=q]">
        <label><input type="checkbox" name="mine"> mine</label>
        <input type="text" name="q" placeholder="filter…" autocomplete="off">
        <span class="htmx-indicator">…</span>
      </form>
      <table>
        <thead><tr>
          <th>module</th><th>app</th><th>source</th>
        </tr></thead>
        <tbody id="m-rows"
               hx-get="/admin_htmx/modules/rows"
               hx-trigger="load"
               hx-include="closest section form"></tbody>
      </table>
    </section>
    """
  end

  def modules_rows(rows) do
    Enum.map_join(rows, "", &module_row/1)
  end

  defp module_row(r) do
    row_cls = if r["mine"], do: ~s| class="mine"|, else: ""

    ~s|<tr#{row_cls}>| <>
      ~s|<td class="mod">#{h(r["module"])}</td>| <>
      ~s|<td>#{h(r["app"] || "-")}</td>| <>
      ~s|<td class="src">#{h(r["source"] || "(no source)")}</td>| <>
      "</tr>"
  end

  # ── helpers ───────────────────────────────────────────────────────

  defp h(text) when is_binary(text) do
    text
    |> String.replace("&", "&amp;")
    |> String.replace("<", "&lt;")
    |> String.replace(">", "&gt;")
    |> String.replace("\"", "&quot;")
    |> String.replace("'", "&#39;")
  end

  defp h(other), do: other |> to_string() |> h()
end
