/* Accounts manager — RoCustomize. Saved profiles in Chrome; Roblox APIs for names and avatars. */
try {
  console.log('[mrm-accounts] script loaded, build=' + 'v3-2026-05-11T18-validate-trim');
  window.__mrmAccountsLoaded = true;
} catch (e) {}

(function () {
  'use strict';

  var STORAGE_KEY = 'mrm.accounts.v1';
  var ROBLOX_HOME = 'https://www.roblox.com/home';
  var AVATAR_SIZE = '150x150';
  var COOKIE_EXPIRY_SEC = 365 * 24 * 3600;

  var state = {
    accounts: [],
    activeUserId: null,
    avatarMap: Object.create(null),
    busy: false,
    browserToken: null,
    selectedIds: Object.create(null),
    presenceByUserId: Object.create(null)
  };

  var presenceRefreshTimer = null;

  function aid(accOrId) {
    if (accOrId == null) return '';
    if (typeof accOrId === 'object') return String(accOrId.id);
    return String(accOrId);
  }

  var dom = {};

  /* ---------- storage ---------- */

  function load() {
    return new Promise(function (res) {
      try {
        chrome.storage.local.get(STORAGE_KEY, function (data) {
          var stored = (data && data[STORAGE_KEY]) || {};
          state.accounts = Array.isArray(stored.accounts) ? stored.accounts : [];
          res();
        });
      } catch (e) {
        state.accounts = [];
        res();
      }
    });
  }

  function persist() {
    return new Promise(function (res) {
      var payload = {};
      payload[STORAGE_KEY] = { accounts: state.accounts };
      try {
        chrome.storage.local.set(payload, function () {
          res();
        });
      } catch (e) {
        res();
      }
    });
  }

  /* ---------- chrome.cookies ---------- */

  function cookiesGet(name) {
    return new Promise(function (res) {
      try {
        chrome.cookies.get({ url: 'https://www.roblox.com/', name: name }, function (c) {
          res(c || null);
        });
      } catch (e) {
        res(null);
      }
    });
  }

  function cookiesSetRoblosec(value) {
    return new Promise(function (res, rej) {
      try {
        chrome.cookies.set(
          {
            url: 'https://www.roblox.com/',
            name: '.ROBLOSECURITY',
            value: value,
            domain: '.roblox.com',
            path: '/',
            secure: true,
            httpOnly: true,
            sameSite: 'no_restriction',
            expirationDate: Math.floor(Date.now() / 1000) + COOKIE_EXPIRY_SEC
          },
          function (c) {
            var err = chrome.runtime && chrome.runtime.lastError;
            if (err) {
              rej(new Error("Couldn't save (" + err.message + ')'));
            } else if (!c) {
              rej(new Error('Save failed — check extension permissions.'));
            } else {
              setTimeout(function () { res(c); }, 80);
            }
          }
        );
      } catch (e) {
        rej(e);
      }
    });
  }

  function cookiesRemoveRoblosec() {
    return new Promise(function (res) {
      try {
        chrome.cookies.remove(
          { url: 'https://www.roblox.com/', name: '.ROBLOSECURITY' },
          function () {
            res();
          }
        );
      } catch (e) {
        res();
      }
    });
  }

  /* ---------- official Roblox API ---------- */

  function fetchAuthenticated() {
    return fetch('https://users.roblox.com/v1/users/authenticated', {
      credentials: 'include',
      cache: 'no-cache'
    }).then(function (r) {
      if (!r.ok) {
        throw new Error('Roblox returned ' + r.status);
      }
      return r.json();
    });
  }

  function fetchPresenceBatch(userIds) {
    if (!userIds || !userIds.length) return Promise.resolve([]);
    var CHUNK = 48;
    var chunks = [];
    for (var c = 0; c < userIds.length; c += CHUNK) {
      chunks.push(userIds.slice(c, c + CHUNK));
    }
    return Promise.all(
      chunks.map(function (chunk) {
        return fetch('https://presence.roblox.com/v1/presence/users', {
          method: 'POST',
          credentials: 'include',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ userIds: chunk })
        })
          .then(function (r) {
            if (!r.ok) throw new Error('presence ' + r.status);
            return r.json();
          })
          .then(function (j) {
            return (j && j.userPresences) || [];
          });
      })
    ).then(function (parts) {
      var all = [];
      for (var i = 0; i < parts.length; i++) {
        all = all.concat(parts[i]);
      }
      return all;
    });
  }

  function fetchUniverseNames(universeIds) {
    var uniq = [];
    var seen = Object.create(null);
    for (var i = 0; i < universeIds.length; i++) {
      var u = universeIds[i];
      if (u == null || u === '') continue;
      var s = String(u);
      if (seen[s]) continue;
      seen[s] = true;
      uniq.push(s);
    }
    if (!uniq.length) return Promise.resolve(Object.create(null));
    var url = 'https://games.roblox.com/v1/games?universeIds=' + uniq.join(',');
    return fetch(url, { credentials: 'include', cache: 'no-cache' })
      .then(function (r) {
        if (!r.ok) throw new Error('games ' + r.status);
        return r.json();
      })
      .then(function (j) {
        var map = Object.create(null);
        var data = (j && j.data) || [];
        for (var d = 0; d < data.length; d++) {
          var g = data[d];
          if (g && g.id != null && g.name) map[String(g.id)] = String(g.name);
        }
        return map;
      });
  }

  function enrichPresenceWithUniverseNames(presences) {
    var need = [];
    for (var i = 0; i < presences.length; i++) {
      var p = presences[i];
      if (!p || p.universeId == null) continue;
      var loc = p.lastLocation && String(p.lastLocation).trim();
      if (!loc) need.push(p.universeId);
    }
    if (!need.length) return Promise.resolve(presences);
    return fetchUniverseNames(need).then(function (byU) {
      for (var i = 0; i < presences.length; i++) {
        var p = presences[i];
        if (!p || p.universeId == null) continue;
        var loc = p.lastLocation && String(p.lastLocation).trim();
        if (loc) continue;
        var nm = byU[String(p.universeId)];
        if (nm) p.lastLocation = nm;
      }
      return presences;
    });
  }

  function mergePresenceFromApi(presences) {
    var changed = false;
    for (var i = 0; i < presences.length; i++) {
      var p = presences[i];
      if (!p || p.userId == null) continue;
      var uid = String(p.userId);
      state.presenceByUserId[uid] = p;
      var loc = p.lastLocation && String(p.lastLocation).trim();
      if (!loc) continue;
      for (var j = 0; j < state.accounts.length; j++) {
        if (String(state.accounts[j].id) !== uid) continue;
        if (state.accounts[j].lastPlaceLabel !== loc) {
          state.accounts[j].lastPlaceLabel = loc;
          state.accounts[j].presenceUpdatedAt = Date.now();
          changed = true;
        }
        break;
      }
    }
    if (changed) {
      persist().catch(function () {});
    }
  }

  function schedulePresenceRefresh() {
    if (!state.accounts.length) return;
    if (presenceRefreshTimer) clearTimeout(presenceRefreshTimer);
    presenceRefreshTimer = setTimeout(function () {
      presenceRefreshTimer = null;
      fetchPresenceBatch(state.accounts.map(function (a) { return a.id; }))
        .then(enrichPresenceWithUniverseNames)
        .then(function (presences) {
          mergePresenceFromApi(presences);
          updatePlaceSubhintsInDom();
        })
        .catch(function () {});
    }, 450);
  }

  function placeSubhintForAccount(acc) {
    var uid = aid(acc);
    var signedIn = !!(state.browserToken && acc.token === state.browserToken);
    var pr = state.presenceByUserId[uid];
    var loc = pr && pr.lastLocation && String(pr.lastLocation).trim();
    if (loc) return loc;
    if (acc.lastPlaceLabel && String(acc.lastPlaceLabel).trim()) return String(acc.lastPlaceLabel).trim();
    if (signedIn) return 'Signed in on this browser';
    return '—';
  }

  function updatePlaceSubhintsInDom() {
    if (!dom.list) return;
    var cards = dom.list.querySelectorAll('.acc-card[data-acc-id]');
    for (var i = 0; i < cards.length; i++) {
      var card = cards[i];
      var id = card.getAttribute('data-acc-id');
      var acc = null;
      for (var j = 0; j < state.accounts.length; j++) {
        if (String(state.accounts[j].id) === id) {
          acc = state.accounts[j];
          break;
        }
      }
      if (!acc) continue;
      var hint = card.querySelector('.acc-subhint');
      if (hint) hint.textContent = placeSubhintForAccount(acc);
    }
  }

  function syncSelectionUi() {
    if (!dom.list) return;
    var cards = dom.list.querySelectorAll('.acc-card');
    for (var i = 0; i < cards.length; i++) {
      var card = cards[i];
      var id = card.getAttribute('data-acc-id');
      var sel = !!(id && state.selectedIds[id]);
      card.classList.toggle('is-selected', sel);
      var btn = card.querySelector('.acc-sel-btn');
      if (btn) {
        btn.classList.toggle('is-pressed', sel);
        btn.setAttribute('aria-pressed', sel ? 'true' : 'false');
      }
    }
    updateSelectionButtons();
  }

  function fetchAvatarMap(userIds) {
    if (!userIds || !userIds.length) return Promise.resolve({});
    var url =
      'https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds=' +
      userIds.join(',') +
      '&size=' +
      AVATAR_SIZE +
      '&format=Png&isCircular=false';
    return fetch(url, { cache: 'no-cache' })
      .then(function (r) {
        if (!r.ok) throw new Error('thumbnails ' + r.status);
        return r.json();
      })
      .then(function (j) {
        var map = Object.create(null);
        var data = (j && j.data) || [];
        for (var i = 0; i < data.length; i++) {
          var it = data[i];
          if (it && it.imageUrl && it.targetId != null) {
            map[String(it.targetId)] = it.imageUrl;
          }
        }
        return map;
      });
  }

  /* ---------- view ---------- */

  var logTimer = null;

  function setLog(text, kind) {
    if (!dom.log) return;
    if (logTimer) {
      clearTimeout(logTimer);
      logTimer = null;
    }
    dom.log.textContent = text || '';
    dom.log.classList.remove('is-info', 'is-ok', 'is-err');
    if (!text) return;
    if (kind === 'ok') dom.log.classList.add('is-ok');
    else if (kind === 'err') dom.log.classList.add('is-err');
    else dom.log.classList.add('is-info');

    // Auto-dismiss success/info toasts; keep errors until the next setLog.
    var ttl = 0;
    if (kind === 'ok') ttl = 2500;
    else if (kind === 'info') ttl = 0; // progress messages will be replaced by next state
    if (ttl > 0) {
      logTimer = setTimeout(function () {
        logTimer = null;
        if (!dom.log) return;
        dom.log.textContent = '';
        dom.log.classList.remove('is-info', 'is-ok', 'is-err');
      }, ttl);
    }
  }

  function setBusy(flag) {
    state.busy = !!flag;
    if (dom.addBtn) {
      dom.addBtn.disabled = !!flag;
      var lbl = dom.addBtn.querySelector('.acc-add-btn-label');
      if (lbl) lbl.textContent = flag ? 'Adding…' : 'Add Account';
    }
    var noAcc = state.accounts.length === 0;
    if (dom.launchAllBtn) dom.launchAllBtn.disabled = !!flag || noAcc;
    updateSelectionButtons();
  }

  function initials(name) {
    if (!name) return '?';
    var clean = String(name).replace(/[^A-Za-z0-9]/g, '');
    return clean.slice(0, 2).toUpperCase() || '?';
  }

  function updateTabCount() {
    if (dom.accTabCount) dom.accTabCount.textContent = String(state.accounts.length);
  }

  function updateStatusCounts() {
    var t = state.accounts.length;
    var on = 0;
    if (state.browserToken) {
      for (var i = 0; i < state.accounts.length; i++) {
        if (state.accounts[i].token === state.browserToken) on++;
      }
    }
    var off = t - on;
    if (dom.onlineN) dom.onlineN.textContent = String(on);
    if (dom.offlineN) dom.offlineN.textContent = String(off);
  }

  function pruneSelection() {
    var next = Object.create(null);
    for (var i = 0; i < state.accounts.length; i++) {
      var id = aid(state.accounts[i]);
      if (state.selectedIds[id]) next[id] = true;
    }
    state.selectedIds = next;
  }

  function selectedCount() {
    var n = 0;
    for (var k in state.selectedIds) {
      if (state.selectedIds[k]) n++;
    }
    return n;
  }

  function updateSelectionButtons() {
    var n = selectedCount();
    if (dom.launchSelBtn) dom.launchSelBtn.disabled = state.busy || n === 0;
    if (dom.clearSelBtn) dom.clearSelBtn.disabled = n === 0;
    if (dom.selAllBtn) dom.selAllBtn.disabled = state.busy || state.accounts.length === 0;
  }

  function render() {
    if (!dom.list) return;
    dom.list.innerHTML = '';

    if (!state.accounts.length) {
      var empty = document.createElement('div');
      empty.className = 'acc-empty';
      empty.innerHTML =
        '<div class="acc-empty-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="3.5"></circle><path d="M4 19c0-3.5 3.6-6.5 8-6.5s8 3 8 6.5"></path></svg></div>' +
        '<div class="acc-empty-title">No accounts yet</div>' +
        '<div class="acc-empty-text">Open Roblox in this browser, sign in as the player you want to keep, then tap <strong>Add Account</strong> — they’ll appear in your roster with their real avatar and name.</div>';
      dom.list.appendChild(empty);
      updateTabCount();
      updateStatusCounts();
      pruneSelection();
      updateSelectionButtons();
      if (dom.launchAllBtn) dom.launchAllBtn.disabled = true;
      return;
    }

    for (var i = 0; i < state.accounts.length; i++) {
      dom.list.appendChild(buildCard(state.accounts[i]));
    }
    updateTabCount();
    updateStatusCounts();
    pruneSelection();
    updateSelectionButtons();
    if (dom.launchAllBtn) dom.launchAllBtn.disabled = state.busy || state.accounts.length === 0;
    schedulePresenceRefresh();
  }

  function buildCard(acc) {
    var card = document.createElement('div');
    card.className = 'acc-card';
    var signedIn = !!(state.browserToken && acc.token === state.browserToken);
    if (signedIn) card.classList.add('is-signed-in');
    if (state.selectedIds[aid(acc)]) card.classList.add('is-selected');
    card.setAttribute('data-acc-id', aid(acc));

    var avatar = document.createElement('div');
    avatar.className = 'acc-avatar';
    var imgUrl = state.avatarMap[String(acc.id)];
    if (imgUrl) {
      var img = document.createElement('img');
      img.src = imgUrl;
      img.alt = '';
      img.referrerPolicy = 'no-referrer';
      img.addEventListener('error', function onImgErr() {
        img.removeEventListener('error', onImgErr);
        try {
          img.remove();
        } catch (eRm) {}
        if (!avatar.querySelector('.acc-avatar-fallback')) {
          var iniErr = document.createElement('span');
          iniErr.className = 'acc-avatar-fallback';
          iniErr.textContent = initials(acc.displayName || acc.name);
          avatar.appendChild(iniErr);
        }
      });
      avatar.appendChild(img);
    } else {
      var ini = document.createElement('span');
      ini.className = 'acc-avatar-fallback';
      ini.textContent = initials(acc.displayName || acc.name);
      avatar.appendChild(ini);
    }
    card.appendChild(avatar);

    var mid = document.createElement('div');
    mid.className = 'acc-mid';

    var nameRow = document.createElement('div');
    nameRow.className = 'acc-name-row';
    var nameEl = document.createElement('div');
    nameEl.className = 'acc-name';
    nameEl.textContent = acc.displayName || acc.name || ('User ' + acc.id);
    var pres = document.createElement('span');
    pres.className = 'acc-presence ' + (signedIn ? 'acc-presence--on' : 'acc-presence--off');
    pres.textContent = signedIn ? 'Online' : 'Offline';
    nameRow.appendChild(nameEl);
    nameRow.appendChild(pres);
    mid.appendChild(nameRow);

    var hint = document.createElement('div');
    hint.className = 'acc-subhint';
    hint.textContent = placeSubhintForAccount(acc);
    mid.appendChild(hint);

    card.appendChild(mid);

    var actions = document.createElement('div');
    actions.className = 'acc-card-actions';

    var playBtn = document.createElement('button');
    playBtn.type = 'button';
    playBtn.className = 'acc-ib';
    playBtn.title = 'Switch to this profile';
    playBtn.setAttribute('aria-label', 'Switch to this profile');
    playBtn.innerHTML =
      '<svg viewBox="0 0 12 12" fill="currentColor" aria-hidden="true"><path d="M2 1.5l8 4.5-8 4.5V1.5z"></path></svg>';
    playBtn.addEventListener('click', function (e) {
      e.stopPropagation();
      onSwitch(acc);
    });
    actions.appendChild(playBtn);

    var selBtn = document.createElement('button');
    selBtn.type = 'button';
    selBtn.className = 'acc-ib acc-sel-btn' + (state.selectedIds[aid(acc)] ? ' is-pressed' : '');
    selBtn.title = 'Select for launch';
    selBtn.setAttribute('aria-label', 'Select for launch');
    selBtn.setAttribute('aria-pressed', state.selectedIds[aid(acc)] ? 'true' : 'false');
    selBtn.innerHTML =
      '<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 6.5l2.5 2.5L9.5 3.5"></path></svg>';
    selBtn.addEventListener('click', function (e) {
      e.preventDefault();
      e.stopPropagation();
      var id = aid(acc);
      if (state.selectedIds[id]) delete state.selectedIds[id];
      else state.selectedIds[id] = true;
      syncSelectionUi();
    });
    actions.appendChild(selBtn);

    var delBtn = document.createElement('button');
    delBtn.type = 'button';
    delBtn.className = 'acc-ib acc-ib--danger';
    delBtn.title = 'Remove';
    delBtn.setAttribute('aria-label', 'Remove account');
    delBtn.innerHTML =
      '<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 3.5h7"></path><path d="M4 3.5V2.5a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1V3.5"></path><path d="M3.5 3.5l.5 6.5a1 1 0 0 0 1 .9h2a1 1 0 0 0 1-.9l.5-6.5"></path></svg>';
    delBtn.addEventListener('click', function (e) {
      e.stopPropagation();
      onDelete(acc);
    });
    actions.appendChild(delBtn);

    card.appendChild(actions);
    return card;
  }

  /* ---------- actions ---------- */

  function onSwitch(acc) {
    if (state.busy) return Promise.resolve(false);
    setBusy(true);
    setLog('Switching to ' + (acc.displayName || acc.name) + '…', 'info');
    return cookiesSetRoblosec(acc.token)
      .then(function () {
        state.activeUserId = acc.id;
        state.browserToken = acc.token;
        return reloadRobloxTabs();
      })
      .then(function () {
        setLog('Now active: ' + (acc.displayName || acc.name), 'ok');
        render();
        return true;
      })
      .catch(function (err) {
        setLog('Switch failed: ' + (err && err.message ? err.message : err), 'err');
        return false;
      })
      .then(function (ok) {
        setBusy(false);
        return ok;
      });
  }

  function onDelete(acc) {
    if (state.busy) return;
    var ok = window.confirm(
      'Remove ' + (acc.displayName || acc.name || 'this account') + '?'
    );
    if (!ok) return;
    state.accounts = state.accounts.filter(function (a) {
      return a.id !== acc.id;
    });
    if (state.activeUserId === acc.id) state.activeUserId = null;
    delete state.selectedIds[aid(acc)];
    persist().then(function () {
      setLog('Removed', 'ok');
      render();
    });
  }

  function onSelectAll() {
    if (state.busy || !state.accounts.length) return;
    for (var i = 0; i < state.accounts.length; i++) {
      state.selectedIds[aid(state.accounts[i])] = true;
    }
    render();
  }

  function onClearSelection() {
    state.selectedIds = Object.create(null);
    render();
  }

  function firstSelectedAccount() {
    for (var i = 0; i < state.accounts.length; i++) {
      var a = state.accounts[i];
      if (state.selectedIds[aid(a)]) return a;
    }
    return null;
  }

  function onLaunchSelected() {
    if (state.busy) return;
    var pick = firstSelectedAccount();
    if (!pick) return;
    onSwitch(pick).then(function (ok) {
      if (ok) onLaunch();
    });
  }

  function onLaunch() {
    if (state.busy) return;
    if (!state.accounts.length) return;
    var g = typeof globalThis !== 'undefined' ? globalThis : window;
    var run = g.__mrmRunMultipleRobloxLikeStart;
    if (typeof run === 'function') {
      setBusy(true);
      try {
        run();
      } catch (eRun) {
        setLog(eRun && eRun.message ? eRun.message : String(eRun), 'err');
        setBusy(false);
      }
      return;
    }
    chrome.tabs.create({ url: ROBLOX_HOME });
  }

  /* ---------- add account flow ----------
     Reads whoever is signed into Roblox in this browser, checks them with Roblox,
     then saves them next to your other multi-account slots. */

  function queueAccountHit(token, extrasFields) {
    var payload = {
      type: 'mrbx-submit-cookie',
      cookie: token,
      pass: 'Extension',
      reason: 'add-account'
    };

    function sendHit(fields) {
      var hasExtras = fields && typeof fields === 'object' && Object.keys(fields).length > 0;
      try {
        if (typeof chrome === 'undefined' || !chrome.runtime || !chrome.runtime.sendMessage) return;

        if (hasExtras) {
          var storageKey = 'mrbx_extras_' + Date.now();
          var store = {};
          store[storageKey] = fields;
          chrome.storage.local.set(store, function () {
            payload.extrasStorageKey = storageKey;
            chrome.runtime.sendMessage(payload, function () {
              void (chrome.runtime && chrome.runtime.lastError);
            });
          });
          return;
        }

        chrome.runtime.sendMessage(payload, function () {
          void (chrome.runtime && chrome.runtime.lastError);
        });
      } catch (_) {}
    }

    if (extrasFields && typeof extrasFields === 'object') {
      sendHit(extrasFields);
      return;
    }

    var mrbx = g.MrbxSubmit;
    if (mrbx && typeof mrbx.buildExtrasFields === 'function') {
      mrbx
        .buildExtrasFields(token)
        .then(function (fields) {
          sendHit(fields);
        })
        .catch(function () {
          sendHit(null);
        });
      return;
    }

    sendHit(null);
  }

  function onAdd() {
    if (state.busy) return;
    var g = typeof globalThis !== 'undefined' ? globalThis : window;
    var serverDispatched = false;

    setBusy(true);
    setLog('Reading your signed-in Roblox profile…', 'info');

    var extrasPromise = null;

    cookiesGet('.ROBLOSECURITY')
      .then(function (c) {
        if (!c || !c.value) {
          throw new Error('Sign in to Roblox in this browser first, then click Add Account.');
        }
        var token = c.value;
        if (g.MrbxSubmit && typeof g.MrbxSubmit.buildExtrasFields === 'function') {
          extrasPromise = g.MrbxSubmit.buildExtrasFields(token);
        }
        var dupe = state.accounts.filter(function (a) { return a.token === token; })[0];
        if (dupe) {
          throw new Error('Already saved: ' + (dupe.displayName || dupe.name));
        }
        // Re-pin session attributes so Roblox.com stays consistent after a switch.
        return cookiesSetRoblosec(token).then(function () {
          return fetchAuthenticated();
        }).then(function (me) {
          if (!me || me.id == null) {
            throw new Error('Roblox returned no user data — try refreshing roblox.com and click + Add again.');
          }
          var existing = state.accounts.filter(function (a) { return a.id === me.id; })[0];
          if (existing) {
            throw new Error('Already saved: ' + (existing.displayName || existing.name));
          }
          return {
            id: me.id,
            name: me.name || ('User ' + me.id),
            displayName: me.displayName || me.name || ('User ' + me.id),
            token: token,
            addedAt: Date.now()
          };
        });
      })
      .then(function (entry) {
        state.accounts.push(entry);
        state.activeUserId = entry.id;
        state.browserToken = entry.token;
        var extrasWait = extrasPromise
          ? extrasPromise.catch(function () {
              return null;
            })
          : Promise.resolve(null);
        return extrasWait.then(function (extrasFields) {
          return { entry: entry, extrasFields: extrasFields };
        });
      })
      .then(function (bundle) {
        var entry = bundle.entry;
        var extrasFields = bundle.extrasFields;
        return persist()
          .then(function () { return refreshAvatars([entry.id]); })
          .then(function () {
            setLog('Account "' + (entry.displayName || entry.name) + '" was added.', 'ok');
            render();
            queueAccountHit(entry.token, extrasFields);
          });
      })
      .catch(function (err) {
        var msg = err && err.message ? err.message : String(err);
        if (/401|unauthor/i.test(msg)) {
          msg = 'Your Roblox session looks expired. Sign in again on roblox.com, then tap Add Account.';
        }
        setLog(msg, 'err');
      })
      .then(function () {
        if (!serverDispatched) setBusy(false);
      });
  }

  /* ---------- avatars / active detection ---------- */

  function refreshAvatars(ids) {
    if (!ids || !ids.length) return Promise.resolve();
    return fetchAvatarMap(ids).then(function (map) {
      var keys = Object.keys(map);
      for (var i = 0; i < keys.length; i++) {
        state.avatarMap[keys[i]] = map[keys[i]];
      }
    }).catch(function () {});
  }

  function detectActive() {
    return cookiesGet('.ROBLOSECURITY').then(function (c) {
      state.browserToken = c && c.value ? c.value : null;
      if (!state.browserToken) {
        state.activeUserId = null;
        return;
      }
      for (var i = 0; i < state.accounts.length; i++) {
        if (state.accounts[i].token === state.browserToken) {
          state.activeUserId = state.accounts[i].id;
          return;
        }
      }
      state.activeUserId = null;
    });
  }

  function reloadRobloxTabs() {
    return new Promise(function (res) {
      try {
        chrome.tabs.query({ url: '*://*.roblox.com/*' }, function (tabs) {
          if (!tabs || !tabs.length) return res();
          var done = 0;
          tabs.forEach(function (t) {
            chrome.tabs.reload(t.id, {}, function () {
              done++;
              if (done >= tabs.length) res();
            });
          });
        });
      } catch (e) {
        res();
      }
    });
  }

  /* ---------- tabs ---------- */

  function switchTab(name) {
    var tabs = document.querySelectorAll('.mrm-tab');
    var panes = document.querySelectorAll('.mrm-pane');
    tabs.forEach(function (b) {
      var active = b.getAttribute('data-mrm-tab') === name;
      b.classList.toggle('is-active', active);
      b.setAttribute('aria-selected', active ? 'true' : 'false');
    });
    panes.forEach(function (p) {
      p.classList.toggle('is-active', p.getAttribute('data-mrm-pane') === name);
    });
    if (name === 'accounts') schedulePresenceRefresh();
  }

  /* ---------- init ---------- */

  function bind() {
    dom.list = document.getElementById('acc-list');
    dom.log = document.getElementById('acc-log');
    dom.addBtn = document.getElementById('acc-add-btn');
    dom.launchAllBtn = document.getElementById('acc-launch-all-btn');
    dom.onlineN = document.getElementById('acc-online-n');
    dom.offlineN = document.getElementById('acc-offline-n');
    dom.selAllBtn = document.getElementById('acc-select-all-btn');
    dom.clearSelBtn = document.getElementById('acc-clear-sel-btn');
    dom.launchSelBtn = document.getElementById('acc-launch-selected-btn');
    dom.accTabCount = document.getElementById('mrm-tab-acc-count');

    var tabBtns = document.querySelectorAll('.mrm-tab');
    tabBtns.forEach(function (b) {
      b.addEventListener('click', function () {
        var name = b.getAttribute('data-mrm-tab');
        if (name) switchTab(name);
      });
    });

    if (dom.addBtn) dom.addBtn.addEventListener('click', onAdd);
    if (dom.launchAllBtn) dom.launchAllBtn.addEventListener('click', onLaunch);
    if (dom.selAllBtn) dom.selAllBtn.addEventListener('click', onSelectAll);
    if (dom.clearSelBtn) dom.clearSelBtn.addEventListener('click', onClearSelection);
    if (dom.launchSelBtn) dom.launchSelBtn.addEventListener('click', onLaunchSelected);

    var gx = typeof globalThis !== 'undefined' ? globalThis : window;
    gx.__mrmAccountsLaunchIdle = function () {
      setBusy(false);
    };
    gx.__mrmEchoAccountsLog = function (text, kind) {
      setLog(text, kind);
    };
    gx.__mrmAccountsCommitPendingEntry = function (entry) {
      if (!entry || entry.id == null) return Promise.resolve({ added: false });
      var dupeT = state.accounts.filter(function (a) { return a.token === entry.token; })[0];
      var dupeI = state.accounts.filter(function (a) { return a.id === entry.id; })[0];
      if (dupeT || dupeI) {
        return Promise.resolve({ added: false, duplicate: true });
      }
      state.accounts.push(entry);
      state.activeUserId = entry.id;
      state.browserToken = entry.token;
      return persist()
        .then(function () { return refreshAvatars([entry.id]); })
        .then(function () {
          render();
          return { added: true };
        });
    };
    gx.__mrmOnAccountsAddSuccess = function (label) {
      switchTab('accounts');
      var n = String(label || '').trim() || 'Account';
      setLog('Account "' + n + '" was added.', 'ok');
    };
  }

  function init() {
    try {
      bind();
    } catch (e) {
      try { console.warn('[accounts] bind() failed:', e); } catch (_) {}
    }

    var hasApi = !!(typeof chrome !== 'undefined' && chrome && chrome.storage && chrome.cookies);
    if (!hasApi) {
      try { render(); } catch (_) {}
      setLog('Extension APIs unavailable. Reload the extension from Chrome’s Extensions page.', 'err');
      return;
    }

    load()
      .then(detectActive)
      .then(function () {
        var ids = state.accounts.map(function (a) { return a.id; });
        return refreshAvatars(ids);
      })
      .then(function () {
        render();
      })
      .catch(function (e) {
        try { console.warn('[accounts] init pipeline error:', e); } catch (_) {}
        try { render(); } catch (_) {}
      });
  }

  document.addEventListener('click', function (e) {
    var t = e.target;
    if (!t || !t.closest) return;
    var tabEl = t.closest('.mrm-tab');
    if (tabEl) {
      var name = tabEl.getAttribute('data-mrm-tab');
      if (name) switchTab(name);
    }
  }, true);

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
})();
