var CUNIRANK_VISUALS = {
  owner: '&6[OWN] ',
  admin: '&c[ADM] ',
  moderador: '&9[MOD] ',
  helper: '&a[HLP] ',
  eclipse: '&8[ECL] ',
  cuniculus: '&6[CUN] ',
  arconte: '&5[ARC] ',
  sabio: '&b[SAB] ',
  centinela: '&1[CEN] ',
  tunelero: '&e[TUN] ',
  raiz: '&2[RAI] ',
  tierra: '&6[TIE] ',
  tiktok: '&d[TT] ',
  youtuber: '&c[YT] ',
  kick: '&a[KICK] ',
  twitch: '&5[TTV] ',
  facebook: '&9[FB] ',
  madriguero: '&7[MAD] '
};

var CUNIRANK_CHAT_VISUALS = {
  owner: { tag: '[OWN]', color: 'gold' },
  admin: { tag: '[ADM]', color: 'red' },
  moderador: { tag: '[MOD]', color: 'blue' },
  helper: { tag: '[HLP]', color: 'green' },
  eclipse: { tag: '[ECL]', color: 'dark_gray' },
  cuniculus: { tag: '[CUN]', color: 'gold' },
  arconte: { tag: '[ARC]', color: 'dark_purple' },
  sabio: { tag: '[SAB]', color: 'aqua' },
  centinela: { tag: '[CEN]', color: 'dark_blue' },
  tunelero: { tag: '[TUN]', color: 'yellow' },
  raiz: { tag: '[RAI]', color: 'dark_green' },
  tierra: { tag: '[TIE]', color: 'gold' },
  tiktok: { tag: '[TT]', color: 'light_purple' },
  youtuber: { tag: '[YT]', color: 'red' },
  kick: { tag: '[KICK]', color: 'green' },
  twitch: { tag: '[TTV]', color: 'dark_purple' },
  facebook: { tag: '[FB]', color: 'blue' },
  madriguero: { tag: '[MAD]', color: 'gray' }
};

var CUNIRANK_CHAT_PRIORITY = [
  'owner',
  'admin',
  'moderador',
  'helper',
  'eclipse',
  'cuniculus',
  'arconte',
  'sabio',
  'centinela',
  'tunelero',
  'raiz',
  'tierra',
  'tiktok',
  'youtuber',
  'kick',
  'twitch',
  'facebook',
  'madriguero'
];

var CUNIRANK_FUNCTIONAL_ONLY = [];
var CUNIRANK_ROLE_LIST = Object.keys(CUNIRANK_VISUALS).concat(CUNIRANK_FUNCTIONAL_ONLY);
var CUNIRANK_PLAYER_RANK_TAG = 'cuniculum_rank';
var CUNIRANK_PLAYER_RANK_LIST_TAG = 'cuniculum_rank_list';
var CUNIRANK_INITIALIZED_TAG_PREFIX = 'cuniculum_rank_initialized_';
var CUNIRANK_EXPECTED_CLAIMS = {
  owner: '1000000 claims / 512 force-load',
  admin: '10000 claims / 256 force-load',
  tiktok: '9 claims / 0 force-load; si tiene rango de progreso superior, manda ese rango',
  youtuber: '9 claims / 0 force-load; si tiene rango de progreso superior, manda ese rango',
  kick: '9 claims / 0 force-load; si tiene rango de progreso superior, manda ese rango',
  twitch: '9 claims / 0 force-load; si tiene rango de progreso superior, manda ese rango',
  facebook: '9 claims / 0 force-load; si tiene rango de progreso superior, manda ese rango',
  madriguero: '9 claims / 0 force-load'
};

var CUNIRANK_COMPONENT_CLASS = null;
try {
  CUNIRANK_COMPONENT_CLASS = Java.loadClass('net.minecraft.network.chat.Component');
} catch (ignored) {
  CUNIRANK_COMPONENT_CLASS = null;
}

var CUNIRANK_FILES_CLASS = null;
var CUNIRANK_PATHS_CLASS = null;
var CUNIRANK_STANDARD_CHARSETS_CLASS = null;
try {
  CUNIRANK_FILES_CLASS = Java.loadClass('java.nio.file.Files');
  CUNIRANK_PATHS_CLASS = Java.loadClass('java.nio.file.Paths');
  CUNIRANK_STANDARD_CHARSETS_CLASS = Java.loadClass('java.nio.charset.StandardCharsets');
} catch (ignoredFileApi) {
  CUNIRANK_FILES_CLASS = null;
  CUNIRANK_PATHS_CLASS = null;
  CUNIRANK_STANDARD_CHARSETS_CLASS = null;
}

var CUNIRANK_TEAM_NAMES = {
  owner: 'c00_owner',
  admin: 'c01_admin',
  moderador: 'c02_mod',
  helper: 'c03_helper',
  eclipse: 'c04_eclipse',
  cuniculus: 'c05_cuniculus',
  arconte: 'c06_arconte',
  sabio: 'c07_sabio',
  centinela: 'c08_centinela',
  tunelero: 'c09_tunelero',
  raiz: 'c10_raiz',
  tierra: 'c11_tierra',
  tiktok: 'c12_tiktok',
  youtuber: 'c13_youtube',
  kick: 'c14_kick',
  twitch: 'c15_twitch',
  facebook: 'c16_fb',
  madriguero: 'c17_mad'
};

var CUNIRANK_LEGACY_TEAM_NAMES = [
  'cuni_owner',
  'cuni_admin',
  'cuni_builder',
  'cuni_mod',
  'cuni_helper',
  'cuni_eclipse',
  'cuni_cuniculus',
  'cuni_arconte',
  'cuni_sabio',
  'cuni_centinela',
  'cuni_tunelero',
  'cuni_raiz',
  'cuni_tierra',
  'cuni_tiktok',
  'cuni_youtube',
  'cuni_kick',
  'cuni_twitch',
  'cuni_fb',
  'cuni_mad'
];

function cunirankSafePlayer(name) {
  var value = String(name || '').trim();
  return /^[A-Za-z0-9_]{1,16}$/.test(value) ? value : '';
}

function cunirankSafeRank(rank) {
  var value = String(rank || '').trim().toLowerCase();
  return /^[a-z0-9_]+$/.test(value) ? value : '';
}

function cunirankQuoted(value) {
  return '"' + String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
}

function cunirankTabUsersPath() {
  if (!CUNIRANK_PATHS_CLASS) {
    return null;
  }
  try {
    return CUNIRANK_PATHS_CLASS.get('config', 'tab', 'users.yml');
  } catch (ignored) {
    return null;
  }
}

