/* ═══════════════════════════════════════════════════════
   lib/store · Global state, actions, ranking rebuild
   Single source of truth — components read S via useStore()
═══════════════════════════════════════════════════════ */

const S = {
  /* auth */
  token: null, userEmail: null, userUid: null,
  /* screen state */
  screen: 'loading',           // loading | login | register | recover | clubs | app
  loadingMsg: '♠ carregando… ♣',
  /* navigation within app */
  route: 'home',               // home | ranking | partidas | mensalistas | usuarios | perfil
  currentClubId: null,
  clubInfo: null,
  currentGame: null,           // when set, shows GameScreen
  gameSubTab: 'confirm',       // confirm | session | caixa | result
  drawerOpen: false,
  sheet: null,                 // create-club | join-club | share-qr | new-game | etc
  /* data */
  userClubs: [],               // [{clubId, info, role, status}]
  allUsers: [],                // [{uid, email, displayName, avatar, ...}]
  pendingMembers: [],          // pending join requests for current club
  adminUids: [],
  superAdminUid: null,
  games: [],
  ranking: {},                 // {uid: {points, partidas, profit, invested}} — current season
  allTimeRanking: {},          // {uid: {...}} — all seasons combined
  seasons: [],                 // [{id, info:{label,startDate,endDate,status}}]
  currentSeasonId: null,       // active season id
  mensalMes: null,
  mensalidades: {},
  monthlyFee: 50,
  playersList: [],
  mensalistasList: [],
  prizeSplitRanking: 30,
  prizeSplitFinal: 70,
  accumulatedPrize: 0,
  /* meta */
  sync: 'ok',                  // ok | saving | error
  toasts: [],
};

/* ── Reactive plumbing ───────────────────────────────── */
const _listeners = new Set();
const subscribe = fn => { _listeners.add(fn); return () => _listeners.delete(fn); };
const notify = () => _listeners.forEach(fn => { try { fn(); } catch(e){} });
function update(patch) {
  if (typeof patch === 'function') patch(S);
  else Object.assign(S, patch);
  notify();
}

/* Hook for React */
function useStore() {
  const [, setTick] = React.useState(0);
  React.useEffect(() => subscribe(() => setTick(t => t + 1)), []);
  return S;
}

/* ── Toast queue ───────────────────────────────── */
function toast(msg, kind = 'success') {
  const id = Math.random().toString(36).slice(2);
  S.toasts = [...S.toasts, { id, msg, kind }];
  notify();
  setTimeout(() => {
    S.toasts = S.toasts.filter(x => x.id !== id);
    notify();
  }, 2800);
}

/* ── Sync badge ────────────────────────────────── */
function syncSet(s) { S.sync = s; notify(); }

