const prefix = '!!!TEST!!!/'

async function getCollection(path) {
  const collections = Zotero.Collections.getByLibrary(Zotero.Libraries.userLibraryID, true)

  let parentKey = false
  let coll
  for (const name of path.split('/').map(p => p.trim()).filter(p => p)) {
    coll = collections.find(c => c.name === name && c.parentKey === parentKey)
    if (!coll) {
      coll = new Zotero.Collection({ libraryID: Zotero.Libraries.userLibraryID, name })
      if (parentKey) coll.parentKey = parentKey
      await coll.saveTx()
    }
    parentKey = coll.key
  }

  return coll
}

async function getTagItems(libraryID, tagID) {
  var sql = "SELECT itemID FROM itemTags JOIN items USING (itemID) "
          + "WHERE tagID=? AND libraryID=? AND items.itemID NOT IN (SELECT itemID FROM deletedItems)"
  return Zotero.DB.columnQueryAsync(sql, [tagID, libraryID])
}

async function convert() {
  const now = Date.now()
  let added = 0
  let tags = await Zotero.Tags.getAll(Zotero.Libraries.userLibraryID);
  tags = tags.map(tag => tag.tag || tag || '').filter(tag => tag.includes('/')).sort()

  for (let tag of tags) {
    let itemIDs = await getTagItems(Zotero.Libraries.userLibraryID, Zotero.Tags.getID(tag))
    if (!itemIDs.length) continue

    const coll = await getCollection(prefix + tag)
    await Zotero.DB.executeTransaction(async function () {
      await coll.addItems(itemIDs)
    })

    added += itemIDs.length
  }

  return `${new Date} ${Date.now() - now}ms: ${added} assigned`
}

return await convert()