defmodule Mv.Authorization.RoleTest do
  @moduledoc """
  Unit tests for Role resource validations and constraints.
  """
  use Mv.DataCase, async: true

  alias Mv.Authorization

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

  describe "permission_set_name validation" do
    test "accepts valid permission set names", %{actor: actor} do
      attrs = %{
        name: "Test Role",
        permission_set_name: "own_data"
      }

      assert {:ok, role} = Authorization.create_role(attrs, actor: actor)
      assert role.permission_set_name == "own_data"
    end

    test "rejects invalid permission set names", %{actor: actor} do
      attrs = %{
        name: "Test Role",
        permission_set_name: "invalid_set"
      }

      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Authorization.create_role(attrs, actor: actor)

      assert error_message(errors, :permission_set_name) =~ "must be one of"
    end

    test "accepts all four valid permission sets", %{actor: actor} do
      valid_sets = ["own_data", "read_only", "normal_user", "admin"]

      for permission_set <- valid_sets do
        attrs = %{
          name: "Role #{permission_set}",
          permission_set_name: permission_set
        }

        assert {:ok, _role} = Authorization.create_role(attrs, actor: actor)
      end
    end
  end

  describe "system role deletion protection" do
    test "prevents deletion of system roles", %{actor: actor} do
      # is_system_role is not settable via public API, so we use Ash.Changeset directly
      changeset =
        Mv.Authorization.Role
        |> Ash.Changeset.for_create(:create_role, %{
          name: "System Role",
          permission_set_name: "own_data"
        })
        |> Ash.Changeset.force_change_attribute(:is_system_role, true)

      {:ok, system_role} = Ash.create(changeset, actor: actor)

      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Authorization.destroy_role(system_role, actor: actor)

      message = error_message(errors, :is_system_role)
      assert message =~ "Cannot delete system role"
    end

    test "allows deletion of non-system roles", %{actor: actor} do
      # is_system_role defaults to false, so regular create works
      {:ok, regular_role} =
        Authorization.create_role(
          %{name: "Regular Role", permission_set_name: "read_only"},
          actor: actor
        )

      assert :ok = Authorization.destroy_role(regular_role, actor: actor)
    end
  end

  describe "name uniqueness" do
    test "enforces unique role names", %{actor: actor} do
      attrs = %{
        name: "Unique Role",
        permission_set_name: "own_data"
      }

      assert {:ok, _} = Authorization.create_role(attrs, actor: actor)

      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Authorization.create_role(attrs, actor: actor)

      assert error_message(errors, :name) =~ "has already been taken"
    end
  end

  # Helper function for error evaluation
  defp error_message(errors, field) when is_atom(field) do
    errors
    |> Enum.filter(fn err -> Map.get(err, :field) == field end)
    |> Enum.map(&Map.get(&1, :message, ""))
    |> List.first() || ""
  end
end