/* ── Helpers ───────────────────────────────────── */
function clubPath(sub) { return '/clubs/' + S.currentClubId + (sub || ''); }
function isAdmin() { return S.adminUids.indexOf(S.userUid) > -1; }
function getCurrentUserName() {
  const me = S.allUsers.find(u => u.uid === S.userUid);
  if (me) return me.displayName || (me.email ? me.email.split('@')[0] : S.userEmail || '?');
  return S.userEmail ? S.userEmail.split('@')[0] : '?';
}
function getDisplayName(uidOrName) {
  if (!uidOrName) return '?';
  const u = S.allUsers.find(x => x.uid === uidOrName);
  if (u) return u.displayName || (u.email ? u.email.split('@')[0] : uidOrName);
  return uidOrName;
}
function getMyAvatar() {
  const me = S.allUsers.find(u => u.uid === S.userUid);
  return me?.avatar || null;
}
function nameToRankKey(name) {
  if (!name) return name;
  // 1. Already a UID
  if (S.allUsers.find(u => u.uid === name)) return name;
  // 2. Exact displayName match (current name)
  const byDisplay = S.allUsers.find(x => x.displayName === name);
  if (byDisplay) return byDisplay.uid;
  // 3. Email prefix match
  const byEmailPfx = S.allUsers.find(x => x.email && x.email.split('@')[0] === name);
  if (byEmailPfx) return byEmailPfx.uid;
  // 4. Email full match
  const byEmail = S.allUsers.find(x => x.email === name);
  if (byEmail) return byEmail.uid;
  // 5. Alias map (DJ Eder -> Eder, Dario Franz -> Dario)
  const aliased = resolveAlias(name);
  if (aliased !== name) {
    const byAlias = S.allUsers.find(x => x.displayName === aliased);
    if (byAlias) return byAlias.uid;
  }
  // 6. First-word match both ways (legacy data: "Pedro Souto" stored in game, user now "Phfree")
  const first = name.split(' ')[0].toLowerCase();
  if (first && first !== name.toLowerCase()) {
    const u1 = S.allUsers.find(x => x.displayName && x.displayName.toLowerCase() === first);
    if (u1) return u1.uid;
    const u2 = S.allUsers.find(x => x.displayName && x.displayName.toLowerCase().split(' ')[0] === first);
    if (u2) return u2.uid;
  }
  // 7. User whose first word matches incoming name
  const byFirstWord = S.allUsers.find(x => x.displayName && x.displayName.toLowerCase().split(' ')[0] === name.toLowerCase());
  if (byFirstWord) return byFirstWord.uid;
  // 8. previousNames — handles name changes (e.g. "Pedro Souto" -> "PHFree")
  const nameLower = name.toLowerCase();
  const byPrevName = S.allUsers.find(x =>
    Array.isArray(x.previousNames) && x.previousNames.some(pn =>
      pn.toLowerCase() === nameLower ||
      pn.toLowerCase().split(' ')[0] === nameLower ||
      nameLower === pn.toLowerCase().split(' ')[0]
    )
  );
  if (byPrevName) return byPrevName.uid;
  return name;
}
function getUserAvatar(uidOrName) {
  if (!uidOrName) return null;
  let u = S.allUsers.find(x => x.uid === uidOrName);
  if (!u) u = S.allUsers.find(x => x.displayName === uidOrName || x.email === uidOrName);
  if (!u) {
    // First-word fallback for legacy name-based keys (e.g. "Bruno Couto" -> user "Bruno")
    const first = String(uidOrName).split(' ')[0].toLowerCase();
    u = S.allUsers.find(x => x.displayName && x.displayName.toLowerCase().split(' ')[0] === first);
  }
  return u?.avatar || null;
}
function rankKeyDisplay(key) {
  const u = S.allUsers.find(x => x.uid === key);
  if (u) return u.displayName || (u.email ? u.email.split('@')[0] : key);
  return key;
}

/* ── Super admin helpers ─────────────────────────── */
function isSuperAdmin() {
  // Check explicit superAdmin override in config, fallback to createdBy
  if (S.superAdminUid && S.superAdminUid !== '') return S.superAdminUid === S.userUid;
  return !!S.clubInfo && S.clubInfo.createdBy === S.userUid;
}
function getSuperAdminUid() {
  return S.superAdminUid || S.clubInfo?.createdBy || null;
}
function transferSuperAdmin(uid) {
  if (!confirm('Transferir super admin para este usuário? Você perderá o status de super admin.')) return Promise.resolve();
  return dbPut(clubPath('/config/superAdmin.json'), uid)
    .then(() => dbPatch(clubPath('/info.json'), { createdBy: uid }))
    .then(() => {
      S.superAdminUid = uid;
      if (S.clubInfo) S.clubInfo.createdBy = uid;
      if (!S.adminUids.includes(uid)) S.adminUids.push(uid);
      notify();
      toast('Super admin transferido');
    });
}
function addAdmin(uid) {
  return dbPut(clubPath('/config/admins/' + uid + '.json'), true).then(() => {
    if (!S.adminUids.includes(uid)) S.adminUids.push(uid);
    notify();
    toast('Admin adicionado');
  });
}
function removeAdmin(uid) {
  if (uid === S.userUid) { toast('Você não pode remover a si mesmo', 'error'); return Promise.resolve(); }
  if (isSuperAdmin() && S.clubInfo.createdBy === uid) { toast('Não é possível remover o super admin', 'error'); return Promise.resolve(); }
  return dbDelete(clubPath('/config/admins/' + uid + '.json')).then(() => {
    S.adminUids = S.adminUids.filter(x => x !== uid);
    notify();
    toast('Admin removido');
  });
}

