defmodule Mv.Membership.MemberGroupTest do
  @moduledoc """
  Tests for MemberGroup join table resource - validations and cascade delete behavior.
  Uses async: true; no shared DB state or sandbox constraints.
  """
  use Mv.DataCase, async: true

  alias Mv.Membership

  require Ash.Query
  import Ash.Expr

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

  describe "Validations & Associations" do
    test "create association between member and group", %{actor: actor} do
      {:ok, member} = Membership.create_member(%{email: "test@test.com"}, actor: actor)
      {:ok, group} = Membership.create_group(%{name: "Test Group"}, actor: actor)

      assert {:ok, member_group} =
               Membership.create_member_group(%{member_id: member.id, group_id: group.id},
                 actor: actor
               )

      assert member_group.member_id == member.id
      assert member_group.group_id == group.id
    end

    test "prevent duplicate associations (same member + same group)", %{actor: actor} do
      {:ok, member} = Membership.create_member(%{email: "test@test.com"}, actor: actor)
      {:ok, group} = Membership.create_group(%{name: "Test Group"}, actor: actor)

      {:ok, _mg1} =
        Membership.create_member_group(%{member_id: member.id, group_id: group.id},
          actor: actor
        )

      # Try to create duplicate
      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Membership.create_member_group(%{member_id: member.id, group_id: group.id},
                 actor: actor
               )

      assert Enum.any?(errors, fn err ->
               field = Map.get(err, :field)
               message = Map.get(err, :message, "")

               (field == :member_id or field == :group_id) and
                 (String.contains?(message, "already been taken") or
                    String.contains?(message, "already exists") or
                    String.contains?(message, "duplicate") or
                    String.contains?(message, "already in this group"))
             end)
    end
  end

  describe "Cascade Delete Behavior" do
    test "cascade delete when member deleted (MemberGroup deleted, Group remains)", %{
      actor: actor
    } do
      {:ok, member} = Membership.create_member(%{email: "test@test.com"}, actor: actor)
      {:ok, group} = Membership.create_group(%{name: "Test Group"}, actor: actor)

      {:ok, _mg} =
        Membership.create_member_group(%{member_id: member.id, group_id: group.id},
          actor: actor
        )

      # Delete member
      :ok = Membership.destroy_member(member, actor: actor)

      # Group should still exist
      {:ok, group_reloaded} = Ash.get(Mv.Membership.Group, group.id, actor: actor)
      assert group_reloaded != nil

      # MemberGroup should be deleted
      {:ok, mgs} =
        Ash.read(
          Mv.Membership.MemberGroup
          |> Ash.Query.filter(expr(member_id == ^member.id)),
          actor: actor,
          domain: Mv.Membership
        )

      assert mgs == []
    end

    test "cascade delete when group deleted (MemberGroup deleted, Member remains)", %{
      actor: actor
    } do
      {:ok, member} = Membership.create_member(%{email: "test@test.com"}, actor: actor)
      {:ok, group} = Membership.create_group(%{name: "Test Group"}, actor: actor)

      {:ok, _mg} =
        Membership.create_member_group(%{member_id: member.id, group_id: group.id},
          actor: actor
        )

      # Delete group
      :ok = Membership.destroy_group(group, actor: actor)

      # Member should still exist
      {:ok, member_reloaded} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
      assert member_reloaded != nil

      # MemberGroup should be deleted
      {:ok, mgs} =
        Ash.read(
          Mv.Membership.MemberGroup
          |> Ash.Query.filter(expr(group_id == ^group.id)),
          actor: actor,
          domain: Mv.Membership
        )

      assert mgs == []
    end
  end
end