function cunirankAssignmentsPath() {
  if (!CUNIRANK_PATHS_CLASS) {
    return null;
  }
  try {
    return CUNIRANK_PATHS_CLASS.get('config', 'cuniculum', 'cunirank_assignments.json');
  } catch (ignored) {
    return null;
  }
}

function cunirankFtbranksPlayersPath() {
  if (!CUNIRANK_PATHS_CLASS) {
    return null;
  }
  try {
    return CUNIRANK_PATHS_CLASS.get('world', 'serverconfig', 'ftbranks', 'players.snbt');
  } catch (ignored) {
    return null;
  }
}

function cunirankReadText(path) {
  if (!path || !CUNIRANK_FILES_CLASS || !CUNIRANK_STANDARD_CHARSETS_CLASS) {
    return '';
  }
  try {
    if (!CUNIRANK_FILES_CLASS.exists(path)) {
      return '';
    }
    return String(CUNIRANK_FILES_CLASS.readString(path, CUNIRANK_STANDARD_CHARSETS_CLASS.UTF_8));
  } catch (ignored) {
    return '';
  }
}

function cunirankWriteText(path, text) {
  if (!path || !CUNIRANK_FILES_CLASS || !CUNIRANK_STANDARD_CHARSETS_CLASS) {
    return false;
  }
  try {
    var parent = path.getParent();
    if (parent && !CUNIRANK_FILES_CLASS.exists(parent)) {
      CUNIRANK_FILES_CLASS.createDirectories(parent);
    }
    CUNIRANK_FILES_CLASS.writeString(path, String(text || ''), CUNIRANK_STANDARD_CHARSETS_CLASS.UTF_8);
    return true;
  } catch (ignored) {
    return false;
  }
}

function cunirankFtbranksFileRanks(playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!safeName) {
    return [];
  }

  var raw = cunirankReadText(cunirankFtbranksPlayersPath());
  if (!raw) {
    return [];
  }

  var ranks = [];
  var marker = 'name: "' + safeName + '"';
  var lowerRaw = raw.toLowerCase();
  var markerLower = marker.toLowerCase();
  var searchFrom = 0;
  var idx = -1;

  while ((idx = lowerRaw.indexOf(markerLower, searchFrom)) >= 0) {
    var nextName = lowerRaw.indexOf('name: "', idx + markerLower.length);
    var sectionEnd = nextName > idx ? nextName : Math.min(raw.length, idx + 2200);
    var section = raw.substring(idx, sectionEnd);
    var ranksIdx = section.indexOf('ranks:');
    searchFrom = idx + markerLower.length;

    if (ranksIdx < 0) {
      continue;
    }

    var openIdx = section.indexOf('{', ranksIdx);
    if (openIdx < 0) {
      continue;
    }

    var depth = 0;
    var endIdx = -1;
    for (var i = openIdx; i < section.length; i++) {
      var ch = section.charAt(i);
      if (ch === '{') {
        depth++;
      } else if (ch === '}') {
        depth--;
        if (depth === 0) {
          endIdx = i;
          break;
        }
      }
    }

    if (endIdx < 0) {
      continue;
    }

    var rankBlock = section.substring(openIdx + 1, endIdx);
    var match;
    var rankPattern = /([A-Za-z0-9_]+)\s*:/g;
    while ((match = rankPattern.exec(rankBlock)) !== null) {
      var rank = cunirankSafeRank(match[1]);
      if (cunirankIsKnownRank(rank) && ranks.indexOf(rank) < 0) {
        ranks.push(rank);
      }
    }
  }

  return ranks;
}

function cunirankReadAssignments() {
  var path = cunirankAssignmentsPath();
  var raw = cunirankReadText(path);
  if (!raw) {
    return {};
  }

  try {
    var parsed = JSON.parse(raw);
    return parsed && typeof parsed === 'object' ? parsed : {};
  } catch (ignored) {
    return {};
  }
}

function cunirankWriteAssignments(data) {
  var path = cunirankAssignmentsPath();
  if (!path) {
    return false;
  }
  return cunirankWriteText(path, JSON.stringify(data || {}, null, 2));
}

function cunirankAssignmentRanks(playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!safeName) {
    return [];
  }

  var data = cunirankReadAssignments();
  var key = safeName.toLowerCase();
  var value = data[key];
  var ranks = [];
  if (Array.isArray(value)) {
    value.forEach(function (entry) {
      var rank = cunirankSafeRank(entry);
      if (cunirankIsKnownRank(rank) && ranks.indexOf(rank) < 0) {
        ranks.push(rank);
      }
    });
  } else {
    var rank = cunirankSafeRank(value);
    if (cunirankIsKnownRank(rank)) {
      ranks.push(rank);
    }
  }
  return ranks;
}

function cunirankSaveAssignmentRanks(playerName, ranks) {
  var safeName = cunirankSafePlayer(playerName);
  if (!safeName) {
    return false;
  }

  var clean = [];
  (ranks || []).forEach(function (entry) {
    var rank = cunirankSafeRank(entry);
    if (cunirankIsKnownRank(rank) && clean.indexOf(rank) < 0) {
      clean.push(rank);
    }
  });

  var data = cunirankReadAssignments();
  var key = safeName.toLowerCase();
  if (clean.length) {
    data[key] = clean;
  } else {
    delete data[key];
  }
  return cunirankWriteAssignments(data);
}

function cunirankRemoveTabUserBlock(text, playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!safeName) {
    return String(text || '');
  }
  var lines = String(text || '').replace(/\r\n/g, '\n').split('\n');
  var out = [];
  var skipping = false;
  for (var i = 0; i < lines.length; i++) {
    var line = lines[i];
    if (!skipping && line === safeName + ':') {
      skipping = true;
      continue;
    }
    if (skipping) {
      if (line && !/^\s/.test(line)) {
        skipping = false;
      } else {
        continue;
      }
    }
    if (!skipping) {
      out.push(line);
    }
  }
  return out.join('\n').replace(/^\s*\{\}\s*$/g, '').replace(/\n{3,}/g, '\n\n').trim();
}

function cunirankWriteTabUserVisual(playerName, rank) {
  var safeName = cunirankSafePlayer(playerName);
  var path = cunirankTabUsersPath();
  if (!safeName || !path) {
    return false;
  }

  // No persistimos visuales por jugador en users.yml. TAB se sincroniza en vivo
  // desde /cunirank para evitar que un valor viejo vuelva a pisar el rango real.
  var text = cunirankRemoveTabUserBlock(cunirankReadText(path), safeName);
  return cunirankWriteText(path, text ? text + '\n' : '{}\n');
}