/* ── Per-user game history ────────────────────── */
function getMyGameHistory() {
  const myUid = S.userUid;
  const me = S.allUsers.find(u => u.uid === myUid);
  const myCurrentName = me?.displayName || getCurrentUserName();
  const myPrevNames = (me?.previousNames || []).map(n => n.toLowerCase());
  // All names this user has ever had (current + previous)
  const allMyNames = [myCurrentName, ...( me?.previousNames || [])].map(n => n.toLowerCase());

  const history = [];
  S.games.forEach(g => {
    if (g.status !== 'closed' || !g.closedPlayers) return;
    const gv = gameValues(g);
    const players = toArr(g.closedPlayers);
    const found = players.find(p => {
      // 1. Stored UID (new games)
      if (p.uid && p.uid === myUid) return true;
      // 2. nameToRankKey resolves to my UID
      const raw = resolveAlias(p.name || '');
      if (nameToRankKey(raw) === myUid) return true;
      if (nameToRankKey(p.name || '') === myUid) return true;
      // 3. Match against all my historical names
      const pNameLow = (p.name || '').toLowerCase();
      const pRawLow = raw.toLowerCase();
      if (allMyNames.includes(pNameLow) || allMyNames.includes(pRawLow)) return true;
      // 4. First-word match against any of my names
      const pFirst = pNameLow.split(' ')[0];
      if (allMyNames.some(n => n.split(' ')[0] === pFirst)) return true;
      return false;
    });
    if (found) history.push({ game: g, player: found, gv });
  });
  return history;
}

/* ── Ranking rebuild ─────────────────────── */
function _computeRanking(games) {
  const r = {};
  games.forEach(g => {
    if (g.status !== 'closed' || !g.closedPlayers) return;
    const gv = gameValues(g);
    const all = toArr(g.closedPlayers);
    const active = all.filter(p => !p.bustedAt);
    const busted = all.filter(p => !!p.bustedAt).sort((a, b) => a.bustedAt - b.bustedAt);
    active.sort((a, b) => {
      const invA = a.invested || (a.buyins||1)*gv.cv, invB = b.invested || (b.buyins||1)*gv.cv;
      const roeA = invA > 0 ? (a.profit||0)/invA*100 : 0;
      const roeB = invB > 0 ? (b.profit||0)/invB*100 : 0;
      if (Math.abs(roeB - roeA) > 0.01) return roeB - roeA;
      return invB - invA;
    });
    let mensPos = 0;
    active.concat(busted).forEach(p => {
      const rawName = resolveAlias(p.name || '');
      const key = p.uid || nameToRankKey(rawName);
      const inv = p.invested || (p.buyins||1)*gv.cv;
      const isMens = p.isMensalista != null ? p.isMensalista :
        S.mensalistasList.some(m => { const mk = nameToRankKey(m); return mk===key || m===rankKeyDisplay(key) || m===rawName || nameToRankKey(m)===key; });
      if (!isMens || !key) return;
      if (!r[key]) r[key] = { points:0, partidas:0, profit:0, invested:0, cashout:0, wins:0 };
      r[key].partidas++;
      r[key].profit += (p.profit||0);
      r[key].invested += inv;
      r[key].cashout += (p.cashout!=null ? p.cashout : (p.profit||0)+inv);
      r[key].points += mensPos < PTS.length ? PTS[mensPos] : 0;
      if (mensPos === 0) r[key].wins++;
      mensPos++;
    });
  });
  return r;
}
function rebuildRanking() {
  // Current-season games (or all games if no seasons defined)
  const activeSeason = S.currentSeasonId;
  const seasonGames = activeSeason
    ? S.games.filter(g => (g.seasonId || 'default') === activeSeason)
    : S.games;
  S.ranking = _computeRanking(seasonGames);
  S.allTimeRanking = _computeRanking(S.games);
}

