package model

// In Cwtch "handle" exclusively refers to an onion address, this is unlikely to ever change
type Handle string

// A role
type Role string

const (
	ROLE_ADMIN = Role("admin")
	ROLE_OP    = Role("op")
	ROLE_MOD   = Role("mod")
	ROLE_VOICE = Role("voice")
	ROLE_USER  = Role("user")

	ROLE_UNKNOWN = Role("unknown") // no permissions
)

func (r Role) IsEffectiveAdmin() bool {
	return r == ROLE_ADMIN
}

func (r Role) IsEffectiveOperator() bool {
	return r == ROLE_ADMIN || r == ROLE_OP
}

func (r Role) IsEffectiveMod() bool {
	return r == ROLE_ADMIN || r == ROLE_OP || r == ROLE_MOD
}

func (r Role) IsEffectiveVoice() bool {
	return r == ROLE_ADMIN || r == ROLE_OP || r == ROLE_MOD || r == ROLE_VOICE
}

func (r Role) ComputeEffectiveACL(mode GroupMode) AccessControl {
	acl := AccessControl{
		GroupAdmin:          r.IsEffectiveAdmin(),
		HideMessage:         r.IsEffectiveMod(),
		UpdateGroupMetadata: r.IsEffectiveOperator(),

		Read:   r.IsEffectiveMod() || mode.IsNotSet(WriteOnly),
		Append: r.IsEffectiveVoice() || mode.IsNotSet(VoiceOnly),

		Promote: r.IsEffectiveMod(), // NOTE +e equality rules apply additionally to this check

		Invite: r.IsEffectiveOperator() || mode.IsSet(AnyoneCanInvite) || mode.IsSet(OpenGroup),
		Kick:   r.IsEffectiveMod(),

		ChangeAllChannelModes:    r.IsEffectiveOperator(),
		ChangeMaskedChannelModes: r.IsEffectiveMod(),

		AssignedRole: r,
	}

	return acl
}

func IsAssignableRole(r Role) bool {
	return r == ROLE_ADMIN || r == ROLE_OP || r == ROLE_MOD || r == ROLE_VOICE || r == ROLE_USER
}

func (r Role) IsAbleToPromoteTo(currentRole Role, targetRole Role, mode GroupMode) bool {

	if !IsAssignableRole(targetRole) {
		return false
	}

	if r.IsEffectiveAdmin() {
		return true // admins can do anything
	}

	// no one below admin can promote/demote to admin ever
	if currentRole == ROLE_ADMIN || targetRole == ROLE_ADMIN {
		return false
	}

	if r == ROLE_OP {
		if currentRole == ROLE_OP || targetRole == ROLE_OP {
			return mode.IsSet(Equality) // ops can only promote/demote ops if equality
		} else {
			return true // op can always promote/demote below op
		}
	}

	// no one below op can promote/demote op ever
	if currentRole == ROLE_OP || targetRole == ROLE_OP {
		return false
	}

	if r == ROLE_MOD {
		if currentRole == ROLE_MOD || targetRole == ROLE_MOD {
			return mode.IsSet(Equality) // ops can only promote/demote mod if equality
		} else {
			return true // mod can always promote/demote below mode
		}
	}

	return false // not an admin, operator, or mod means we can never promote/demote
}