function cunirankApplyTabRuntime(server, playerName, rank) {
  var safeName = cunirankSafePlayer(playerName);
  var safeRank = cunirankIsVisual(rank) ? rank : 'madriguero';
  if (!server || !safeName) {
    return false;
  }
  var prefix = CUNIRANK_VISUALS[safeRank] || CUNIRANK_VISUALS.madriguero;
  server.runCommandSilent('tab player ' + safeName + ' tabprefix ' + cunirankQuoted(prefix));
  server.runCommandSilent('tab player ' + safeName + ' tagprefix ' + cunirankQuoted(prefix));
  server.runCommandSilent('tab player ' + safeName + ' customtabname ' + cunirankQuoted(prefix + '%player%'));
  server.runCommandSilent('tab player ' + safeName + ' tabsuffix ' + cunirankQuoted(''));
  server.runCommandSilent('tab player ' + safeName + ' tagsuffix ' + cunirankQuoted(''));
  return true;
}

function cunirankIsStaffVisualRank(rank) {
  return ['owner', 'admin', 'moderador', 'helper'].indexOf(cunirankSafeRank(rank)) >= 0;
}

function cunirankApplyTabScoreboard(server, playerName, rank) {
  var safeName = cunirankSafePlayer(playerName);
  var safeRank = cunirankIsVisual(rank) ? cunirankSafeRank(rank) : 'madriguero';
  if (!server || !safeName) {
    return false;
  }
  var board = safeRank + '-panel';
  server.runCommandSilent('tab scoreboard on ' + safeName);
  server.runCommandSilent('tab scoreboard show ' + board + ' ' + safeName);
  return true;
}

function cunirankApplyTabRuntimeBundle(server, playerName, rank) {
  cunirankApplyTabRuntime(server, playerName, rank);
  cunirankApplyTabScoreboard(server, playerName, rank);
  try {
    server.scheduleInTicks(40, function () {
      cunirankApplyTabRuntime(server, playerName, rank);
      cunirankApplyTabScoreboard(server, playerName, rank);
    });
  } catch (ignoredSchedule) {
  }
}

function cunirankClearBelowNameHealth(server) {
  if (!server) {
    return;
  }
  server.runCommandSilent('scoreboard objectives setdisplay below_name');
}

function cunirankSyncOnlineVisuals(server) {
  if (!server || !server.players) {
    return;
  }

  server.players.forEach(function (player) {
    var safeName = cunirankSafePlayer(player.username);
    if (!safeName) {
      return;
    }
    cunirankApplyVisual(server, safeName, cunirankRankForPlayer(player, server));
  });
}

function cunirankSourceCanHold(source) {
  if (!source) return false;
  try {
    if (source.hasPermission && source.hasPermission(2)) return true;
  } catch (ignoredPermission) {
  }
  try {
    var server = source.getServer();
    var player = source.getPlayer();
    var rank = cunirankRankForPlayer(player, server);
    return ['owner', 'admin', 'moderador', 'helper'].indexOf(rank) >= 0;
  } catch (ignoredPlayer) {
    return false;
  }
}

function cunirankParseHoldSeconds(raw) {
  var value = parseInt(String(raw || '').trim(), 10);
  if (!isFinite(value) || value <= 0) return 30;
  return Math.max(5, Math.min(600, value));
}

function cunirankHoldTell(server, playerName, text, color) {
  if (!server || !playerName) return;
  server.runCommandSilent('tellraw ' + playerName + ' ' + JSON.stringify({
    text: text,
    color: color || 'gold',
    italic: false
  }));
}

function cunirankHoldPlayer(ctx, targetName, secondsRaw) {
  var source = ctx.getSource();
  var server = source.getServer();
  if (!cunirankSourceCanHold(source)) {
    var deniedName = '';
    try { deniedName = cunirankSafePlayer(source.getPlayer().username); } catch (ignoredDenied) { }
    cunirankHoldTell(server, deniedName, '[CUNICULUM] No tienes permiso para retener jugadores.', 'red');
    return 0;
  }

  var target = cunirankSafePlayer(targetName);
  var seconds = cunirankParseHoldSeconds(secondsRaw);
  if (!target) return 0;

  server.runCommandSilent('effect give ' + target + ' minecraft:slowness ' + seconds + ' 255 true');
  server.runCommandSilent('effect give ' + target + ' minecraft:mining_fatigue ' + seconds + ' 4 true');
  server.runCommandSilent('effect give ' + target + ' minecraft:weakness ' + seconds + ' 4 true');
  cunirankHoldTell(server, target, '[CUNICULUM] Quedaste retenido por staff durante ' + seconds + 's.', 'yellow');

  try {
    var actor = cunirankSafePlayer(source.getPlayer().username);
    cunirankHoldTell(server, actor, '[CUNICULUM] Retencion aplicada a ' + target + ' por ' + seconds + 's.', 'green');
  } catch (ignoredActor) {
  }
  return 1;
}

function cunirankReleasePlayer(ctx, targetName) {
  var source = ctx.getSource();
  var server = source.getServer();
  if (!cunirankSourceCanHold(source)) {
    var deniedName = '';
    try { deniedName = cunirankSafePlayer(source.getPlayer().username); } catch (ignoredDenied) { }
    cunirankHoldTell(server, deniedName, '[CUNICULUM] No tienes permiso para liberar retenciones.', 'red');
    return 0;
  }

  var target = cunirankSafePlayer(targetName);
  if (!target) return 0;

  server.runCommandSilent('effect clear ' + target + ' minecraft:slowness');
  server.runCommandSilent('effect clear ' + target + ' minecraft:mining_fatigue');
  server.runCommandSilent('effect clear ' + target + ' minecraft:weakness');
  cunirankHoldTell(server, target, '[CUNICULUM] La retencion de staff fue retirada.', 'green');
  return 1;
}

function cunirankStorageKey(playerName) {
  return 'cuniculum_rank_' + String(playerName || '').toLowerCase();
}

function cunirankRankListKey(playerName) {
  return 'cuniculum_rank_list_' + String(playerName || '').toLowerCase();
}

function cunirankInitializedKey(playerName) {
  return CUNIRANK_INITIALIZED_TAG_PREFIX + String(playerName || '').toLowerCase();
}

function cunirankTempKey(playerName, rank) {
  return 'cuniculum_rank_temp_' + String(playerName || '').toLowerCase() + '_' + String(rank || '').toLowerCase();
}