/* ── Loaders ──────────────────────────────────── */
function loadGlobalUsers() {
  return dbGet('/users.json').then(users => {
    S.allUsers = [];
    if (users && typeof users === 'object') {
      S.allUsers = Object.keys(users).map(uid => {
        const u = users[uid] || {};
        return {
          uid, email: u.email || uid,
          displayName: u.displayName || '',
          notes: u.notes || '', disabled: !!u.disabled,
          avatar: u.avatar || null,
          lastLogin: u.lastLogin || '', createdAt: u.createdAt || '',
          lastSeen: u.lastSeen || null, online: !!u.online,
          previousNames: Array.isArray(u.previousNames) ? u.previousNames : [],
        };
      });
    }
    /* ensure current user record exists */
    const me = S.allUsers.find(u => u.uid === S.userUid);
    if (!me) {
      const userData = { uid: S.userUid, email: S.userEmail, displayName: '', disabled: false, createdAt: new Date().toISOString() };
      return dbPut('/users/' + S.userUid + '.json', userData).then(() => {
        S.allUsers.push(merge(userData, { avatar: null }));
      });
    } else {
      dbPatch('/users/' + S.userUid + '.json', { lastLogin: new Date().toISOString() }).catch(() => {});
    }
  });
}
function loadUserClubs() {
  return dbGet('/userClubs/' + S.userUid + '.json').then(data => {
    S.userClubs = [];
    if (!data || typeof data !== 'object') return;
    const ids = Object.keys(data).filter(k => data[k] && data[k].status === 'active');
    if (ids.length === 0) return;
    return Promise.all(ids.map(clubId =>
      dbGet('/clubs/' + clubId + '/info.json').then(info => {
        if (info) {
          S.userClubs.push({ clubId, info, role: data[clubId].role || 'member', status: 'active' });
        }
      }).catch(() => {})
    ));
  });
}
function loadClubData(clubId) {
  S.currentClubId = clubId;
  S.loadingMsg = '♣ entrando no clube… ♠';
  S.screen = 'loading';
  notify();
  return Promise.all([
    dbGet(clubPath('/info.json')).then(info => { if (info) S.clubInfo = info; }),
    dbGet(clubPath('/config.json')).then(cfg => {
      if (cfg) {
        if (cfg.admins && typeof cfg.admins === 'object') {
          S.adminUids = Object.keys(cfg.admins).filter(k => cfg.admins[k] === true);
        } else S.adminUids = [];
        S.superAdminUid = cfg.superAdmin || null;
        if (cfg.monthlyFee) S.monthlyFee = cfg.monthlyFee; else S.monthlyFee = DEFAULT_FEE;
        if (cfg.players && Array.isArray(cfg.players)) S.playersList = cfg.players; else S.playersList = [];
        if (cfg.prizeSplitRanking != null) S.prizeSplitRanking = cfg.prizeSplitRanking;
        if (cfg.prizeSplitFinal != null) S.prizeSplitFinal = cfg.prizeSplitFinal;
        if (cfg.accumulatedPrize != null) S.accumulatedPrize = cfg.accumulatedPrize; else S.accumulatedPrize = 0;
        if (cfg.mensalistas && Array.isArray(cfg.mensalistas)) S.mensalistasList = cfg.mensalistas;
        else S.mensalistasList = S.playersList.slice();
      } else {
        S.adminUids = []; S.monthlyFee = DEFAULT_FEE; S.playersList = []; S.mensalistasList = [];
        S.prizeSplitRanking = 30; S.prizeSplitFinal = 70; S.accumulatedPrize = 0;
      }
    }),
    dbGet(clubPath('/games.json')).then(games => {
      if (games && typeof games === 'object') {
        S.games = Object.keys(games).map(k => {
          const g = games[k]; g.id = k;
          g.confirmedPlayers = toArr(g.confirmedPlayers);
          g.sessionPlayers = toArr(g.sessionPlayers);
          g.closedPlayers = toArr(g.closedPlayers);
          g.waitingList = toArr(g.waitingList);
          return g;
        });
        S.games.sort((a, b) => (b.date || '') > (a.date || '') ? 1 : -1);
      } else S.games = [];
    }),
    dbGet(clubPath('/mensalidades.json')).then(mens => {
      S.mensalidades = mens && typeof mens === 'object' ? mens : {};
    }),
    loadSeasons(),
  ]).then(() => {
    try { rebuildRanking(); } catch(e) { console.error('rebuildRanking error:', e); }
    S.screen = 'app';
    S.route = 'home';
    S.currentGame = null;
    notify();
  }).catch(err => {
    console.error('loadClubData error:', err);
    if (err.auth) { doLogout(); toast('Sessão expirada', 'error'); }
    else { S.screen = 'clubs'; toast('Erro ao carregar clube — tente novamente', 'error'); notify(); }
  });
}

function loadPendingMembers() {
  return dbGet(clubPath('/members.json')).then(members => {
    S.pendingMembers = [];
    if (members && typeof members === 'object') {
      Object.keys(members).forEach(uid => {
        const m = members[uid];
        if (m.status === 'pending') {
          const u = S.allUsers.find(x => x.uid === uid);
          S.pendingMembers.push({
            uid, email: m.email || uid,
            displayName: u?.displayName || '',
            requestedAt: m.requestedAt || '',
          });
        }
      });
    }
    notify();
  });
}

