defmodule Mv.Membership.CustomFieldDeletionTest do
  @moduledoc """
  Tests for CustomField deletion with CASCADE behavior.

  Tests cover:
  - Deletion of custom fields without assigned values
  - Deletion of custom fields with assigned values (CASCADE)
  - assigned_members_count calculation
  - prepare_deletion action with count loading
  - CASCADE deletion only affects specific custom field values
  """
  use Mv.DataCase, async: true

  alias Mv.Membership.{CustomField, CustomFieldValue, Member}

  setup do
    system_actor = Mv.Helpers.SystemActor.get_system_actor()
    %{actor: system_actor}
  end

  describe "assigned_members_count calculation" do
    test "returns 0 for custom field without any values", %{actor: actor} do
      {:ok, custom_field} =
        CustomField
        |> Ash.Changeset.for_create(:create, %{
          name: "test_field",
          value_type: :string
        })
        |> Ash.create(actor: actor)

      custom_field_with_count = Ash.load!(custom_field, :assigned_members_count, actor: actor)
      assert custom_field_with_count.assigned_members_count == 0
    end

    test "returns correct count for custom field with one member", %{actor: actor} do
      {:ok, member} = create_member(actor)
      {:ok, custom_field} = create_custom_field("test_field", :string, actor)

      {:ok, _custom_field_value} =
        CustomFieldValue
        |> Ash.Changeset.for_create(:create, %{
          member_id: member.id,
          custom_field_id: custom_field.id,
          value: %{"_union_type" => "string", "_union_value" => "test"}
        })
        |> Ash.create(actor: actor)

      custom_field_with_count = Ash.load!(custom_field, :assigned_members_count, actor: actor)
      assert custom_field_with_count.assigned_members_count == 1
    end

    test "returns correct count for custom field with multiple members", %{actor: actor} do
      {:ok, member1} = create_member(actor)
      {:ok, member2} = create_member(actor)
      {:ok, member3} = create_member(actor)
      {:ok, custom_field} = create_custom_field("test_field", :string, actor)

      # Create custom field value for each member
      for member <- [member1, member2, member3] do
        {:ok, _} =
          CustomFieldValue
          |> Ash.Changeset.for_create(:create, %{
            member_id: member.id,
            custom_field_id: custom_field.id,
            value: %{"_union_type" => "string", "_union_value" => "test"}
          })
          |> Ash.create(actor: actor)
      end

      custom_field_with_count = Ash.load!(custom_field, :assigned_members_count, actor: actor)
      assert custom_field_with_count.assigned_members_count == 3
    end

    test "counts distinct members (not multiple values per member)", %{actor: actor} do
      {:ok, member} = create_member(actor)
      {:ok, custom_field} = create_custom_field("test_field", :string, actor)

      # Create custom field value for member
      {:ok, _} =
        CustomFieldValue
        |> Ash.Changeset.for_create(:create, %{
          member_id: member.id,
          custom_field_id: custom_field.id,
          value: %{"_union_type" => "string", "_union_value" => "test"}
        })
        |> Ash.create(actor: actor)

      custom_field_with_count = Ash.load!(custom_field, :assigned_members_count, actor: actor)

      # Should still be 1, not 2, even if we tried to create multiple (which would fail due to uniqueness)
      assert custom_field_with_count.assigned_members_count == 1
    end
  end

  describe "prepare_deletion action" do
    test "loads assigned_members_count for deletion preparation", %{actor: actor} do
      {:ok, member} = create_member(actor)
      {:ok, custom_field} = create_custom_field("test_field", :string, actor)

      {:ok, _} =
        CustomFieldValue
        |> Ash.Changeset.for_create(:create, %{
          member_id: member.id,
          custom_field_id: custom_field.id,
          value: %{"_union_type" => "string", "_union_value" => "test"}
        })
        |> Ash.create(actor: actor)

      # Use prepare_deletion action
      [prepared_custom_field] =
        CustomField
        |> Ash.Query.for_read(:prepare_deletion, %{id: custom_field.id})
        |> Ash.read!(actor: actor)

      assert prepared_custom_field.assigned_members_count == 1
      assert prepared_custom_field.id == custom_field.id
    end

    test "returns empty list for non-existent custom field", %{actor: actor} do
      non_existent_id = Ash.UUID.generate()

      result =
        CustomField
        |> Ash.Query.for_read(:prepare_deletion, %{id: non_existent_id})
        |> Ash.read!(actor: actor)

      assert result == []
    end
  end

  describe "destroy_with_values action" do
    test "deletes custom field without any values", %{actor: actor} do
      {:ok, custom_field} = create_custom_field("test_field", :string, actor)

      assert :ok = Ash.destroy(custom_field, actor: actor)

      # Verify custom field is deleted
      assert {:error, _} = Ash.get(CustomField, custom_field.id, actor: actor)
    end

    test "deletes custom field and cascades to all its values", %{actor: actor} do
      {:ok, member} = create_member(actor)
      {:ok, custom_field} = create_custom_field("test_field", :string, actor)

      {:ok, custom_field_value} =
        CustomFieldValue
        |> Ash.Changeset.for_create(:create, %{
          member_id: member.id,
          custom_field_id: custom_field.id,
          value: %{"_union_type" => "string", "_union_value" => "test"}
        })
        |> Ash.create(actor: actor)

      # Delete custom field
      assert :ok = Ash.destroy(custom_field, actor: actor)

      # Verify custom field is deleted
      assert {:error, _} = Ash.get(CustomField, custom_field.id, actor: actor)

      # Verify custom field value is also deleted (CASCADE)
      assert {:error, _} = Ash.get(CustomFieldValue, custom_field_value.id, actor: actor)

      # Verify member still exists
      assert {:ok, _} = Ash.get(Member, member.id, actor: actor)
    end

    test "deletes only values of the specific custom field", %{actor: actor} do
      {:ok, member} = create_member(actor)
      {:ok, custom_field1} = create_custom_field("field1", :string, actor)
      {:ok, custom_field2} = create_custom_field("field2", :string, actor)

      # Create value for custom_field1
      {:ok, value1} =
        CustomFieldValue
        |> Ash.Changeset.for_create(:create, %{
          member_id: member.id,
          custom_field_id: custom_field1.id,
          value: %{"_union_type" => "string", "_union_value" => "value1"}
        })
        |> Ash.create(actor: actor)

      # Create value for custom_field2
      {:ok, value2} =
        CustomFieldValue
        |> Ash.Changeset.for_create(:create, %{
          member_id: member.id,
          custom_field_id: custom_field2.id,
          value: %{"_union_type" => "string", "_union_value" => "value2"}
        })
        |> Ash.create(actor: actor)

      # Delete custom_field1
      assert :ok = Ash.destroy(custom_field1, actor: actor)

      # Verify custom_field1 and value1 are deleted
      assert {:error, _} = Ash.get(CustomField, custom_field1.id, actor: actor)
      assert {:error, _} = Ash.get(CustomFieldValue, value1.id, actor: actor)

      # Verify custom_field2 and value2 still exist
      assert {:ok, _} = Ash.get(CustomField, custom_field2.id, actor: actor)
      assert {:ok, _} = Ash.get(CustomFieldValue, value2.id, actor: actor)
    end

    test "deletes custom field with values from multiple members", %{actor: actor} do
      {:ok, member1} = create_member(actor)
      {:ok, member2} = create_member(actor)
      {:ok, member3} = create_member(actor)
      {:ok, custom_field} = create_custom_field("test_field", :string, actor)

      # Create value for each member
      values =
        for member <- [member1, member2, member3] do
          {:ok, value} =
            CustomFieldValue
            |> Ash.Changeset.for_create(:create, %{
              member_id: member.id,
              custom_field_id: custom_field.id,
              value: %{"_union_type" => "string", "_union_value" => "test"}
            })
            |> Ash.create(actor: actor)

          value
        end

      # Delete custom field
      assert :ok = Ash.destroy(custom_field, actor: actor)

      # Verify all values are deleted
      for value <- values do
        assert {:error, _} = Ash.get(CustomFieldValue, value.id, actor: actor)
      end

      # Verify all members still exist
      for member <- [member1, member2, member3] do
        assert {:ok, _} = Ash.get(Member, member.id, actor: actor)
      end
    end
  end

  # Helper functions
  defp create_member(actor) do
    Mv.Membership.create_member(
      %{
        first_name: "Test",
        last_name: "User#{System.unique_integer([:positive])}",
        email: "test#{System.unique_integer([:positive])}@example.com"
      },
      actor: actor
    )
  end

  defp create_custom_field(name, value_type, actor) do
    CustomField
    |> Ash.Changeset.for_create(:create, %{
      name: "#{name}_#{System.unique_integer([:positive])}",
      value_type: value_type
    })
    |> Ash.create(actor: actor)
  end
end