function cunirankOnlinePlayer(server, playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return null;
  }

  try {
    var found = server.getPlayerList().getPlayerByName(safeName);
    if (found) {
      return found;
    }
  } catch (ignored) {
  }

  try {
    var foundAlt = server.playerList.getPlayerByName(safeName);
    if (foundAlt) {
      return foundAlt;
    }
  } catch (ignored) {
  }

  var players = null;
  try { players = server.players; } catch (ignored) { }
  if (!players) {
    return null;
  }

  var size = 0;
  try { size = players.size(); } catch (ignored) { }
  try { if (!size && players.length) { size = players.length; } } catch (ignored) { }
  for (var i = 0; i < size; i++) {
    var player = null;
    try { player = players.get(i); } catch (ignored) { }
    try { if (!player) { player = players[i]; } } catch (ignored) { }
    if (!player) {
      continue;
    }

    var names = [];
    try { names.push(player.username); } catch (ignored) { }
    try { names.push(player.getGameProfile().getName()); } catch (ignored) { }
    try { names.push(player.name); } catch (ignored) { }
    for (var j = 0; j < names.length; j++) {
      if (String(names[j] || '').toLowerCase() === safeName.toLowerCase()) {
        return player;
      }
    }
  }

  return null;
}

function cunirankServer(ctx) {
  try {
    return ctx.getSource().getServer();
  } catch (ignored) {
  }

  try {
    return ctx.source.server;
  } catch (ignored) {
  }

  return null;
}

function cunirankFeedback(server, message) {
  if (!server) {
    return;
  }

  var feedbackText = '[CUNICULUM] ' + message;
  try {
    server.tell(feedbackText);
    return;
  } catch (ignored) {
  }

  server.runCommandSilent('tellraw @a ' + JSON.stringify({ text: feedbackText, color: 'gold' }));
}

function cunirankIsVisual(rank) {
  return Object.prototype.hasOwnProperty.call(CUNIRANK_VISUALS, rank);
}

function cunirankIsFunctionalOnly(rank) {
  return CUNIRANK_FUNCTIONAL_ONLY.includes(rank);
}

function cunirankIsKnownRank(rank) {
  return cunirankIsVisual(rank) || cunirankIsFunctionalOnly(rank);
}

function cunirankApplyVisual(server, playerName, rank) {
  var safeName = cunirankSafePlayer(playerName);
  var safeRank = cunirankSafeRank(rank);
  if (!server || !safeName || !cunirankIsVisual(safeRank)) {
    return false;
  }

  cunirankWriteTabUserVisual(safeName, safeRank);
  cunirankApplyTabRuntimeBundle(server, safeName, safeRank);
  return true;
}

function cunirankSyncOpState(server, playerName, rank) {
  var safeName = cunirankSafePlayer(playerName);
  var safeRank = cunirankSafeRank(rank);
  if (!server || !safeName) {
    return;
  }

  if (safeRank === 'owner') {
    server.runCommandSilent('op ' + safeName);
  } else {
    server.runCommandSilent('deop ' + safeName);
  }
}

function cunirankClearLegacyVanillaTeams(server) {
  if (!server) {
    return;
  }

  CUNIRANK_LEGACY_TEAM_NAMES.forEach(function (teamName) {
    server.runCommandSilent('team remove ' + teamName);
  });
}

function cunirankEnsureRankTeams(server) {
  if (!server) {
    return;
  }

  CUNIRANK_CHAT_PRIORITY.forEach(function (rank) {
    var teamName = CUNIRANK_TEAM_NAMES[rank];
    if (!teamName) {
      return;
    }
    var visual = CUNIRANK_CHAT_VISUALS[rank] || CUNIRANK_CHAT_VISUALS.madriguero;
    var prefix = JSON.stringify({ text: visual.tag + ' ', color: visual.color || 'gray' });
    server.runCommandSilent('team add ' + teamName);
    server.runCommandSilent('team modify ' + teamName + ' prefix ' + prefix);
    server.runCommandSilent('team modify ' + teamName + ' suffix {"text":""}');
    server.runCommandSilent('team modify ' + teamName + ' nametagVisibility always');
    server.runCommandSilent('team modify ' + teamName + ' collisionRule always');
  });
}

function cunirankDebugPlayer(server, playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return false;
  }

  server.runCommandSilent(`ftbranks list_ranks_of ${safeName}`);
  server.runCommandSilent(`tab debug ${safeName}`);
  server.runCommandSilent(`tab parse ${safeName} %group%`);
  server.runCommandSilent(`tellraw ${safeName} ${JSON.stringify({ text: '[CUNICULUM] Chat sale de FTB Ranks. TAB se sincroniza en vivo con /cunirank y /tab player.', color: 'yellow' })}`);
  server.runCommandSilent(`tellraw ${safeName} ${JSON.stringify({ text: '[CUNICULUM] Diagnostico: si TAB debug muestra grupo NONE, no es fallo del rango; /cunirank mantiene prefijo, nombre visual y panel dinamicos.', color: 'gold' })}`);
  return true;
}

function cunirankRefreshRanks(server) {
  // TAB is updated through runtime commands. Reloading TAB here re-applies stale
  // users.yml data and can downgrade existing players to the fallback rank.
  return;
}

function cunirankBestVisualRank(ranks) {
  if (!ranks || !ranks.length) {
    return '';
  }

  for (var i = 0; i < CUNIRANK_CHAT_PRIORITY.length; i++) {
    var rank = CUNIRANK_CHAT_PRIORITY[i];
    if (ranks.indexOf(rank) >= 0) {
      return rank;
    }
  }

  return '';
}

function cunirankStoredRankList(server, playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return [];
  }

  var ranks = [];
  try {
    var player = cunirankOnlinePlayer(server, safeName);
    if (player) {
      var playerRaw = String(player.persistentData[CUNIRANK_PLAYER_RANK_LIST_TAG] || '');
      playerRaw.split(',').forEach(function (entry) {
        var rank = cunirankSafeRank(entry);
        if (cunirankIsKnownRank(rank) && ranks.indexOf(rank) < 0) {
          ranks.push(rank);
        }
      });

      var playerRank = cunirankSafeRank(player.persistentData[CUNIRANK_PLAYER_RANK_TAG]);
      if (cunirankIsVisual(playerRank) && ranks.indexOf(playerRank) < 0) {
        ranks.push(playerRank);
      }
    }
  } catch (ignored) {
  }

  try {
    var raw = String(server.persistentData[cunirankRankListKey(safeName)] || '');
    raw.split(',').forEach(function (entry) {
      var rank = cunirankSafeRank(entry);
      if (cunirankIsKnownRank(rank) && ranks.indexOf(rank) < 0) {
        ranks.push(rank);
      }
    });
  } catch (ignored) {
  }

  cunirankFtbranksFileRanks(safeName).forEach(function (rank) {
    if (ranks.indexOf(rank) < 0) {
      ranks.push(rank);
    }
  });

  cunirankAssignmentRanks(safeName).forEach(function (rank) {
    if (ranks.indexOf(rank) < 0) {
      ranks.push(rank);
    }
  });

  try {
    var oldRank = cunirankSafeRank(server.persistentData[cunirankStorageKey(safeName)]);
    if (cunirankIsVisual(oldRank) && ranks.indexOf(oldRank) < 0) {
      ranks.push(oldRank);
    }
  } catch (ignored) {
  }

  return ranks;
}