/* ── Auth actions ──────────────────────────────── */
function doLogin(email, pw) {
  return fbLogin(email, pw).then(d => {
    const auth = fbStoreAuth(d.idToken, d.email, d.expiresIn, d.localId, d.refreshToken);
    S.token = auth.token; S.userEmail = auth.email; S.userUid = auth.uid;
    return loadGlobalUsers().then(loadUserClubs).then(() => {
      S.screen = 'clubs'; notify();
      startPresence();
    });
  });
}
function doRegister(email, pw, displayName) {
  return fbRegister(email, pw).then(d => {
    const auth = fbStoreAuth(d.idToken, d.email, d.expiresIn, d.localId, d.refreshToken);
    S.token = auth.token; S.userEmail = auth.email; S.userUid = auth.uid;
    const userData = { uid: auth.uid, email, displayName: displayName || '', disabled: false, createdAt: new Date().toISOString() };
    return dbPut('/users/' + auth.uid + '.json', userData).then(() => {
      S.allUsers.push(merge(userData, { avatar: null }));
      S.userClubs = []; S.screen = 'clubs'; notify();
      startPresence();
    });
  });
}
function doLogout() {
  stopPresence();
  fbClearAuth();
  S.token = null; S.userEmail = null; S.userUid = null;
  S.userClubs = []; S.currentClubId = null; S.clubInfo = null;
  S.games = []; S.allUsers = []; S.mensalidades = {}; S.ranking = {};
  S.screen = 'login'; S.route = 'home'; S.drawerOpen = false; S.currentGame = null;
  notify();
}

/* ── Presence system ──────────────────────────── */
let _presenceInterval = null;
const PRESENCE_TTL = 60000;      // 60s — online if lastSeen within this window
const PRESENCE_INTERVAL = 20000; // heartbeat every 20s

function isOnline(u) {
  if (!u) return false;
  return u.lastSeen && (Date.now() - Number(u.lastSeen)) < PRESENCE_TTL;
}
function _writePresence() {
  if (!S.userUid || !S.token) return;
  const me = S.allUsers.find(u => u.uid === S.userUid);
  // Write lightweight presence node (no heavy avatar bulk)
  const payload = { lastSeen: Date.now(), online: true, displayName: me?.displayName || '' };
  dbPut('/presence/' + S.userUid + '.json', payload).catch(() => {});
  dbPatch('/users/' + S.userUid + '.json', { lastSeen: Date.now(), online: true }).catch(() => {});
  if (me) { me.lastSeen = Date.now(); me.online = true; }
  notify();
}
// Fetch /presence — lightweight path, syncs name changes too
function refreshPresenceOnly() {
  return dbGet('/presence.json').then(data => {
    if (!data || typeof data !== 'object') return;
    Object.keys(data).forEach(uid => {
      const p = data[uid];
      const u = S.allUsers.find(x => x.uid === uid);
      if (u) {
        u.lastSeen = p.lastSeen || null;
        u.online = !!p.online;
        // Propagate name changes from other users
        if (p.displayName && p.displayName !== u.displayName) u.displayName = p.displayName;
      }
    });
    notify();
  }).catch(() => {});
}
function startPresence() {
  _writePresence();
  clearInterval(_presenceInterval);
  _presenceInterval = setInterval(() => {
    _writePresence();
    refreshPresenceOnly();
  }, PRESENCE_INTERVAL);
  window.addEventListener('beforeunload', stopPresence, { once: true });
}
function stopPresence() {
  clearInterval(_presenceInterval);
  _presenceInterval = null;
  if (S.userUid && S.token) {
    dbPut('/presence/' + S.userUid + '.json', { lastSeen: Date.now(), online: false, displayName: '' }).catch(() => {});
    dbPatch('/users/' + S.userUid + '.json', { online: false }).catch(() => {});
  }
}


