defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
  @moduledoc """
  Integration tests for field visibility dropdown functionality.

  Tests cover:
  - Field selection dropdown rendering
  - Toggling field visibility
  - URL parameter persistence
  - Select all / deselect all
  - Integration with member list display
  - Custom fields visibility
  """
  # async: false to prevent PostgreSQL deadlocks when creating members and custom fields
  use MvWeb.ConnCase, async: false

  import Phoenix.LiveViewTest
  require Ash.Query

  alias Mv.Membership.{CustomField, CustomFieldValue}

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

    # Create test members
    {:ok, member1} =
      Mv.Membership.create_member(
        %{
          first_name: "Alice",
          last_name: "Anderson",
          email: "alice@example.com",
          street: "Main St",
          city: "Berlin"
        },
        actor: system_actor
      )

    {:ok, member2} =
      Mv.Membership.create_member(
        %{
          first_name: "Bob",
          last_name: "Brown",
          email: "bob@example.com",
          street: "Second St",
          city: "Hamburg"
        },
        actor: system_actor
      )

    # Create custom field
    {:ok, custom_field} =
      CustomField
      |> Ash.Changeset.for_create(:create, %{
        name: "membership_number",
        value_type: :string,
        show_in_overview: true
      })
      |> Ash.create(actor: system_actor)

    # Create custom field values
    {:ok, _cfv1} =
      CustomFieldValue
      |> Ash.Changeset.for_create(:create, %{
        member_id: member1.id,
        custom_field_id: custom_field.id,
        value: "M001"
      })
      |> Ash.create(actor: system_actor)

    {:ok, _cfv2} =
      CustomFieldValue
      |> Ash.Changeset.for_create(:create, %{
        member_id: member2.id,
        custom_field_id: custom_field.id,
        value: "M002"
      })
      |> Ash.create(actor: system_actor)

    %{
      member1: member1,
      member2: member2,
      custom_field: custom_field
    }
  end

  describe "field visibility dropdown" do
    test "renders dropdown button", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, _view, html} = live(conn, "/members")

      assert html =~ "Columns"
      assert html =~ ~s(aria-controls="field-visibility-menu")
    end

    test "opens dropdown when button is clicked", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Initially closed
      refute has_element?(view, "ul#field-visibility-menu")

      # Click button
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      # Should be open now
      assert has_element?(view, "ul#field-visibility-menu")
    end

    test "displays all member fields in dropdown", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Open dropdown
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      html = render(view)

      # Check for member fields (formatted labels)
      assert html =~ "First Name" or html =~ "first_name"
      assert html =~ "Email" or html =~ "email"
      assert html =~ "Street" or html =~ "street"
    end

    test "displays custom fields in dropdown", %{conn: conn, custom_field: custom_field} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Open dropdown
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      html = render(view)

      assert html =~ custom_field.name
    end
  end

  describe "field visibility toggling" do
    test "hiding a field removes it from display", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Verify email is visible initially
      html = render(view)
      assert html =~ "alice@example.com"

      # Open dropdown and hide email
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      view
      |> element("button[phx-click='select_item'][phx-value-item='email']")
      |> render_click()

      # Wait for update
      :timer.sleep(100)

      # Email should no longer be visible
      html = render(view)
      refute html =~ "alice@example.com"
      refute html =~ "bob@example.com"
    end

    test "hiding custom field removes it from display", %{conn: conn, custom_field: custom_field} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Verify custom field is visible initially
      html = render(view)
      assert html =~ "M001" or html =~ custom_field.name

      # Open dropdown and hide custom field
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      custom_field_id = custom_field.id
      custom_field_string = "custom_field_#{custom_field_id}"

      view
      |> element("button[phx-click='select_item'][phx-value-item='#{custom_field_string}']")
      |> render_click()

      # Wait for update
      :timer.sleep(100)

      # Custom field should no longer be visible
      html = render(view)
      refute html =~ "M001"
      refute html =~ "M002"
    end
  end

  describe "select all / deselect all" do
    test "select all makes all fields visible", %{conn: conn} do
      conn = conn_with_oidc_user(conn)

      # Start with some fields hidden
      {:ok, view, _html} = live(conn, "/members?fields=first_name")

      # Open dropdown
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      # Click select all
      view
      |> element("button[phx-click='select_all']")
      |> render_click()

      # Wait for update
      :timer.sleep(100)

      # All fields should be visible
      html = render(view)
      assert html =~ "alice@example.com"
      assert html =~ "Main St"
      assert html =~ "Berlin"
    end

    test "deselect all hides all fields except first_name", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Open dropdown
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      # Click deselect all
      view
      |> element("button[phx-click='select_none']")
      |> render_click()

      # Wait for update
      :timer.sleep(100)

      # Only first_name should be visible (it's always shown)
      html = render(view)
      # Email and street should be hidden
      refute html =~ "alice@example.com"
      refute html =~ "Main St"
    end
  end

  describe "URL parameter persistence" do
    test "field selection is persisted in URL", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Open dropdown and hide email
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      view
      |> element("button[phx-click='select_item'][phx-value-item='email']")
      |> render_click()

      # Wait for URL update
      :timer.sleep(100)

      # Check that URL contains fields parameter
      # Note: In LiveView tests, we check the rendered HTML for the updated state
      # The actual URL update happens via push_patch
    end

    test "loading page with fields parameter applies selection", %{conn: conn} do
      conn = conn_with_oidc_user(conn)

      # Load with first_name and city explicitly set in URL
      # Note: Other fields may still be visible due to global settings
      {:ok, view, _html} = live(conn, "/members?fields=first_name,city")

      html = render(view)

      # first_name and city should be visible
      assert html =~ "Alice"
      assert html =~ "Berlin"

      # Note: email and street may still be visible if global settings allow it
      # This test verifies that the URL parameters work, not that they hide other fields
    end

    test "fields parameter works with custom fields", %{conn: conn, custom_field: custom_field} do
      conn = conn_with_oidc_user(conn)
      custom_field_id = custom_field.id

      # Load with custom field visible
      {:ok, view, _html} =
        live(conn, "/members?fields=first_name,custom_field_#{custom_field_id}")

      html = render(view)

      # Custom field should be visible
      assert html =~ "M001" or html =~ custom_field.name
    end
  end

  describe "integration with global settings" do
    test "respects global settings when no user selection", %{conn: conn} do
      # This test would require setting up global settings
      # For now, we verify that the system works with default settings
      conn = conn_with_oidc_user(conn)
      {:ok, _view, html} = live(conn, "/members")

      # All fields should be visible by default
      assert html =~ "alice@example.com"
      assert html =~ "Main St"
    end

    test "user selection overrides global settings", %{conn: conn} do
      # This would require setting up global settings first
      # Then verifying that user selection takes precedence
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Hide a field via dropdown
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      view
      |> element("button[phx-click='select_item'][phx-value-item='email']")
      |> render_click()

      :timer.sleep(100)

      html = render(view)
      refute html =~ "alice@example.com"
    end
  end

  describe "edge cases" do
    test "handles empty fields parameter", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, _view, html} = live(conn, "/members?fields=")

      # Should fall back to global settings
      assert html =~ "alice@example.com"
    end

    test "handles invalid field names in URL", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, _view, html} = live(conn, "/members?fields=invalid_field,another_invalid")

      # Should ignore invalid fields and use defaults
      assert html =~ "alice@example.com"
    end

    test "handles custom field that doesn't exist", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, _view, html} = live(conn, "/members?fields=first_name,custom_field_nonexistent")

      # Should work without errors
      assert html =~ "Alice"
    end

    test "URL with only custom field keeps custom field visible (no invalid fallback)", %{
      conn: conn,
      custom_field: custom_field
    } do
      conn = conn_with_oidc_user(conn)
      id = custom_field.id
      {:ok, _view, html} = live(conn, "/members?fields=custom_field_#{id}")

      # Selection must not be treated as invalid; custom field column stays visible
      assert html =~ "M001" or html =~ custom_field.name
    end

    test "handles rapid toggling", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Open dropdown
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      # Rapidly toggle a field multiple times
      for _ <- 1..5 do
        view
        |> element("button[phx-click='select_item'][phx-value-item='email']")
        |> render_click()

        :timer.sleep(50)
      end

      # Should still work correctly
      html = render(view)
      assert html =~ "Alice"
    end
  end

  describe "accessibility" do
    test "dropdown has proper ARIA attributes", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, _view, html} = live(conn, "/members")

      assert html =~ ~s(aria-controls="field-visibility-menu")
      assert html =~ ~s(aria-haspopup="menu")
      assert html =~ ~s(role="button")
    end

    test "menu items have proper ARIA attributes when open", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Open dropdown
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      html = render(view)

      assert html =~ ~s(role="menu")
      assert html =~ ~s(role="menuitemcheckbox")
      assert html =~ ~s(aria-checked)
    end

    test "keyboard navigation works", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Open dropdown
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      # Check that elements are keyboard accessible
      html = render(view)
      assert html =~ ~s(tabindex="0")
      # Check that keyboard events are supported
      assert html =~ ~s(phx-keydown="select_item")
      assert html =~ ~s(phx-key="Enter")
    end

    test "keyboard activation with Enter key works", %{conn: conn} do
      conn = conn_with_oidc_user(conn)
      {:ok, view, _html} = live(conn, "/members")

      # Verify email is visible initially
      html = render(view)
      assert html =~ "alice@example.com"

      # Open dropdown
      view
      |> element("button[aria-controls='field-visibility-menu']")
      |> render_click()

      # Simulate Enter key press on email field button
      view
      |> element("button[phx-click='select_item'][phx-value-item='email']")
      |> render_keydown(%{key: "Enter"})

      # Wait for update
      :timer.sleep(100)

      # Email should no longer be visible
      html = render(view)
      refute html =~ "alice@example.com"
    end
  end
end