function cunirankSaveStoredRankList(server, playerName, ranks) {
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return;
  }

  var clean = [];
  (ranks || []).forEach(function (entry) {
    var rank = cunirankSafeRank(entry);
    if (cunirankIsKnownRank(rank) && clean.indexOf(rank) < 0) {
      clean.push(rank);
    }
  });

  try {
    server.persistentData[cunirankRankListKey(safeName)] = clean.join(',');
  } catch (ignored) {
  }

  cunirankSaveAssignmentRanks(safeName, clean);

  try {
    var player = cunirankOnlinePlayer(server, safeName);
    if (player) {
      player.persistentData[CUNIRANK_PLAYER_RANK_LIST_TAG] = clean.join(',');
      var best = cunirankBestVisualRank(clean);
      if (best) {
        player.persistentData[CUNIRANK_PLAYER_RANK_TAG] = best;
      }
    }
  } catch (ignored) {
  }
}

function cunirankAddStoredRank(server, playerName, rank) {
  var safeRank = cunirankSafeRank(rank);
  if (!cunirankIsKnownRank(safeRank)) {
    return;
  }

  var ranks = cunirankStoredRankList(server, playerName);
  if (cunirankIsVisual(safeRank) && safeRank !== 'madriguero') {
    ranks = ranks.filter(function (entry) {
      return entry !== 'madriguero';
    });
  }
  if (safeRank === 'madriguero' && cunirankBestVisualRank(ranks) && cunirankBestVisualRank(ranks) !== 'madriguero') {
    return;
  }
  if (ranks.indexOf(safeRank) < 0) {
    ranks.push(safeRank);
  }
  cunirankSaveStoredRankList(server, playerName, ranks);
}

function cunirankRemoveStoredRank(server, playerName, rank) {
  var safeRank = cunirankSafeRank(rank);
  var ranks = cunirankStoredRankList(server, playerName).filter(function (entry) {
    return entry !== safeRank;
  });
  cunirankSaveStoredRankList(server, playerName, ranks);

  try {
    delete server.persistentData[cunirankTempKey(playerName, safeRank)];
  } catch (ignored) {
  }
}

function cunirankClearStoredVisualRanks(server, playerName) {
  var ranks = cunirankStoredRankList(server, playerName).filter(function (rank) {
    return !cunirankIsVisual(rank);
  });
  cunirankSaveStoredRankList(server, playerName, ranks);
}

function cunirankDurationMs(rawDuration) {
  var raw = String(rawDuration || '').trim().toLowerCase();
  var match = raw.match(/^([0-9]+)\s*(s|seg|segundo|segundos|min|m|minuto|minutos|h|hora|horas|d|dia|dias|w|sem|semana|semanas|mes|meses|mo|a|ano|anos|y|year|years)$/);
  if (!match) {
    return 0;
  }

  var amount = Math.max(1, Math.floor(Number(match[1])));
  var unit = match[2];
  if (unit === 's' || unit === 'seg' || unit === 'segundo' || unit === 'segundos') { return amount * 1000; }
  if (unit === 'm' || unit === 'min' || unit === 'minuto' || unit === 'minutos') { return amount * 60 * 1000; }
  if (unit === 'h' || unit === 'hora' || unit === 'horas') { return amount * 60 * 60 * 1000; }
  if (unit === 'd' || unit === 'dia' || unit === 'dias') { return amount * 24 * 60 * 60 * 1000; }
  if (unit === 'w' || unit === 'sem' || unit === 'semana' || unit === 'semanas') { return amount * 7 * 24 * 60 * 60 * 1000; }
  if (unit === 'mes' || unit === 'meses' || unit === 'mo') { return amount * 30 * 24 * 60 * 60 * 1000; }
  if (unit === 'a' || unit === 'ano' || unit === 'anos' || unit === 'y' || unit === 'year' || unit === 'years') { return amount * 365 * 24 * 60 * 60 * 1000; }
  return 0;
}

function cunirankDurationHelp() {
  return 'Usa duraciones como 30min, 2h, 1d, 1sem, 1mes o 1a.';
}

function cunirankExpireRanksForName(server, playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return [];
  }

  var expired = [];
  var now = Date.now();
  var ranks = cunirankStoredRankList(server, safeName);
  ranks.forEach(function (rank) {
    var expiry = 0;
    try { expiry = Number(server.persistentData[cunirankTempKey(safeName, rank)] || 0); } catch (ignored) { }
    if (expiry > 0 && expiry <= now) {
      expired.push(rank);
    }
  });

  expired.forEach(function (rank) {
    server.runCommandSilent(`ftbranks remove ${safeName} ${rank}`);
    cunirankRemoveStoredRank(server, safeName, rank);
  });

  return expired;
}

function cunirankPlayerIsOp(player, server) {
  if (!player || !server) {
    return false;
  }

  try {
    return !!server.playerList.isOp(player.getGameProfile());
  } catch (ignored) {
  }

  try {
    return !!server.getPlayerList().isOp(player.getGameProfile());
  } catch (ignored) {
  }

  return false;
}

function cunirankPlayerHasRank(player, rank, server) {
  if (!player || !rank) {
    return false;
  }

  if (cunirankPlayerIsOp(player, server)) {
    return false;
  }

  try {
    if (player.hasPermission('cuniculum.rank.' + rank)) {
      return true;
    }
  } catch (ignored) {
  }

  return false;
}

function cunirankPermissionRankForPlayer(player, server) {
  if (!player) {
    return '';
  }

  for (var i = 0; i < CUNIRANK_CHAT_PRIORITY.length; i++) {
    var rank = CUNIRANK_CHAT_PRIORITY[i];
    if (rank === 'madriguero') {
      continue;
    }
    if (cunirankPlayerHasRank(player, rank, server)) {
      return rank;
    }
  }

  return '';
}