/* ── Boot ──────────────────────────────────────── */
function boot() {
  const sa = fbGetStoredAuth();
  if (!sa) { S.screen = 'login'; notify(); return; }

  const proceed = (auth) => {
    S.token = auth.token; S.userEmail = auth.email; S.userUid = auth.uid;
    S.screen = 'loading'; notify();
    loadGlobalUsers().then(loadUserClubs).then(() => {
      S.screen = 'clubs'; notify();
      startPresence();
    }).catch(err => {
      if (err.auth) { doLogout(); toast('Sessão expirada — faça login', 'error'); }
      else { S.screen = 'login'; toast('Sem conexão com o servidor', 'error'); notify(); }
    });
  };

  if (sa.needsRefresh && sa.refreshToken) {
    S.screen = 'loading'; S.loadingMsg = '♠ renovando sessão… ♣'; notify();
    fbRefreshToken(sa.refreshToken).then(newAuth => {
      // Preserve email/uid from old stored auth
      newAuth.email = newAuth.email || sa.email;
      newAuth.uid = newAuth.uid || sa.uid;
      proceed(newAuth);
    }).catch(() => {
      doLogout();
      toast('Sessão expirada — faça login novamente', 'error');
    });
  } else {
    proceed(sa);
  }
}

/* ── Club actions ─────────────────────────────── */
function createClub(name, description, logoData) {
  const clubId = genId();
  const code = genClubCode();
  const info = { name, description: description || '', logo: logoData || null, clubCode: code, createdBy: S.userUid, createdAt: new Date().toISOString() };
  return dbPut('/clubs/' + clubId + '/info.json', info)
    .then(() => dbPut('/clubs/' + clubId + '/config/admins/' + S.userUid + '.json', true))
    .then(() => dbPut('/userClubs/' + S.userUid + '/' + clubId + '.json', { status: 'active', role: 'admin', joinedAt: new Date().toISOString() }))
    .then(() => loadUserClubs())
    .then(() => { S.sheet = null; toast('Clube criado!'); notify(); return clubId; });
}
function joinClubByCode(code) {
  return dbGet('/clubs.json').then(clubs => {
    if (!clubs) throw new Error('Clube não encontrado');
    let foundId = null, found = null;
    Object.keys(clubs).forEach(id => {
      const info = clubs[id]?.info;
      if (info && info.clubCode && info.clubCode.toUpperCase() === code.toUpperCase()) {
        foundId = id; found = info;
      }
    });
    if (!foundId) throw new Error('Código não encontrado');
    if (S.userClubs.some(c => c.clubId === foundId)) throw new Error('Você já é membro deste clube');
    return { foundId, found };
  });
}
function requestJoinClub(clubId, clubInfo) {
  return dbPut('/clubs/' + clubId + '/members/' + S.userUid + '.json', {
    status: 'pending', email: S.userEmail, displayName: getCurrentUserName(), requestedAt: new Date().toISOString(),
  }).then(() => dbPut('/userClubs/' + S.userUid + '/' + clubId + '.json', { status: 'pending', role: 'member', requestedAt: new Date().toISOString() }))
    .then(() => { toast('Solicitação enviada — aguarde aprovação'); S.sheet = null; notify(); });
}
function approveMember(uid, email, name) {
  return dbPut(clubPath('/members/' + uid + '.json'), {
    status: 'active', email, displayName: name, approvedBy: S.userUid, approvedAt: new Date().toISOString(),
  }).then(() => dbPatch('/userClubs/' + uid + '/' + S.currentClubId + '.json', { status: 'active', role: 'member' }))
    .then(() => { S.pendingMembers = S.pendingMembers.filter(x => x.uid !== uid); notify(); toast('Membro aprovado'); });
}
function rejectMember(uid) {
  return dbDelete(clubPath('/members/' + uid + '.json'))
    .then(() => dbDelete('/userClubs/' + uid + '/' + S.currentClubId + '.json'))
    .then(() => { S.pendingMembers = S.pendingMembers.filter(x => x.uid !== uid); notify(); });
}
function updateClubInfo(updates) {
  return dbPatch(clubPath('/info.json'), updates).then(() => {
    S.clubInfo = merge(S.clubInfo || {}, updates);
    const idx = findIdx(S.userClubs, c => c.clubId === S.currentClubId);
    if (idx >= 0) S.userClubs[idx].info = S.clubInfo;
    notify();
  });
}
function deleteClub() {
  return dbDelete(clubPath('')).then(() => dbDelete('/userClubs/' + S.userUid + '/' + S.currentClubId + '.json'))
    .then(() => { S.currentClubId = null; S.clubInfo = null; return loadUserClubs(); })
    .then(() => { S.screen = 'clubs'; notify(); toast('Clube excluído'); });
}

