defmodule Mv.Membership.CustomFieldPoliciesTest do
  @moduledoc """
  Tests for CustomField resource authorization policies.

  Verifies that all authenticated users with a valid role can read custom fields,
  and only admin can create/update/destroy custom fields.
  """
  use Mv.DataCase, async: false

  alias Mv.Membership.CustomField

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

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

    {:ok, field} =
      CustomField
      |> Ash.Changeset.for_create(:create, %{
        name: "test_field_#{System.unique_integer([:positive])}",
        value_type: :string
      })
      |> Ash.create(actor: admin, domain: Mv.Membership)

    field
  end

  describe "read access (all roles)" do
    test "user with own_data can read all custom fields", %{actor: _actor} do
      custom_field = create_custom_field()
      user = Mv.Fixtures.user_with_role_fixture("own_data")

      {:ok, fields} = Ash.read(CustomField, actor: user, domain: Mv.Membership)
      ids = Enum.map(fields, & &1.id)
      assert custom_field.id in ids

      {:ok, fetched} = Ash.get(CustomField, custom_field.id, actor: user, domain: Mv.Membership)
      assert fetched.id == custom_field.id
    end

    test "user with read_only can read all custom fields", %{actor: _actor} do
      custom_field = create_custom_field()
      user = Mv.Fixtures.user_with_role_fixture("read_only")

      {:ok, fields} = Ash.read(CustomField, actor: user, domain: Mv.Membership)
      ids = Enum.map(fields, & &1.id)
      assert custom_field.id in ids

      {:ok, fetched} = Ash.get(CustomField, custom_field.id, actor: user, domain: Mv.Membership)
      assert fetched.id == custom_field.id
    end

    test "user with normal_user can read all custom fields", %{actor: _actor} do
      custom_field = create_custom_field()
      user = Mv.Fixtures.user_with_role_fixture("normal_user")

      {:ok, fields} = Ash.read(CustomField, actor: user, domain: Mv.Membership)
      ids = Enum.map(fields, & &1.id)
      assert custom_field.id in ids

      {:ok, fetched} = Ash.get(CustomField, custom_field.id, actor: user, domain: Mv.Membership)
      assert fetched.id == custom_field.id
    end

    test "user with admin can read all custom fields", %{actor: _actor} do
      custom_field = create_custom_field()
      user = Mv.Fixtures.user_with_role_fixture("admin")

      {:ok, fields} = Ash.read(CustomField, actor: user, domain: Mv.Membership)
      ids = Enum.map(fields, & &1.id)
      assert custom_field.id in ids

      {:ok, fetched} = Ash.get(CustomField, custom_field.id, actor: user, domain: Mv.Membership)
      assert fetched.id == custom_field.id
    end
  end

  describe "write access - non-admin cannot create/update/destroy" do
    setup %{actor: _actor} do
      user = Mv.Fixtures.user_with_role_fixture("normal_user")
      custom_field = create_custom_field()
      %{user: user, custom_field: custom_field}
    end

    test "non-admin cannot create custom field (forbidden)", %{user: user} do
      assert {:error, %Ash.Error.Forbidden{}} =
               CustomField
               |> Ash.Changeset.for_create(:create, %{
                 name: "forbidden_field_#{System.unique_integer([:positive])}",
                 value_type: :string
               })
               |> Ash.create(actor: user, domain: Mv.Membership)
    end

    test "non-admin cannot update custom field (forbidden)", %{
      user: user,
      custom_field: custom_field
    } do
      assert {:error, %Ash.Error.Forbidden{}} =
               custom_field
               |> Ash.Changeset.for_update(:update, %{description: "Updated"})
               |> Ash.update(actor: user, domain: Mv.Membership)
    end

    test "non-admin cannot destroy custom field (forbidden)", %{
      user: user,
      custom_field: custom_field
    } do
      assert {:error, %Ash.Error.Forbidden{}} =
               Ash.destroy(custom_field, actor: user, domain: Mv.Membership)
    end
  end

  describe "write access - admin can create/update/destroy" do
    setup %{actor: _actor} do
      user = Mv.Fixtures.user_with_role_fixture("admin")
      custom_field = create_custom_field()
      %{user: user, custom_field: custom_field}
    end

    test "admin can create custom field", %{user: user} do
      name = "admin_field_#{System.unique_integer([:positive])}"

      assert {:ok, %CustomField{} = field} =
               CustomField
               |> Ash.Changeset.for_create(:create, %{name: name, value_type: :string})
               |> Ash.create(actor: user, domain: Mv.Membership)

      assert field.name == name
    end

    test "admin can update custom field", %{user: user, custom_field: custom_field} do
      assert {:ok, updated} =
               custom_field
               |> Ash.Changeset.for_update(:update, %{description: "Admin updated"})
               |> Ash.update(actor: user, domain: Mv.Membership)

      assert updated.description == "Admin updated"
    end

    test "admin can destroy custom field", %{user: user, custom_field: custom_field} do
      assert :ok = Ash.destroy(custom_field, actor: user, domain: Mv.Membership)

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