defmodule Mv.MembershipFees.MembershipFeeTypePoliciesTest do
  @moduledoc """
  Tests for MembershipFeeType resource authorization policies.

  Verifies all roles (own_data, read_only, normal_user, admin) can read;
  only admin can create, update, and destroy; non-admin create/update/destroy → Forbidden.
  """
  use Mv.DataCase, async: false

  alias Mv.MembershipFees

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

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

    {:ok, fee_type} =
      MembershipFees.create_membership_fee_type(
        %{
          name: "Test Fee #{System.unique_integer([:positive])}",
          amount: Decimal.new("10.00"),
          interval: :yearly,
          description: "Test"
        },
        actor: admin
      )

    fee_type
  end

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

    test "can read membership_fee_types (list)", %{user: user} do
      {:ok, list} =
        Mv.MembershipFees.MembershipFeeType
        |> Ash.read(actor: user, domain: Mv.MembershipFees)

      assert is_list(list)
    end

    test "can read single membership_fee_type", %{user: user, fee_type: fee_type} do
      {:ok, found} =
        Ash.get(Mv.MembershipFees.MembershipFeeType, fee_type.id,
          actor: user,
          domain: Mv.MembershipFees
        )

      assert found.id == fee_type.id
    end

    test "cannot create membership_fee_type (returns forbidden)", %{user: user} do
      assert {:error, %Ash.Error.Forbidden{}} =
               MembershipFees.create_membership_fee_type(
                 %{
                   name: "New Fee #{System.unique_integer([:positive])}",
                   amount: Decimal.new("5.00"),
                   interval: :monthly
                 },
                 actor: user
               )
    end

    test "cannot update membership_fee_type (returns forbidden)", %{
      user: user,
      fee_type: fee_type
    } do
      assert {:error, %Ash.Error.Forbidden{}} =
               MembershipFees.update_membership_fee_type(fee_type, %{name: "Updated"},
                 actor: user
               )
    end

    test "cannot destroy membership_fee_type (returns forbidden)", %{user: user, actor: _actor} do
      # Use a fee type with no members/cycles so destroy would succeed if authorized
      admin = Mv.Fixtures.user_with_role_fixture("admin")

      {:ok, isolated} =
        MembershipFees.create_membership_fee_type(
          %{
            name: "Isolated #{System.unique_integer([:positive])}",
            amount: Decimal.new("1.00"),
            interval: :yearly
          },
          actor: admin
        )

      assert {:error, %Ash.Error.Forbidden{}} =
               MembershipFees.destroy_membership_fee_type(isolated, actor: user)
    end
  end

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

    test "can read membership_fee_types (list)", %{user: user} do
      {:ok, list} =
        Mv.MembershipFees.MembershipFeeType
        |> Ash.read(actor: user, domain: Mv.MembershipFees)

      assert is_list(list)
    end

    test "cannot create membership_fee_type (returns forbidden)", %{user: user} do
      assert {:error, %Ash.Error.Forbidden{}} =
               MembershipFees.create_membership_fee_type(
                 %{
                   name: "New Fee #{System.unique_integer([:positive])}",
                   amount: Decimal.new("5.00"),
                   interval: :monthly
                 },
                 actor: user
               )
    end

    test "cannot update membership_fee_type (returns forbidden)", %{
      user: user,
      fee_type: fee_type
    } do
      assert {:error, %Ash.Error.Forbidden{}} =
               MembershipFees.update_membership_fee_type(fee_type, %{name: "Updated"},
                 actor: user
               )
    end

    test "cannot destroy membership_fee_type (returns forbidden)", %{user: user, actor: _actor} do
      admin = Mv.Fixtures.user_with_role_fixture("admin")

      {:ok, isolated} =
        MembershipFees.create_membership_fee_type(
          %{
            name: "Isolated #{System.unique_integer([:positive])}",
            amount: Decimal.new("1.00"),
            interval: :yearly
          },
          actor: admin
        )

      assert {:error, %Ash.Error.Forbidden{}} =
               MembershipFees.destroy_membership_fee_type(isolated, actor: user)
    end
  end

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

    test "can read membership_fee_types (list)", %{user: user} do
      {:ok, list} =
        Mv.MembershipFees.MembershipFeeType
        |> Ash.read(actor: user, domain: Mv.MembershipFees)

      assert is_list(list)
    end

    test "cannot create membership_fee_type (returns forbidden)", %{user: user} do
      assert {:error, %Ash.Error.Forbidden{}} =
               MembershipFees.create_membership_fee_type(
                 %{
                   name: "New Fee #{System.unique_integer([:positive])}",
                   amount: Decimal.new("5.00"),
                   interval: :monthly
                 },
                 actor: user
               )
    end

    test "cannot update membership_fee_type (returns forbidden)", %{
      user: user,
      fee_type: fee_type
    } do
      assert {:error, %Ash.Error.Forbidden{}} =
               MembershipFees.update_membership_fee_type(fee_type, %{name: "Updated"},
                 actor: user
               )
    end

    test "cannot destroy membership_fee_type (returns forbidden)", %{user: user, actor: _actor} do
      admin = Mv.Fixtures.user_with_role_fixture("admin")

      {:ok, isolated} =
        MembershipFees.create_membership_fee_type(
          %{
            name: "Isolated #{System.unique_integer([:positive])}",
            amount: Decimal.new("1.00"),
            interval: :yearly
          },
          actor: admin
        )

      assert {:error, %Ash.Error.Forbidden{}} =
               MembershipFees.destroy_membership_fee_type(isolated, actor: user)
    end
  end

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

    test "can read membership_fee_types (list)", %{user: user} do
      {:ok, list} =
        Mv.MembershipFees.MembershipFeeType
        |> Ash.read(actor: user, domain: Mv.MembershipFees)

      assert is_list(list)
    end

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

      assert {:ok, created} =
               MembershipFees.create_membership_fee_type(
                 %{name: name, amount: Decimal.new("20.00"), interval: :quarterly},
                 actor: user
               )

      assert created.name == name
    end

    test "can update membership_fee_type", %{user: user, fee_type: fee_type} do
      new_name = "Updated #{System.unique_integer([:positive])}"

      assert {:ok, updated} =
               MembershipFees.update_membership_fee_type(fee_type, %{name: new_name}, actor: user)

      assert updated.name == new_name
    end

    test "can destroy membership_fee_type", %{user: user} do
      {:ok, isolated} =
        MembershipFees.create_membership_fee_type(
          %{
            name: "To Delete #{System.unique_integer([:positive])}",
            amount: Decimal.new("1.00"),
            interval: :yearly
          },
          actor: user
        )

      assert :ok = MembershipFees.destroy_membership_fee_type(isolated, actor: user)
    end
  end
end