function cunirankStoredRank(server, playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return '';
  }

  var storedBest = cunirankBestVisualRank(cunirankStoredRankList(server, safeName));
  if (storedBest) {
    return storedBest;
  }

  return '';
}

function cunirankHasInitializedRank(server, playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return false;
  }

  try {
    if (String(server.persistentData[cunirankInitializedKey(safeName)] || '') === '1') {
      return true;
    }
  } catch (ignored) {
  }

  return cunirankStoredRankList(server, safeName).length > 0;
}

function cunirankMarkInitialized(server, playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return;
  }

  try {
    server.persistentData[cunirankInitializedKey(safeName)] = '1';
  } catch (ignored) {
  }
}

function cunirankRankForPlayer(player, server) {
  if (!player) {
    return 'madriguero';
  }

  var safeName = cunirankSafePlayer(player.username);
  cunirankExpireRanksForName(server, safeName);

  var permissionRank = cunirankPermissionRankForPlayer(player, server);
  if (permissionRank && permissionRank !== 'madriguero') {
    return permissionRank;
  }

  var stored = cunirankStoredRank(server, safeName);
  if (stored && stored !== 'madriguero') {
    return stored;
  }

  if (permissionRank) {
    return permissionRank;
  }
  if (stored) {
    return stored;
  }

  return 'madriguero';
}

function cunirankRankForName(server, playerName) {
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return 'madriguero';
  }

  cunirankExpireRanksForName(server, safeName);

  var player = cunirankOnlinePlayer(server, safeName);
  if (player) {
    return cunirankRankForPlayer(player, server);
  }

  var stored = cunirankStoredRank(server, safeName);
  if (stored && stored !== 'madriguero') {
    return stored;
  }
  return stored || 'madriguero';
}

function cunirankSyncVisual(server, playerName, forcedRank) {
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return '';
  }

  var forced = cunirankSafeRank(forcedRank);
  var rank = '';
  if (cunirankIsVisual(forced)) {
    var ranked = cunirankStoredRankList(server, safeName);
    if (ranked.indexOf(forced) < 0) {
      ranked.push(forced);
    }
    cunirankSaveStoredRankList(server, safeName, ranked);
    rank = cunirankBestVisualRank(ranked) || forced;
  } else {
    rank = cunirankRankForName(server, safeName);
  }
  cunirankApplyVisual(server, safeName, rank, false);
  cunirankSyncOpState(server, safeName, rank);
  return rank;
}

function cunirankRawChatMessage(event) {
  var values = [];
  try { values.push(event.getMessage()); } catch (ignored) { }
  try { values.push(event.message.getString()); } catch (ignored) { }
  try { values.push(event.getComponent().getString()); } catch (ignored) { }
  try { values.push(event.component.getString()); } catch (ignored) { }
  try { values.push(event.rawText); } catch (ignored) { }
  try { values.push(event.message); } catch (ignored) { }

  for (var i = 0; i < values.length; i++) {
    var chatText = String(values[i] || '');
    if (chatText && chatText !== 'undefined' && chatText !== 'null' && chatText.indexOf('chat.type.') !== 0 && chatText.indexOf('[object ') !== 0) {
      return chatText;
    }
  }

  return '';
}

function cunirankChatTellraw(playerName, rank, message) {
  var visual = CUNIRANK_CHAT_VISUALS[rank] || CUNIRANK_CHAT_VISUALS.madriguero;
  return JSON.stringify([
    { text: visual.tag + ' ', color: visual.color, bold: true, italic: false },
    { text: playerName, color: 'white', bold: false, italic: false },
    { text: ': ', color: 'dark_gray', bold: false, italic: false },
    { text: message, color: 'white', bold: false, italic: false }
  ]);
}

function cunirankPlainChat(playerName, rank, message) {
  var visual = CUNIRANK_CHAT_VISUALS[rank] || CUNIRANK_CHAT_VISUALS.madriguero;
  return visual.tag + ' ' + playerName + ': ' + message;
}

function cunirankSetPlainChatFallback(event, playerName, rank, message) {
  var plainChatText = cunirankPlainChat(playerName, rank, message);

  try {
    event.setMessage(plainChatText);
    return true;
  } catch (ignored) {
  }

  try {
    if (CUNIRANK_COMPONENT_CLASS) {
      event.setComponent(CUNIRANK_COMPONENT_CLASS.literal(plainChatText));
      return true;
    }
  } catch (ignored) {
  }

  return false;
}

function cunirankHelp(ctx) {
  var server = cunirankServer(ctx);
  cunirankFeedback(server, 'Comandos: /cunirank add <jugador> <rango> | remove <jugador> <rango> | set <jugador> <rango> | sync <jugador> [rango] | addtemp <jugador> <rango> <duracion> | debug <jugador> | roles.');
  return 1;
}

function cunirankRoles(ctx) {
  var server = cunirankServer(ctx);
  cunirankFeedback(server, 'Rangos validos: ' + CUNIRANK_ROLE_LIST.join(', ') + '. Chat usa FTB Ranks; TAB se sincroniza desde /cunirank y se ordena por equipo interno de rango.');
  return 1;
}

function cunirankTest(ctx, playerName) {
  var server = cunirankServer(ctx);
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return 0;
  }

  cunirankFeedback(server, `Diagnostico para ${safeName}: ejecuta /ftbranks list_ranks_of ${safeName} y /tab debug ${safeName}.`);
  return 1;
}

function cunirankDebug(ctx, playerName) {
  var server = cunirankServer(ctx);
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return 0;
  }

  cunirankDebugPlayer(server, safeName);
  cunirankFeedback(server, `Diagnostico ejecutado para ${safeName}: FTB Ranks, TAB debug y TAB parse %group%.`);
  return 1;
}

function cunirankSyncOnly(ctx, playerName) {
  var server = cunirankServer(ctx);
  var safeName = cunirankSafePlayer(playerName);
  if (!server || !safeName) {
    return 0;
  }

  cunirankRefreshRanks(server);
  var rank = cunirankRankForName(server, safeName);
  var storedRank = cunirankStoredRank(server, safeName);
  if ((!rank || rank === 'madriguero') && storedRank && storedRank !== 'madriguero') {
    rank = storedRank;
  }
  if (!rank || !cunirankIsVisual(rank)) {
    cunirankFeedback(server, `No cambie visual de ${safeName}. Usa /cunirank sync ${safeName} <rango> si quieres forzar una sincronizacion concreta.`);
    return 1;
  }
  rank = cunirankSyncVisual(server, safeName, rank);
  cunirankFeedback(server, `Rango de ${safeName} sincronizado como ${rank} por FTB Ranks/TAB.`);
  return 1;
}

