defmodule MvWeb.MemberLive.IndexGroupsPerformanceTest do
  @moduledoc """
  Tests for performance and N+1 query prevention for groups in the member overview.

  Tests cover:
  - Groups are loaded with members in a single query (preloading)
  - No N+1 queries when loading members with groups
  - Filter works at database level (not in-memory)
  - Sort runs in-memory but uses preloaded group data (no extra DB queries)
  """
  # async: false to prevent PostgreSQL deadlocks when creating members and groups
  use MvWeb.ConnCase, async: false
  import Phoenix.LiveViewTest
  require Ash.Query

  alias Mv.Membership.{Group, MemberGroup}

  setup do
    system_actor = Mv.Helpers.SystemActor.get_system_actor()

    # Create test members (enough to test performance)
    members =
      for i <- 1..10 do
        {:ok, member} =
          Mv.Membership.create_member(
            %{
              first_name: "Member#{i}",
              last_name: "Test#{i}",
              email: "member#{i}@example.com"
            },
            actor: system_actor
          )

        member
      end

    # Create test groups
    {:ok, group1} =
      Group
      |> Ash.Changeset.for_create(:create, %{name: "Group 1"})
      |> Ash.create(actor: system_actor)

    {:ok, group2} =
      Group
      |> Ash.Changeset.for_create(:create, %{name: "Group 2"})
      |> Ash.create(actor: system_actor)

    # Assign members to groups (alternating pattern)
    Enum.each(Enum.with_index(members), fn {member, index} ->
      group_id = if rem(index, 2) == 0, do: group1.id, else: group2.id

      {:ok, _mg} =
        MemberGroup
        |> Ash.Changeset.for_create(:create, %{member_id: member.id, group_id: group_id})
        |> Ash.create(actor: system_actor)
    end)

    %{
      members: members,
      group1: group1,
      group2: group2
    }
  end

  @tag :slow
  test "groups are preloaded with members (no N+1 queries)", %{
    conn: conn,
    members: _members
  } do
    # This test verifies that groups are loaded efficiently
    # We check query count by monitoring database queries
    # Note: Actual query counting would require Ecto query logging
    # For now, we verify the functionality works correctly

    conn = conn_with_oidc_user(conn)
    {:ok, _view, html} = live(conn, "/members")

    # Verify all members are loaded
    Enum.each(1..10, fn i ->
      assert html =~ "Member#{i}"
    end)

    # Verify groups are displayed (if preloaded correctly, this should work)
    # If N+1 queries occurred, the page might be slow or fail
    assert html
  end

  @tag :slow
  test "filter works at database level", %{
    conn: conn,
    group1: group1,
    members: members
  } do
    # This test verifies that filtering happens in the database query,
    # not by filtering in-memory after loading all members

    conn = conn_with_oidc_user(conn)
    {:ok, view, _html} = live(conn, "/members")

    # Open filter and apply "Yes" for group1 (even-indexed members are in group1)
    view
    |> element("button[aria-label='Filter members']")
    |> render_click()

    view
    |> element("[data-testid='member-filter-form']")
    |> render_change(%{"group_#{group1.id}" => "in", "payment_filter" => "all"})

    # Force LiveView to process {:group_filter_changed, ...}
    html = render(view)

    # Only even-indexed members (0,2,4,6,8) are in group1
    Enum.each([0, 2, 4, 6, 8], fn i ->
      member = Enum.at(members, i)
      assert html =~ member.first_name
    end)

    Enum.each([1, 3, 5, 7, 9], fn i ->
      member = Enum.at(members, i)
      refute html =~ member.first_name
    end)
  end

  @tag :slow
  test "sorting works at database level", %{
    conn: conn,
    members: _members
  } do
    # This test verifies that sorting happens in the database query,
    # not by sorting in-memory after loading all members

    conn = conn_with_oidc_user(conn)
    {:ok, view, _html} = live(conn, "/members")

    # Apply sorting
    view
    |> element("[data-testid='groups']")
    |> render_click()

    # Verify sorting is applied
    html = render(view)

    # Verify members are displayed (if sorting was done in-memory,
    # we'd load all members first, which is less efficient)
    assert html

    # Database-level sorting is more efficient for large datasets
  end

  @tag :slow
  test "handles many members with many groups efficiently", %{
    conn: conn
  } do
    system_actor = Mv.Helpers.SystemActor.get_system_actor()

    # Create many members (20) with multiple groups each (use distinct emails to avoid collision with setup)
    members =
      for i <- 11..30 do
        {:ok, member} =
          Mv.Membership.create_member(
            %{
              first_name: "Member#{i}",
              last_name: "Test#{i}",
              email: "member#{i}@example.com"
            },
            actor: system_actor
          )

        member
      end

    # Create multiple groups (use distinct names to avoid collision with setup's Group 1/2)
    groups =
      for i <- 1..5 do
        {:ok, group} =
          Group
          |> Ash.Changeset.for_create(:create, %{name: "Perf Group #{i}"})
          |> Ash.create(actor: system_actor)

        group
      end

    # Assign each member to 2-3 random groups
    Enum.each(members, fn member ->
      selected_groups = Enum.take_random(groups, Enum.random(2..3))

      Enum.each(selected_groups, fn group ->
        {:ok, _mg} =
          MemberGroup
          |> Ash.Changeset.for_create(:create, %{member_id: member.id, group_id: group.id})
          |> Ash.create(actor: system_actor)
      end)
    end)

    conn = conn_with_oidc_user(conn)
    {:ok, _view, html} = live(conn, "/members")

    # Verify all members are loaded efficiently
    Enum.each(11..30, fn i ->
      assert html =~ "Member#{i}"
    end)

    # If preloading works correctly, this should be fast
    # If N+1 queries occurred, this would be very slow
    assert html
  end
end