/* ── Game actions ─────────────────────────────── */
function saveGame(game) {
  syncSet('saving');
  return dbPut(clubPath('/games/' + game.id + '.json'), game).then(() => {
    syncSet('ok');
    const idx = findIdx(S.games, x => x.id === game.id);
    if (idx >= 0) S.games[idx] = game; else S.games.unshift(game);
    if (S.currentGame && S.currentGame.id === game.id) S.currentGame = game;
    notify();
  }).catch(err => { syncSet('error'); throw err; });
}
function patchGame(gameId, patch) {
  return dbPatch(clubPath('/games/' + gameId + '.json'), patch).then(() => {
    const idx = findIdx(S.games, x => x.id === gameId);
    if (idx >= 0) Object.assign(S.games[idx], patch);
    if (S.currentGame && S.currentGame.id === gameId) Object.assign(S.currentGame, patch);
    notify();
  });
}
/* ── Seasons ─────────────────────────────────── */
function loadSeasons() {
  return dbGet(clubPath('/seasons.json')).then(data => {
    if (data && typeof data === 'object') {
      S.seasons = Object.keys(data).map(id => ({ id, ...data[id] }))
        .sort((a, b) => (a.info?.startDate || '') > (b.info?.startDate || '') ? -1 : 1);
    } else {
      // No seasons yet — create default from existing games
      S.seasons = [];
    }
    // Active season = first with status 'active', fallback to first
    const active = S.seasons.find(s => s.info?.status === 'active');
    S.currentSeasonId = active?.id || S.seasons[0]?.id || null;
  });
}
function createSeason(info) {
  const id = 'season-' + Date.now();
  const payload = { info: { ...info, status: info.status || 'active' } };
  return dbPut(clubPath('/seasons/' + id + '.json'), payload).then(() => {
    S.seasons.unshift({ id, ...payload });
    if (info.status === 'active') S.currentSeasonId = id;
    notify(); toast('Temporada criada');
    return id;
  });
}
function updateSeason(id, info) {
  return dbPatch(clubPath('/seasons/' + id + '/info.json'), info).then(() => {
    const s = S.seasons.find(x => x.id === id);
    if (s) Object.assign(s.info, info);
    // If activating this season, deactivate others
    if (info.status === 'active') {
      S.currentSeasonId = id;
      S.seasons.filter(s => s.id !== id).forEach(s => { if (s.info) s.info.status = 'closed'; });
    }
    rebuildRanking(); notify(); toast('Temporada atualizada');
  });
}
function migrateGamesToSeason(seasonId) {
  const unassigned = S.games.filter(g => !g.seasonId || g.seasonId === 'default');
  if (unassigned.length === 0) return Promise.resolve();
  // Patch each game with the new seasonId
  return Promise.all(
    unassigned.map(g =>
      dbPatch(clubPath('/games/' + g.id + '.json'), { seasonId })
        .then(() => { g.seasonId = seasonId; })
    )
  ).then(() => { rebuildRanking(); notify(); });
}
function deleteSeason(id) {
  if (!confirm('Excluir temporada? Os jogos vinculados não serão excluídos.')) return Promise.resolve();
  return dbDelete(clubPath('/seasons/' + id + '.json')).then(() => {
    S.seasons = S.seasons.filter(x => x.id !== id);
    if (S.currentSeasonId === id) S.currentSeasonId = S.seasons.find(s => s.info?.status==='active')?.id || S.seasons[0]?.id || null;
    rebuildRanking(); notify(); toast('Temporada excluída');
  });
}
function setViewSeason(id) {
  S.currentSeasonId = id === '__all__' ? '__all__' : id;
  rebuildRanking();
  notify();
}