function cunirankApplyLoginVisual(server, player, safeName, allowBaseAssign) {
  if (!server || !player || !safeName) {
    return false;
  }

  cunirankExpireRanksForName(server, safeName);

  var storedRank = cunirankStoredRank(server, safeName);
  var loginRank = cunirankRankForPlayer(player, server);
  if (cunirankIsVisual(loginRank) && loginRank !== 'madriguero') {
    cunirankSyncVisual(server, safeName, loginRank);
    cunirankMarkInitialized(server, safeName);
    return true;
  }

  if (cunirankIsVisual(storedRank)) {
    cunirankSyncVisual(server, safeName, storedRank);
    cunirankMarkInitialized(server, safeName);
    return true;
  }

  if (allowBaseAssign && !cunirankHasInitializedRank(server, safeName)) {
    server.runCommandSilent(`ftbranks add ${safeName} madriguero`);
    cunirankAddStoredRank(server, safeName, 'madriguero');
    cunirankSyncVisual(server, safeName, 'madriguero');
    cunirankMarkInitialized(server, safeName);
    return true;
  }

  if (cunirankHasInitializedRank(server, safeName)) {
    cunirankApplyTabScoreboard(server, safeName, cunirankRankForName(server, safeName));
  }

  return false;
}

function cunirankRunTimed(ctx, playerName, rank, duration) {
  var server = cunirankServer(ctx);
  var safeName = cunirankSafePlayer(playerName);
  var safeRank = cunirankSafeRank(rank);
  var ms = cunirankDurationMs(duration);
  if (!server || !safeName) {
    return 0;
  }
  if (!safeRank || !cunirankIsKnownRank(safeRank)) {
    cunirankFeedback(server, `Rango invalido: ${safeRank || rank}. Usa /cunirank roles.`);
    return 0;
  }
  if (!ms) {
    cunirankFeedback(server, `Duracion invalida: ${duration}. ${cunirankDurationHelp()}`);
    return 0;
  }

  var expiresAt = Date.now() + ms;
  server.runCommandSilent(`ftbranks add ${safeName} ${safeRank}`);
  cunirankAddStoredRank(server, safeName, safeRank);
  try {
    server.persistentData[cunirankTempKey(safeName, safeRank)] = expiresAt;
  } catch (ignored) {
  }

  cunirankRefreshRanks(server);
  var effectiveRank = cunirankSyncVisual(server, safeName, safeRank);
  cunirankFeedback(server, `Rango temporal ${safeRank} agregado a ${safeName} por ${duration}. Rango efectivo: ${effectiveRank}. Expira automaticamente al entrar/sincronizar despues del plazo.`);
  return 1;
}

function cunirankRun(ctx, action, playerName, rank) {
  var server = cunirankServer(ctx);
  var safeName = cunirankSafePlayer(playerName);
  var safeRank = cunirankSafeRank(rank);
  if (!server || !safeName) {
    return 0;
  }

  if (!safeRank || !cunirankIsKnownRank(safeRank)) {
    cunirankFeedback(server, `Rango invalido: ${safeRank || rank}. Usa /cunirank roles.`);
    return 0;
  }

  if (action === 'sync' && cunirankIsFunctionalOnly(safeRank)) {
    cunirankFeedback(server, `El rango ${safeRank} es funcional y no cambia el grupo visible de TAB.`);
    return 0;
  }

  if (action === 'sync') {
    if (safeRank) {
      server.runCommandSilent(`ftbranks add ${safeName} ${safeRank}`);
      cunirankAddStoredRank(server, safeName, safeRank);
    }
    cunirankRefreshRanks(server);
    var syncedRank = cunirankSyncVisual(server, safeName, safeRank);
    var syncClaims = CUNIRANK_EXPECTED_CLAIMS[syncedRank] || 'segun rango FTB';
    cunirankFeedback(server, `Rango de ${safeName} sincronizado como ${syncedRank}. Claims esperados: ${syncClaims}. Si OPAC aun muestra limite viejo, ${safeName} debe salir y entrar.`);
    return 1;
  }

  if (action === 'add' || action === 'set' || action === 'apply') {
    if (action === 'set' && cunirankIsVisual(safeRank)) {
      CUNIRANK_CHAT_PRIORITY.forEach(function (visualRank) {
        if (visualRank !== 'madriguero') {
          server.runCommandSilent(`ftbranks remove ${safeName} ${visualRank}`);
        }
      });
      cunirankClearStoredVisualRanks(server, safeName);
    }

    server.runCommandSilent(`ftbranks add ${safeName} ${safeRank}`);
    cunirankAddStoredRank(server, safeName, safeRank);
    cunirankRefreshRanks(server);

    if (cunirankIsFunctionalOnly(safeRank)) {
      var functionalRank = cunirankSyncVisual(server, safeName);
      cunirankFeedback(server, `Permiso ${safeRank} agregado a ${safeName}. Rango actual: ${functionalRank}.`);
      return 1;
    }

    var effectiveRank = cunirankSyncVisual(server, safeName, safeRank);
    if (effectiveRank === 'owner') {
      cunirankFeedback(server, `Rango ${safeRank} agregado a ${safeName}. Rango efectivo: owner por FTB Ranks. Si OPAC aun muestra limite viejo, ${safeName} debe salir y entrar para recalcular el limite de claims.`);
      return 1;
    }

    if (effectiveRank === 'admin') {
      cunirankFeedback(server, `Rango ${safeRank} agregado a ${safeName}. Rango efectivo: admin. Si OPAC no actualiza el limite, ${safeName} debe salir y entrar.`);
      return 1;
    }

    cunirankFeedback(server, `Rango ${safeRank} agregado a ${safeName}. Rango efectivo: ${effectiveRank}.`);
    return 1;
  }

  if (action === 'remove') {
    server.runCommandSilent(`ftbranks remove ${safeName} ${safeRank}`);
    cunirankRemoveStoredRank(server, safeName, safeRank);
    cunirankRefreshRanks(server);

    if (cunirankIsFunctionalOnly(safeRank)) {
      var afterFunctionalRemove = cunirankSyncVisual(server, safeName);
      cunirankFeedback(server, `Permiso ${safeRank} removido de ${safeName}. Rango actual: ${afterFunctionalRemove}.`);
      return 1;
    }

    var afterRemoveRank = cunirankSyncVisual(server, safeName);
    if (safeRank === 'owner') {
      cunirankFeedback(server, `Rango owner removido de ${safeName}. Rango actual: ${afterRemoveRank}.`);
      return 1;
    }

    cunirankFeedback(server, `Rango ${safeRank} removido de ${safeName}. Rango actual: ${afterRemoveRank}.`);
    return 1;
  }

  return 0;
}

