// ——————————————————————————————————————————————————————————————
// Sticker / achievement logic — SERVER-DRIVEN single source of truth.
//
// Every sticker is derived from the player's real saved scores on the server
// (identified by their email/profile), NOT from localStorage. The Achievements
// page fetches `?action=mine&email=…` — the player's per-game best score + play
// count — and passes those rows to computeBadges() below.
//
// Because the server only stores each game's best score + play count, every
// sticker condition is expressed in those terms:
//   • best score thresholds (e.g. Memory Zoo under 20 moves)
//   • play counts        (e.g. Bubble Maze finished 3×)
//   • distinct games / categories played
// ——————————————————————————————————————————————————————————————
(function () {
  // Fetch the signed-in player's per-game rows from the server.
  // Returns { ok, rows: [{ game, plays, minScore, maxScore }] } or
  // { ok:false } when there's no profile / the request failed.
  window.fetchMyStickers = async function () {
    const email = (typeof window.getStoredEmail === 'function') ? window.getStoredEmail() : '';
    if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
      return { ok: false, noProfile: true, rows: [] };
    }
    try {
      const r = await fetch(`${window.LEADERBOARD_API}?action=mine&email=${encodeURIComponent(email)}`);
      if (!r.ok) return { ok: false, rows: [] };
      const data = await r.json();
      if (!data || data.error) return { ok: false, rows: [] };
      // Also resolve the player's lifetime XP total (powers the rank stickers).
      // The nav points badge treats XP as max(local cache, server total) and
      // caches it in localStorage under `gl_total_xp`. We mirror that exactly so
      // the rank ladder always agrees with the XP number shown in the nav —
      // otherwise a player at 100k+ XP could see only Rookie unlocked if the
      // server `player` SUM lags behind the cached value (or the fetch fails).
      let serverXp = 0;
      try {
        const xr = await fetch(`${window.LEADERBOARD_API}?action=player&email=${encodeURIComponent(email)}`);
        if (xr.ok) {
          const xd = await xr.json();
          if (xd && !xd.error) serverXp = Number(xd.totalXp) || 0;
        }
      } catch { /* keep serverXp = 0 */ }
      let cachedXp = 0;
      try { cachedXp = parseInt(localStorage.getItem('gl_total_xp') || '0', 10) || 0; } catch { /* ignore */ }
      const totalXp = Math.max(serverXp, cachedXp);
      return { ok: true, rows: Array.isArray(data.games) ? data.games : [], totalXp };
    } catch {
      return { ok: false, rows: [] };
    }
  };

  // Pure: turn the player's server rows into the sticker list with unlock state.
  // rows: [{ game, plays, minScore, maxScore }]; totalXp: lifetime XP number.
  window.computeBadges = function (rows, totalXp) {
    rows = Array.isArray(rows) ? rows : [];
    const GAMES = window.GAMES || [];
    const byId = {};
    rows.forEach(r => { byId[r.game] = r; });

    const gameDef = id => GAMES.find(g => g.id === id);
    const played  = id => !!byId[id];
    // Direction-aware personal best from the server row (lower-is-better games
    // use the MIN score, everything else the MAX).
    const best = (id) => {
      const r = byId[id]; if (!r) return null;
      const g = gameDef(id);
      return (g && g.lowerIsBetter) ? r.minScore : r.maxScore;
    };
    const plays = id => (byId[id] ? byId[id].plays : 0);

    // Distinct games the player has actually saved a score in.
    const distinctPlayed = rows.length;
    // Total games played across everything (sum of per-game play counts).
    const totalPlays = rows.reduce((a, r) => a + (r.plays || 0), 0);

    // Categories played — Drawing is excluded from "every category" because its
    // games (Doodle Pad, Color Studio) have no score to save, so they can never
    // appear in the server rows. Requiring them would make Rainbow unearnable.
    const requiredCats = (window.CATEGORIES || []).filter(c => c !== 'All' && c !== 'Drawing');
    const playedCats = new Set(
      rows.map(r => { const g = gameDef(r.game); return g && g.cat; }).filter(Boolean)
    );
    const rainbow = requiredCats.length > 0 && requiredCats.every(c => playedCats.has(c));

    const memBest   = best('memory-zoo');   // fewest moves
    const catchBest = best('math-catch');    // most caught
    const wordBest  = best('word-builder');  // most words in a round

    // ——— Rank stickers, driven by lifetime XP ———
    // Ranks are cumulative: reaching a tier's minimum unlocks it for good, so
    // they accumulate into a progression ladder rather than only showing the
    // single band the player currently sits in.
    const xp = Number(totalXp) || 0;
    const fmtXp = n => n.toLocaleString('en-US');
    const RANK_TIERS = [
      { n: 'Rookie',        glyph: '🌱', min: 0,       color: 'var(--c-grass)' },
      { n: 'Explorer',      glyph: '🔍', min: 1000,    color: 'var(--c-sky)' },
      { n: 'Adventurer',    glyph: '⚔️', min: 5000,    color: 'var(--c-coral)' },
      { n: 'Champion',      glyph: '🏆', min: 15000,   color: 'var(--c-sun)' },
      { n: 'Hero',          glyph: '🦸', min: 40000,   color: 'var(--c-grape)' },
      { n: 'Legend',        glyph: '👑', min: 75000,   color: 'var(--c-grass)' },
      { n: 'Mythic Legend', glyph: '💎', min: 100000,  color: 'var(--c-sky)' },
      { n: 'Immortal',      glyph: '🔥', min: 150000,  color: 'var(--c-coral)' },
      { n: 'Overlord',      glyph: '🔱', min: 200000,  color: 'var(--c-sun)' },
      { n: 'Vanguard',      glyph: '⚡', min: 300000,  color: 'var(--c-grape)' },
      { n: 'Ascendant',     glyph: '🌟', min: 500000,  color: 'var(--c-grass)' },
      { n: 'Elite',         glyph: '👑✨', min: 1000000, color: 'var(--c-sky)' },
    ];
    const rankBadges = RANK_TIERS.map(t => ({
      n: t.n, glyph: t.glyph, color: t.color,
      d: t.min === 0 ? 'Welcome! Your XP journey starts here.' : `Reach ${fmtXp(t.min)} lifetime XP.`,
      unlocked: xp >= t.min,
    }));

    const gameBadges = [
      { n: 'First Match',  d: 'Finish your first game of Memory Zoo.',     color: 'var(--c-sun)',   glyph: '★', unlocked: played('memory-zoo') },
      { n: 'Sharp Eye',    d: 'Win Memory Zoo in under 20 moves.',         color: 'var(--c-coral)', glyph: '✚', unlocked: memBest != null && memBest < 20 },
      { n: 'Quick Hands',  d: 'Score 10 in Number Catcher.',              color: 'var(--c-sky)',   glyph: '◆', unlocked: catchBest != null && catchBest >= 10 },
      { n: 'Number Ninja', d: 'Score 25 in Number Catcher.',              color: 'var(--c-grass)', glyph: '▲', unlocked: catchBest != null && catchBest >= 25 },
      { n: 'Globetrotter', d: 'Save a score in 5 different games.',        color: 'var(--c-grape)', glyph: '●', unlocked: distinctPlayed >= 5 },
      { n: 'Speller',      d: 'Build 10 words in one Word Builder round.', color: 'var(--c-sun)',   glyph: '✚', unlocked: wordBest != null && wordBest >= 10 },
      { n: 'Maze Master',  d: 'Finish Bubble Maze 3 times.',              color: 'var(--c-coral)', glyph: '◆', unlocked: plays('maze-runner') >= 3 },
      { n: 'Rainbow',      d: 'Play a game from every category (drawing not needed).', color: 'var(--c-sky)', glyph: '★', unlocked: rainbow },

      // ——— Math whizzes ———
      { n: 'Math Whiz',     d: 'Score 8 or more in Add It Up.',            color: 'var(--c-coral)', glyph: '✚', unlocked: best('add-it-up') != null && best('add-it-up') >= 8 },
      { n: 'Garden Counter',d: 'Score 8 or more in Counting Garden.',      color: 'var(--c-grass)', glyph: '●', unlocked: best('count-garden') != null && best('count-garden') >= 8 },
      { n: 'Times Tabler',  d: 'Practise your tables in Times Table.',      color: 'var(--c-coral)', glyph: '▲', unlocked: played('times-table') },

      // ——— Word & letter heroes ———
      { n: 'Safari Scout',  d: 'Score 8 or more in Alphabet Safari.',      color: 'var(--c-grass)', glyph: '▲', unlocked: best('alphabet-safari') != null && best('alphabet-safari') >= 8 },
      { n: 'Alphabet Frog', d: 'Hop all the way to Z in Letter Hop.',       color: 'var(--c-coral)', glyph: '●', unlocked: best('letter-hop') != null && best('letter-hop') >= 25 },
      { n: 'Word Shuffler', d: "Play Aaliyah's Shuffle Quest.",             color: 'var(--c-coral)', glyph: '✚', unlocked: played('shuffle-quest') },

      // ——— Memory & matching ———
      { n: 'Pair Pro',      d: 'Score 8 or more in Find the Pair.',         color: 'var(--c-grass)', glyph: '◆', unlocked: best('find-the-pair') != null && best('find-the-pair') >= 8 },
      { n: 'Memory Master', d: 'Win Super Memory in under 40 moves.',       color: 'var(--c-grape)', glyph: '✚', unlocked: best('memory-match-hard') != null && best('memory-match-hard') < 40 },

      // ——— Quiz champs ———
      { n: 'Quiz Whiz',     d: 'Score 8 or more in Quiz Quest.',           color: 'var(--c-sky)',   glyph: '★', unlocked: best('quiz-quest') != null && best('quiz-quest') >= 8 },
      { n: 'Fact Hunter',   d: 'Score 7 or more in Animal Facts.',         color: 'var(--c-sun)',   glyph: '✚', unlocked: best('animal-facts') != null && best('animal-facts') >= 7 },

      // ——— Arcade aces ———
      { n: 'Star Pilot',    d: 'Score 50 or more in Space Dodge.',          color: 'var(--c-grape)', glyph: '★', unlocked: best('space-dodge') != null && best('space-dodge') >= 50 },
      { n: 'Rainbow Streak',d: 'Score 12 or more in Rainbow Tap.',          color: 'var(--c-sky)',   glyph: '◆', unlocked: best('rainbow-tap') != null && best('rainbow-tap') >= 12 },
      { n: 'Dino Friend',   d: 'Go for a run in Aydin Dino Run.',           color: 'var(--c-grass)', glyph: '▲', unlocked: played('dino-run') },
      { n: 'Treasure Hunter',d: "Play Don't Dig the Bomb!",                 color: 'var(--c-coral)', glyph: '◆', unlocked: played('dig-bomb') },

      // ——— Puzzle solvers ———
      { n: 'Chess Player',  d: 'Play a game of The Game Of Chess.',         color: 'var(--c-grape)', glyph: '★', unlocked: played('chess') },
      { n: 'Maze Wizard',   d: 'Finish a level of Shape Maze.',             color: 'var(--c-grass)', glyph: '◆', unlocked: played('shape-maze') },
      { n: 'Puzzle Slider', d: 'Solve the Picture Puzzle.',                color: 'var(--c-sun)',   glyph: '▲', unlocked: played('piece-puzzle') },

      // ——— Big milestones ———
      { n: 'Game Collector',d: 'Save a score in 10 different games.',       color: 'var(--c-sky)',   glyph: '●', unlocked: distinctPlayed >= 10 },
      { n: 'Lab Legend',    d: 'Save a score in 15 different games.',       color: 'var(--c-grape)', glyph: '★', unlocked: distinctPlayed >= 15 },
      { n: 'Marathon Player',d: 'Play 40 games in total.',                 color: 'var(--c-coral)', glyph: '✚', unlocked: totalPlays >= 40 },
    ];

    // Rank ladder leads the sticker book, then the per-game achievements.
    return rankBadges.concat(gameBadges);
  };
})();