/* ── createGame (tag with seasonId) ──── */
function createGame(payload) {
  const gameId = genId();
  const game = merge({
    id: gameId,
    status: 'open',
    seasonId: S.currentSeasonId || 'default',
    confirmedPlayers: [], sessionPlayers: [], waitingList: [], closedPlayers: [],
    createdBy: S.userUid, createdAt: new Date().toISOString(),
  }, payload);
  return dbPut(clubPath('/games/' + gameId + '.json'), game).then(() => {
    S.games.unshift(game); notify(); toast('Partida criada'); return game;
  });
}
function deleteGame(gameId) {
  return dbDelete(clubPath('/games/' + gameId + '.json')).then(() => {
    S.games = S.games.filter(x => x.id !== gameId);
    rebuildRanking(); notify(); toast('Partida excluída');
  });
}
function closeGame(game, ranked, prizePool, totalInvested, quebra) {
  game.status = 'closed';
  game.closedPlayers = ranked;
  game.prizePool = prizePool;
  game.totalInvested = totalInvested;
  game.prizeRanking = Math.round(prizePool * S.prizeSplitRanking / 100 * 100) / 100;
  game.prizeFinal = Math.round(prizePool * S.prizeSplitFinal / 100 * 100) / 100;
  if (quebra != null) game.quebraRetencao = quebra;
  delete game.sessionPlayers;
  S.accumulatedPrize = Math.round((S.accumulatedPrize + game.prizeFinal) * 100) / 100;
  return dbPut(clubPath('/games/' + game.id + '.json'), game)
    .then(() => dbPut(clubPath('/config/accumulatedPrize.json'), S.accumulatedPrize))
    .then(() => dbGet(clubPath('/games/' + game.id + '.json')))
    .then(fresh => {
      if (fresh) {
        fresh.id = game.id;
        fresh.closedPlayers = toArr(fresh.closedPlayers);
        fresh.confirmedPlayers = toArr(fresh.confirmedPlayers);
        const idx = findIdx(S.games, x => x.id === game.id);
        if (idx >= 0) S.games[idx] = fresh;
        S.currentGame = fresh;
      }
      rebuildRanking(); notify();
    });
}

/* Mensalistas */
function saveMensalistasList() {
  return dbPut(clubPath('/config/mensalistas.json'), S.mensalistasList);
}
function setMensalidadePaid(name, ym, paid) {
  const k = name.replace(/\s/g, '_');
  if (!S.mensalidades[k]) S.mensalidades[k] = {};
  if (paid) {
    S.mensalidades[k][ym] = { paid: true, paidAt: new Date().toISOString(), paidBy: S.userUid };
    return dbPut(clubPath('/mensalidades/' + encodeURIComponent(k) + '/' + ym + '.json'), S.mensalidades[k][ym])
      .then(() => notify());
  } else {
    delete S.mensalidades[k][ym];
    return dbDelete(clubPath('/mensalidades/' + encodeURIComponent(k) + '/' + ym + '.json'))
      .then(() => notify());
  }
}

/* User profile */
function updateMyProfile(patch) {
  const idx = findIdx(S.allUsers, u => u.uid === S.userUid);
  const me = idx >= 0 ? S.allUsers[idx] : {};

  // When name changes, save old name to previousNames for legacy game history lookup
  if (patch.displayName !== undefined && patch.displayName !== me.displayName && me.displayName) {
    const prev = Array.isArray(me.previousNames) ? me.previousNames : [];
    const oldName = me.displayName.trim();
    if (oldName && !prev.includes(oldName)) {
      patch.previousNames = [...prev, oldName].slice(-10); // keep last 10 names
    }
  }

  return dbPatch('/users/' + S.userUid + '.json', patch).then(() => {
    if (idx >= 0) Object.assign(S.allUsers[idx], patch);
    // Propagate name/avatar changes to presence node immediately
    if (patch.displayName !== undefined || patch.avatar !== undefined) {
      const updated = S.allUsers[idx] || {};
      dbPatch('/presence/' + S.userUid + '.json', {
        displayName: updated.displayName || '',
        lastSeen: Date.now(),
        online: true,
      }).catch(() => {});
    }
    notify();
  });
}

Object.assign(window, {
  S, subscribe, notify, update, useStore, toast, syncSet,
  clubPath, isAdmin, isSuperAdmin, getSuperAdminUid, transferSuperAdmin, addAdmin, removeAdmin,
  getMyGameHistory, isOnline, startPresence, stopPresence, refreshPresenceOnly,
  getCurrentUserName, getDisplayName, getMyAvatar, getUserAvatar,
  nameToRankKey, rankKeyDisplay, rebuildRanking,
  loadGlobalUsers, loadUserClubs, loadClubData, loadPendingMembers, loadSeasons,
  createSeason, updateSeason, deleteSeason, setViewSeason, migrateGamesToSeason,
  doLogin, doRegister, doLogout, boot,
  createClub, joinClubByCode, requestJoinClub, approveMember, rejectMember,
  updateClubInfo, deleteClub,
  saveGame, patchGame, createGame, deleteGame, closeGame,
  saveMensalistasList, setMensalidadePaid,
  updateMyProfile,
});
