defmodule Mv.Membership.GroupDatabaseConstraintsTest do
  @moduledoc """
  Tests for database-level constraints (unique, foreign keys, CASCADE).
  These tests verify that constraints are enforced at the database level, not just application level.
  """
  use Mv.DataCase, async: false

  alias Mv.Membership

  require Ash.Query
  import Ash.Expr

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

  describe "Unique Constraints" do
    test "database enforces unique name constraint (case-insensitive via LOWER)", %{actor: actor} do
      {:ok, _group1} = Membership.create_group(%{name: "Test Group"}, actor: actor)

      # Try to create with same name, different case - should fail at DB level
      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Membership.create_group(%{name: "TEST GROUP"}, actor: actor)

      assert Enum.any?(errors, fn err ->
               err.field == :name and
                 (String.contains?(err.message, "already been taken") or
                    String.contains?(err.message, "already exists") or
                    String.contains?(err.message, "duplicate"))
             end)
    end

    test "database enforces unique slug constraint (case-sensitive)", %{actor: actor} do
      {:ok, _group1} = Membership.create_group(%{name: "Test Group"}, actor: actor)

      # Try to create with name that generates same slug - should fail at DB level
      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Membership.create_group(%{name: "test-group"}, actor: actor)

      assert Enum.any?(errors, fn err ->
               (err.field == :slug or err.field == :name) and
                 (String.contains?(err.message, "already been taken") or
                    String.contains?(err.message, "already exists") or
                    String.contains?(err.message, "duplicate"))
             end)
    end
  end

  describe "Foreign Key Constraints" do
    test "cannot create MemberGroup with non-existent member_id", %{actor: actor} do
      {:ok, group} = Membership.create_group(%{name: "Test Group"}, actor: actor)
      fake_member_id = Ash.UUID.generate()

      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Membership.create_member_group(%{member_id: fake_member_id, group_id: group.id},
                 actor: actor
               )

      assert Enum.any?(errors, fn err ->
               (err.field == :member_id or err.field == :member) and
                 (String.contains?(err.message, "does not exist") or
                    String.contains?(err.message, "not found") or
                    String.contains?(err.message, "foreign key"))
             end)
    end

    test "cannot create MemberGroup with non-existent group_id", %{actor: actor} do
      {:ok, member} = Membership.create_member(%{email: "test@test.com"}, actor: actor)
      fake_group_id = Ash.UUID.generate()

      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Membership.create_member_group(%{member_id: member.id, group_id: fake_group_id},
                 actor: actor
               )

      assert Enum.any?(errors, fn err ->
               (err.field == :group_id or err.field == :group) and
                 (String.contains?(err.message, "does not exist") or
                    String.contains?(err.message, "not found") or
                    String.contains?(err.message, "foreign key"))
             end)
    end
  end

  describe "CASCADE Delete Constraints" do
    test "deleting member cascades to member_groups (verified at DB level)", %{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, member_group} =
        Membership.create_member_group(%{member_id: member.id, group_id: group.id},
          actor: actor
        )

      # Verify association exists
      assert member_group.member_id == member.id

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

      # Verify MemberGroup is deleted at DB level (CASCADE)
      {:ok, mgs} =
        Ash.read(
          Mv.Membership.MemberGroup
          |> Ash.Query.filter(expr(id == ^member_group.id)),
          actor: actor,
          domain: Mv.Membership
        )

      assert mgs == []
    end

    test "deleting group cascades to member_groups (verified at DB level)", %{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, member_group} =
        Membership.create_member_group(%{member_id: member.id, group_id: group.id},
          actor: actor
        )

      # Verify association exists
      assert member_group.group_id == group.id

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

      # Verify MemberGroup is deleted at DB level (CASCADE)
      {:ok, mgs} =
        Ash.read(
          Mv.Membership.MemberGroup
          |> Ash.Query.filter(expr(id == ^member_group.id)),
          actor: actor,
          domain: Mv.Membership
        )

      assert mgs == []
    end
  end
end
