defmodule Mv.Membership.GroupPoliciesTest do
  @moduledoc """
  Tests for Group resource authorization policies.

  Verifies that own_data, read_only, normal_user can read groups;
  normal_user and admin can create, update, and destroy groups.
  """
  use Mv.DataCase, async: false

  alias Mv.Membership

  require Ash.Query

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

  defp create_group_fixture do
    admin = Mv.Fixtures.user_with_role_fixture("admin")

    {:ok, group} =
      Membership.create_group(
        %{name: "Test Group #{System.unique_integer([:positive])}", description: "Test"},
        actor: admin
      )

    group
  end

  describe "own_data permission set" do
    setup %{actor: _actor} do
      user = Mv.Fixtures.user_with_role_fixture("own_data")
      group = create_group_fixture()
      %{user: user, group: group}
    end

    test "can read groups (list)", %{user: user} do
      {:ok, groups} = Membership.list_groups(actor: user)
      assert is_list(groups)
    end

    test "can read single group", %{user: user, group: group} do
      {:ok, found} = Ash.get(Membership.Group, group.id, actor: user, domain: Mv.Membership)
      assert found.id == group.id
    end
  end

  describe "read_only permission set" do
    setup %{actor: _actor} do
      user = Mv.Fixtures.user_with_role_fixture("read_only")
      group = create_group_fixture()
      %{user: user, group: group}
    end

    test "can read groups (list)", %{user: user} do
      {:ok, groups} = Membership.list_groups(actor: user)
      assert is_list(groups)
    end

    test "can read single group", %{user: user, group: group} do
      {:ok, found} = Ash.get(Membership.Group, group.id, actor: user, domain: Mv.Membership)
      assert found.id == group.id
    end
  end

  describe "normal_user permission set" do
    setup %{actor: _actor} do
      user = Mv.Fixtures.user_with_role_fixture("normal_user")
      group = create_group_fixture()
      %{user: user, group: group}
    end

    test "can read groups (list)", %{user: user} do
      {:ok, groups} = Membership.list_groups(actor: user)
      assert is_list(groups)
    end

    test "can read single group", %{user: user, group: group} do
      {:ok, found} = Ash.get(Membership.Group, group.id, actor: user, domain: Mv.Membership)
      assert found.id == group.id
    end

    test "can create group", %{user: user} do
      assert {:ok, created} =
               Membership.create_group(
                 %{name: "New Group #{System.unique_integer([:positive])}", description: "New"},
                 actor: user
               )

      assert created.name =~ "New Group"
    end

    test "can update group", %{user: user, group: group} do
      assert {:ok, updated} =
               Membership.update_group(group, %{description: "Updated"}, actor: user)

      assert updated.description == "Updated"
    end

    test "can destroy group", %{user: user, group: group} do
      assert :ok = Membership.destroy_group(group, actor: user)
    end
  end

  describe "admin permission set" do
    setup %{actor: _actor} do
      user = Mv.Fixtures.user_with_role_fixture("admin")
      group = create_group_fixture()
      %{user: user, group: group}
    end

    test "can read groups (list)", %{user: user} do
      {:ok, groups} = Membership.list_groups(actor: user)
      assert is_list(groups)
    end

    test "can create group", %{user: user} do
      name = "Admin Group #{System.unique_integer([:positive])}"

      assert {:ok, group} =
               Membership.create_group(%{name: name, description: "Admin created"}, actor: user)

      assert group.name == name
    end

    test "can update group", %{user: user, group: group} do
      assert {:ok, updated} =
               Membership.update_group(group, %{description: "Updated by admin"}, actor: user)

      assert updated.description == "Updated by admin"
    end

    test "can destroy group", %{user: user, group: group} do
      assert :ok = Membership.destroy_group(group, actor: user)

      assert {:error, _} = Ash.get(Membership.Group, group.id, actor: user, domain: Mv.Membership)
    end
  end
end