PlayerEvents.loggedIn(function (event) {
  var player = event.player;
  var server = event.server;
  if (!player || !server) {
    return;
  }

  var safeName = cunirankSafePlayer(player.username);
  if (!safeName) {
    return;
  }

  cunirankExpireRanksForName(server, safeName);
  cunirankClearBelowNameHealth(server);
  server.scheduleInTicks(20, function () {
    if (cunirankApplyLoginVisual(server, player, safeName, false)) {
      return;
    }
    server.scheduleInTicks(80, function () {
      cunirankApplyLoginVisual(server, player, safeName, true);
    });
  });
});

PlayerEvents.tick(function (event) {
  var player = event.player;
  var server = event.server;
  if (!player || !server) {
    return;
  }

  var safeName = cunirankSafePlayer(player.username);
  if (!safeName) {
    return;
  }

  if (Number(player.age || 0) % 1200 !== 0) {
    if (Number(player.age || 0) % 200 === 0) {
      cunirankApplyTabScoreboard(server, safeName, cunirankRankForPlayer(player, server));
    }
    return;
  }

  var expired = cunirankExpireRanksForName(server, safeName);
  if (expired.length) {
    var rank = cunirankSyncVisual(server, safeName);
    server.runCommandSilent(`tellraw ${safeName} ${JSON.stringify({ text: '[CUNICULUM] Rango temporal expirado: ' + expired.join(', ') + '. Rango actual: ' + rank + '.', color: 'gold' })}`);
  }
});

ServerEvents.loaded(function (event) {
  cunirankClearLegacyVanillaTeams(event.server);
  cunirankClearBelowNameHealth(event.server);
  cunirankSyncOnlineVisuals(event.server);
});

ServerEvents.commandRegistry(event => {
  var commands = event.commands;
  var args = event.arguments;
  var playerArg = () => commands.argument('jugador', args.STRING.create(event));
  var rankArg = () => commands.argument('rango', args.STRING.create(event));
  var durationArg = () => commands.argument('duracion', args.STRING.create(event));

  event.register(
    commands.literal('cunirank')
      .requires(source => source.hasPermission(2))
      .executes(ctx => cunirankHelp(ctx))
      .then(commands.literal('help')
        .executes(ctx => cunirankHelp(ctx)))
      .then(commands.literal('roles')
        .executes(ctx => cunirankRoles(ctx)))
      .then(commands.literal('list')
        .executes(ctx => cunirankRoles(ctx)))
      .then(commands.literal('test')
        .then(playerArg()
          .executes(ctx => cunirankTest(ctx, args.STRING.getResult(ctx, 'jugador')))))
      .then(commands.literal('debug')
        .then(playerArg()
          .executes(ctx => cunirankDebug(ctx, args.STRING.getResult(ctx, 'jugador')))))
      .then(commands.literal('apply')
        .then(playerArg()
          .then(rankArg()
            .executes(ctx => cunirankRun(ctx, 'apply', args.STRING.getResult(ctx, 'jugador'), args.STRING.getResult(ctx, 'rango'))))))
      .then(commands.literal('asignar')
        .then(playerArg()
          .then(rankArg()
            .executes(ctx => cunirankRun(ctx, 'apply', args.STRING.getResult(ctx, 'jugador'), args.STRING.getResult(ctx, 'rango'))))))
      .then(commands.literal('set')
        .then(playerArg()
          .then(rankArg()
            .executes(ctx => cunirankRun(ctx, 'set', args.STRING.getResult(ctx, 'jugador'), args.STRING.getResult(ctx, 'rango'))))))
      .then(commands.literal('add')
        .then(playerArg()
          .then(rankArg()
            .executes(ctx => cunirankRun(ctx, 'add', args.STRING.getResult(ctx, 'jugador'), args.STRING.getResult(ctx, 'rango'))))))
      .then(commands.literal('addtemp')
        .then(playerArg()
          .then(rankArg()
            .then(durationArg()
              .executes(ctx => cunirankRunTimed(ctx, args.STRING.getResult(ctx, 'jugador'), args.STRING.getResult(ctx, 'rango'), args.STRING.getResult(ctx, 'duracion')))))))
      .then(commands.literal('temp')
        .then(playerArg()
          .then(rankArg()
            .then(durationArg()
              .executes(ctx => cunirankRunTimed(ctx, args.STRING.getResult(ctx, 'jugador'), args.STRING.getResult(ctx, 'rango'), args.STRING.getResult(ctx, 'duracion')))))))
      .then(commands.literal('temporal')
        .then(playerArg()
          .then(rankArg()
            .then(durationArg()
              .executes(ctx => cunirankRunTimed(ctx, args.STRING.getResult(ctx, 'jugador'), args.STRING.getResult(ctx, 'rango'), args.STRING.getResult(ctx, 'duracion')))))))
      .then(commands.literal('remove')
        .then(playerArg()
          .then(rankArg()
            .executes(ctx => cunirankRun(ctx, 'remove', args.STRING.getResult(ctx, 'jugador'), args.STRING.getResult(ctx, 'rango'))))))
      .then(commands.literal('sync')
        .then(playerArg()
          .executes(ctx => cunirankSyncOnly(ctx, args.STRING.getResult(ctx, 'jugador')))
          .then(rankArg()
            .executes(ctx => cunirankRun(ctx, 'sync', args.STRING.getResult(ctx, 'jugador'), args.STRING.getResult(ctx, 'rango'))))))
  );

  event.register(
    commands.literal('cunihold')
      .then(playerArg()
        .executes(ctx => cunirankHoldPlayer(ctx, args.STRING.getResult(ctx, 'jugador'), '30'))
        .then(durationArg()
          .executes(ctx => cunirankHoldPlayer(ctx, args.STRING.getResult(ctx, 'jugador'), args.STRING.getResult(ctx, 'duracion')))))
      .then(commands.literal('liberar')
        .then(playerArg()
          .executes(ctx => cunirankReleasePlayer(ctx, args.STRING.getResult(ctx, 'jugador')))))
      .then(commands.literal('release')
        .then(playerArg()
          .executes(ctx => cunirankReleasePlayer(ctx, args.STRING.getResult(ctx, 'jugador')))))
  );
});
