permissions_validators_open.gno

package boards2

import (
	"chain/banker"
	"errors"

	"gno.land/p/gnoland/boards"
	"gno.land/p/nt/ufmt/v0"
)

// validateOpenBoardRename validates PermissionBoardRename.
//
// Expected `args` values:
// 1. Caller address
// 2. Board ID
// 3. Current board name
// 4. New board name
func validateOpenBoardRename(_ boards.Permissions, args boards.Args) error {
	caller, ok := args[0].(address)
	if !ok {
		return errors.New("expected a valid caller address")
	}

	newName, ok := args[3].(string)
	if !ok {
		return errors.New("expected new board name to be a string")
	}

	if err := checkBoardNameIsNotAddress(newName); err != nil {
		return err
	}

	if err := checkBoardNameBelongsToAddress(caller, newName); err != nil {
		return err
	}
	return nil
}

// validateOpenMemberInvite validates PermissionMemberInvite.
//
// Expected `args` values:
// 1. Caller address
// 2. Board ID
// 3. Invites
func validateOpenMemberInvite(perms boards.Permissions, args boards.Args) error {
	caller, ok := args[0].(address)
	if !ok {
		return errors.New("expected a valid caller address")
	}

	invites, ok := args[2].([]Invite)
	if !ok {
		return errors.New("expected valid user invites")
	}

	// Make sure that only owners invite other owners
	callerIsOwner := perms.HasRole(caller, RoleOwner)
	for _, v := range invites {
		if v.Role == RoleOwner && !callerIsOwner {
			return errors.New("only owners are allowed to invite other owners")
		}
	}
	return nil
}

// validateOpenRoleChange validates PermissionRoleChange.
//
// Expected `args` values:
// 1. Caller address
// 2. Board ID
// 3. Member address
// 4. Role
func validateOpenRoleChange(perms boards.Permissions, args boards.Args) error {
	caller, ok := args[0].(address)
	if !ok {
		return errors.New("expected a valid caller address")
	}

	// Owners and Admins can change roles.
	// Admins should not be able to assign or remove the Owner role from members.
	if perms.HasRole(caller, RoleAdmin) {
		role, ok := args[3].(boards.Role)
		if !ok {
			return errors.New("expected a valid member role")
		}

		if role == RoleOwner {
			return errors.New("admins are not allowed to promote members to Owner")
		} else {
			member, ok := args[2].(address)
			if !ok {
				return errors.New("expected a valid member address")
			}

			if perms.HasRole(member, RoleOwner) {
				return errors.New("admins are not allowed to remove the Owner role")
			}
		}
	}
	return nil
}

// validateOpenThreadCreate validates PermissionThreadCreate.
//
// Expected `args` values:
// 1. Caller address
// 2. Board ID
// 3. Thread ID
// 4. Title
// 5. Body
func validateOpenThreadCreate(perms boards.Permissions, args boards.Args) error {
	caller, ok := args[0].(address)
	if !ok {
		return errors.New("expected a valid caller address")
	}

	// Owners and admins can create threads without special requirements
	if perms.HasRole(caller, RoleOwner) || perms.HasRole(caller, RoleAdmin) {
		return nil
	}

	// Require non members to have some GNOT in their accounts
	if err := checkAccountHasAmount(caller, gOpenAccountAmount); err != nil {
		return ufmt.Errorf("caller is not allowed to create threads: %s", err)
	}
	return nil
}

// validateOpenReplyCreate validates PermissionReplyCreate.
//
// Expected `args` values:
// 1. Caller address
// 2. Board ID
// 3. Thread ID
// 4. Parent ID
// 5. Reply ID
// 6. Body
func validateOpenReplyCreate(perms boards.Permissions, args boards.Args) error {
	caller, ok := args[0].(address)
	if !ok {
		return errors.New("expected a valid caller address")
	}

	// All board members can reply
	if perms.HasUser(caller) {
		return nil
	}

	// Require non members to have some GNOT in their accounts
	if err := checkAccountHasAmount(caller, gOpenAccountAmount); err != nil {
		return ufmt.Errorf("caller is not allowed to comment: %s", err)
	}
	return nil
}

func checkAccountHasAmount(addr address, amount int64) error {
	bnk := banker.NewBanker(banker.BankerTypeReadonly)
	coins := bnk.GetCoins(addr)
	if coins.AmountOf("ugnot") < gOpenAccountAmount {
		amount = amount / 1_000_000 // ugnot -> GNOT
		return ufmt.Errorf("account amount is lower than %d GNOT", amount)
	}
	return nil
}