defmodule MvWeb.GroupLive.Index do
  @moduledoc """
  LiveView for displaying and managing the groups list.

  ## Features
  - List all groups with name, description, and member count
  - Create new groups
  - Navigate to group details and edit forms
  - Delete groups (with confirmation)

  ## Security
  - Admin users can create, edit, and delete groups
  - Read-only users can view groups but not manage them
  - Non-admin users are redirected
  """
  use MvWeb, :live_view

  import MvWeb.LiveHelpers, only: [current_actor: 1, ash_actor_opts: 1]
  import MvWeb.Authorization

  @impl true
  def mount(_params, _session, socket) do
    actor = current_actor(socket)

    # Check if user can access the groups page (page permission check)
    if can_access_page?(actor, "/groups") do
      groups = load_groups(actor)

      {:ok,
       socket
       |> assign(:page_title, gettext("Groups"))
       |> assign(:groups, groups)}
    else
      {:ok, redirect(socket, to: ~p"/members")}
    end
  end

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash} current_user={@current_user}>
      <div class="flex items-center justify-between mb-6">
        <h1 class="text-2xl font-bold">{gettext("Groups")}</h1>
        <%= if can?(@current_user, :create, Mv.Membership.Group) do %>
          <.button navigate={~p"/groups/new"} variant="primary">
            <.icon name="hero-plus" class="size-4 mr-2" />
            {gettext("Create Group")}
          </.button>
        <% end %>
      </div>

      <%= if Enum.empty?(@groups) do %>
        <div class="text-center py-12">
          <p class="text-base-content/70">{gettext("No groups")}</p>
        </div>
      <% else %>
        <div class="overflow-x-auto">
          <table class="table table-zebra">
            <thead>
              <tr>
                <th>{gettext("Name")}</th>
                <th>{gettext("Description")}</th>
                <th>{gettext("Members")}</th>
                <th>{gettext("Actions")}</th>
              </tr>
            </thead>
            <tbody>
              <%= for group <- @groups do %>
                <tr>
                  <td>
                    {group.name}
                  </td>
                  <td>
                    <%= if group.description do %>
                      {group.description}
                    <% else %>
                      <span class="text-base-content/50 italic">—</span>
                    <% end %>
                  </td>
                  <td>
                    <%= if group.member_count do %>
                      {group.member_count}
                    <% else %>
                      0
                    <% end %>
                  </td>
                  <td>
                    <div class="flex gap-2">
                      <.link navigate={~p"/groups/#{group.slug}"} class="btn btn-sm btn-ghost">
                        {gettext("View")}
                      </.link>
                      <%= if can?(@current_user, :update, Mv.Membership.Group) do %>
                        <.link
                          navigate={~p"/groups/#{group.slug}/edit"}
                          class="btn btn-sm btn-ghost"
                        >
                          {gettext("Edit")}
                        </.link>
                      <% end %>
                    </div>
                  </td>
                </tr>
              <% end %>
            </tbody>
          </table>
        </div>
      <% end %>
    </Layouts.app>
    """
  end

  @spec load_groups(map() | nil) :: [Mv.Membership.Group.t()]
  defp load_groups(actor) do
    require Ash.Query

    # Load groups without aggregates first (faster)
    query =
      Mv.Membership.Group
      |> Ash.Query.sort(:name)

    opts = ash_actor_opts(actor)

    case Ash.read(query, opts) do
      {:ok, groups} ->
        # Load all member counts in a single batch query (avoids N+1)
        member_counts = load_member_counts_batch(groups)

        # Attach counts to groups
        Enum.map(groups, fn group ->
          Map.put(group, :member_count, Map.get(member_counts, group.id, 0))
        end)

      {:error, _error} ->
        require Logger
        Logger.warning("Failed to load groups in GroupLive.Index")
        []
    end
  end

  # Loads all member counts for groups using DB-side aggregation for better performance
  # This avoids N+1 queries when loading member_count aggregate for each group
  @spec load_member_counts_batch([Mv.Membership.Group.t()]) :: %{
          Ecto.UUID.t() => non_neg_integer()
        }
  defp load_member_counts_batch(groups) do
    group_ids = Enum.map(groups, & &1.id)

    if Enum.empty?(group_ids) do
      %{}
    else
      # Use Ecto directly for efficient GROUP BY COUNT query
      # This is much more performant than loading aggregates for each group individually
      # Note: We bypass Ash here for performance, but this is a simple read-only query
      import Ecto.Query

      query =
        from mg in Mv.Membership.MemberGroup,
          where: mg.group_id in ^group_ids,
          group_by: mg.group_id,
          select: {mg.group_id, count(mg.id)}

      results = Mv.Repo.all(query)

      results
      |> Enum.into(%{}, fn {group_id, count} -> {group_id, count} end)
    end
  end
end
