// gamify-effects.jsx — Gamification layer: confetti, XP, combos, sounds, celebrations
// Draws from game design psychology: variable reinforcement, combo momentum, progress bars, juice

const { useState: useGState, useRef: useGRef, useEffect: useGEffect, useCallback: useGCb, createContext, useContext } = React;

// ═══════════════════════════════════════════════════
// Audio Engine — synthesized sounds via Web Audio API
// ═══════════════════════════════════════════════════
const GamifyAudio = (() => {
  let ctx = null;
  function getCtx() {
    if (!ctx) ctx = new (window.AudioContext || window.webkitAudioContext)();
    if (ctx.state === 'suspended') ctx.resume();
    return ctx;
  }

  function playTone(freq, duration, type = 'sine', vol = 0.15, delay = 0) {
    const c = getCtx();
    const osc = c.createOscillator();
    const gain = c.createGain();
    osc.type = type;
    osc.frequency.setValueAtTime(freq, c.currentTime + delay);
    gain.gain.setValueAtTime(vol, c.currentTime + delay);
    gain.gain.exponentialRampToValueAtTime(0.001, c.currentTime + delay + duration);
    osc.connect(gain);
    gain.connect(c.destination);
    osc.start(c.currentTime + delay);
    osc.stop(c.currentTime + delay + duration);
  }

  return {
    // Satisfying "ding" for task completion
    complete() {
      playTone(880, 0.12, 'sine', 0.12);
      playTone(1320, 0.18, 'sine', 0.10, 0.06);
    },
    // Higher pitched combo ding
    combo(count) {
      const base = 660 + count * 110;
      playTone(base, 0.08, 'sine', 0.10);
      playTone(base * 1.5, 0.14, 'sine', 0.08, 0.04);
      playTone(base * 2, 0.12, 'sine', 0.06, 0.08);
    },
    // Level up fanfare
    levelUp() {
      [523, 659, 784, 1047].forEach((f, i) => {
        playTone(f, 0.22, 'sine', 0.10, i * 0.09);
        playTone(f * 1.5, 0.16, 'triangle', 0.05, i * 0.09 + 0.02);
      });
    },
    // Achievement unlock
    achievement() {
      playTone(784, 0.10, 'sine', 0.12);
      playTone(988, 0.10, 'sine', 0.10, 0.08);
      playTone(1175, 0.25, 'sine', 0.12, 0.16);
    },
    // All done celebration
    allDone() {
      [523, 659, 784, 988, 1175, 1319, 1568].forEach((f, i) => {
        playTone(f, 0.3, 'sine', 0.08, i * 0.07);
        playTone(f * 0.5, 0.25, 'triangle', 0.04, i * 0.07 + 0.02);
      });
    },
  };
})();


// ═══════════════════════════════════════════════════
// Confetti Canvas — high-perf particle system
// ═══════════════════════════════════════════════════
function ConfettiCanvas({ bursts }) {
  const canvasRef = useGRef(null);
  const particlesRef = useGRef([]);
  const rafRef = useGRef(null);

  const COLORS = ['#00d4aa','#00e676','#00b4d8','#fbbf24','#a78bfa','#f472b6','#38bdf8','#34d399','#fb923c','#e879f9'];

  useGEffect(() => {
    if (!bursts || bursts.length === 0) return;
    const latest = bursts[bursts.length - 1];
    const { x, y, count = 40, spread = 1, power = 1 } = latest;

    for (let i = 0; i < count; i++) {
      const angle = (Math.random() * Math.PI * 2);
      const speed = (2 + Math.random() * 6) * power;
      const size = 3 + Math.random() * 5;
      const shape = Math.random() > 0.4 ? 'rect' : 'circle';
      const rotation = Math.random() * 360;
      const rotSpeed = (Math.random() - 0.5) * 12;
      particlesRef.current.push({
        x, y,
        vx: Math.cos(angle) * speed * spread,
        vy: Math.sin(angle) * speed * spread - 2 * power,
        size, color: COLORS[Math.floor(Math.random() * COLORS.length)],
        life: 1, decay: 0.008 + Math.random() * 0.012,
        shape, rotation, rotSpeed,
        gravity: 0.12 + Math.random() * 0.06,
      });
    }

    if (!rafRef.current) animate();
  }, [bursts]);

  function animate() {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const W = canvas.width = canvas.offsetWidth;
    const H = canvas.height = canvas.offsetHeight;
    ctx.clearRect(0, 0, W, H);

    const alive = [];
    for (const p of particlesRef.current) {
      p.x += p.vx;
      p.y += p.vy;
      p.vy += p.gravity;
      p.vx *= 0.98;
      p.life -= p.decay;
      p.rotation += p.rotSpeed;

      if (p.life <= 0) continue;
      alive.push(p);

      ctx.save();
      ctx.translate(p.x, p.y);
      ctx.rotate(p.rotation * Math.PI / 180);
      ctx.globalAlpha = Math.min(1, p.life * 2);
      ctx.fillStyle = p.color;

      if (p.shape === 'rect') {
        ctx.fillRect(-p.size / 2, -p.size / 4, p.size, p.size / 2);
      } else {
        ctx.beginPath();
        ctx.arc(0, 0, p.size / 2, 0, Math.PI * 2);
        ctx.fill();
      }
      ctx.restore();
    }

    particlesRef.current = alive;
    if (alive.length > 0) {
      rafRef.current = requestAnimationFrame(animate);
    } else {
      rafRef.current = null;
    }
  }

  return (
    <canvas ref={canvasRef} style={{
      position: 'fixed', inset: 0, pointerEvents: 'none', zIndex: 99998,
      width: '100%', height: '100%',
    }} />
  );
}


// ═══════════════════════════════════════════════════
// Screen Flash — brief color overlay
// ═══════════════════════════════════════════════════
function ScreenFlash({ flash }) {
  const [active, setActive] = useGState(false);
  const [color, setColor] = useGState('rgba(0,212,170,0.15)');
  const prevFlash = useGRef(0);

  useGEffect(() => {
    if (!flash || flash.id === prevFlash.current) return;
    prevFlash.current = flash.id;
    setColor(flash.color || 'rgba(0,212,170,0.12)');
    setActive(true);
    const t = setTimeout(() => setActive(false), 350);
    return () => clearTimeout(t);
  }, [flash]);

  return (
    <div style={{
      position: 'fixed', inset: 0, pointerEvents: 'none', zIndex: 99990,
      background: active ? color : 'transparent',
      transition: active ? 'none' : 'background 0.35s ease-out',
    }} />
  );
}


// ═══════════════════════════════════════════════════
// XP Bar — persistent progress indicator
// ═══════════════════════════════════════════════════
function XPBar({ xp, level, show, position = 'bottom' }) {
  const XP_PER_LEVEL = 100;
  const currentXP = xp % XP_PER_LEVEL;
  const pct = (currentXP / XP_PER_LEVEL) * 100;
  const [displayPct, setDisplayPct] = useGState(0);
  const [pop, setPop] = useGState(false);
  const prevXP = useGRef(xp);

  useGEffect(() => {
    if (xp !== prevXP.current) {
      setPop(true);
      setTimeout(() => setPop(false), 400);
      prevXP.current = xp;
    }
    setDisplayPct(pct);
  }, [xp, pct]);

  if (!show) return null;

  const isTop = position === 'top';

  return (
    <div style={{
      position: 'fixed',
      [isTop ? 'top' : 'bottom']: 0,
      left: 0, right: 0,
      height: 28,
      background: 'rgba(0,0,0,0.7)',
      backdropFilter: 'blur(10px)',
      display: 'flex', alignItems: 'center',
      padding: '0 14px', gap: 10,
      zIndex: 99995,
      borderTop: isTop ? 'none' : '1px solid rgba(0,212,170,0.2)',
      borderBottom: isTop ? '1px solid rgba(0,212,170,0.2)' : 'none',
      fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
      transition: 'opacity 0.3s',
    }}>
      {/* Level badge */}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 5,
        transform: pop ? 'scale(1.2)' : 'scale(1)',
        transition: 'transform 0.3s cubic-bezier(0.34,1.56,0.64,1)',
      }}>
        <div style={{
          width: 18, height: 18, borderRadius: '50%',
          background: 'linear-gradient(135deg, #fbbf24, #f59e0b)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 9, fontWeight: 800, color: '#000',
          boxShadow: '0 0 8px rgba(251,191,36,0.4)',
        }}>{level}</div>
        <span style={{ fontSize: 10, fontWeight: 700, color: 'rgba(255,255,255,0.5)', letterSpacing: '0.5px' }}>LVL</span>
      </div>

      {/* XP bar */}
      <div style={{
        flex: 1, height: 6, borderRadius: 3,
        background: 'rgba(255,255,255,0.08)',
        overflow: 'hidden', position: 'relative',
      }}>
        <div style={{
          height: '100%', borderRadius: 3,
          width: displayPct + '%',
          background: 'linear-gradient(90deg, #00d4aa, #00e676, #38bdf8)',
          boxShadow: '0 0 12px rgba(0,212,170,0.5)',
          transition: 'width 0.6s cubic-bezier(0.25,1,0.5,1)',
        }} />
        {/* Shimmer */}
        <div style={{
          position: 'absolute', inset: 0,
          background: 'linear-gradient(90deg, transparent 30%, rgba(255,255,255,0.15) 50%, transparent 70%)',
          animation: 'xpShimmer 2.5s ease-in-out infinite',
        }} />
      </div>

      {/* XP text */}
      <span style={{
        fontSize: 10, fontWeight: 700, color: '#00d4aa',
        fontVariantNumeric: 'tabular-nums', minWidth: 55, textAlign: 'right',
        transform: pop ? 'scale(1.15)' : 'scale(1)',
        transition: 'transform 0.3s cubic-bezier(0.34,1.56,0.64,1)',
      }}>{currentXP}/{XP_PER_LEVEL} XP</span>

      <style>{`
        @keyframes xpShimmer {
          0%, 100% { transform: translateX(-100%); }
          50% { transform: translateX(200%); }
        }
      `}</style>
    </div>
  );
}


// ═══════════════════════════════════════════════════
// Combo Indicator — shows multiplier on quick completions
// ═══════════════════════════════════════════════════
function ComboIndicator({ combo, x, y }) {
  if (!combo || combo.count < 2) return null;

  const colors = ['', '', '#00d4aa', '#38bdf8', '#a78bfa', '#fbbf24', '#f472b6', '#ef4444'];
  const clr = colors[Math.min(combo.count, 7)];
  const msgs = ['', '', 'DOUBLE!', 'TRIPLE!', 'MEGA!', 'ULTRA!', 'INSANE!', 'GODLIKE!'];
  const msg = msgs[Math.min(combo.count, 7)];

  return ReactDOM.createPortal(
    <div key={combo.id} style={{
      position: 'fixed',
      left: x || '50%', top: y || '40%',
      transform: 'translate(-50%, -50%)',
      pointerEvents: 'none', zIndex: 99997,
      animation: 'comboIn 0.5s cubic-bezier(0.34,1.56,0.64,1) forwards, comboOut 0.3s 0.8s ease forwards',
    }}>
      <div style={{
        fontSize: combo.count >= 5 ? 48 : combo.count >= 3 ? 38 : 28,
        fontWeight: 900, color: clr,
        textShadow: `0 0 30px ${clr}88, 0 0 60px ${clr}44`,
        letterSpacing: '-1px', lineHeight: 1,
        fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
        textAlign: 'center',
      }}>
        {combo.count}x
        <div style={{ fontSize: 14, fontWeight: 800, letterSpacing: '3px', marginTop: 4, opacity: 0.9 }}>{msg}</div>
      </div>
      <style>{`
        @keyframes comboIn { from { opacity: 0; transform: scale(0.3) rotate(-8deg); } to { opacity: 1; transform: scale(1) rotate(0deg); } }
        @keyframes comboOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-30px); } }
      `}</style>
    </div>,
    document.body
  );
}


// ═══════════════════════════════════════════════════
// XP Popup — "+15 XP" floating text
// ═══════════════════════════════════════════════════
function XPPopup({ popups }) {
  return ReactDOM.createPortal(
    <div style={{ position: 'fixed', inset: 0, pointerEvents: 'none', zIndex: 99996 }}>
      {popups.map(p => (
        <div key={p.id} style={{
          position: 'absolute', left: p.x, top: p.y,
          animation: 'xpFloat 1.2s cubic-bezier(0.25,1,0.5,1) forwards',
          fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
        }}>
          <span style={{
            fontSize: p.bonus ? 18 : 14,
            fontWeight: 800,
            color: p.bonus ? '#fbbf24' : '#00e676',
            textShadow: p.bonus ? '0 0 16px rgba(251,191,36,0.6)' : '0 0 12px rgba(0,230,118,0.4)',
          }}>+{p.amount} XP</span>
        </div>
      ))}
      <style>{`
        @keyframes xpFloat { 
          0% { opacity: 1; transform: translateY(0) scale(1); } 
          60% { opacity: 1; transform: translateY(-40px) scale(1.1); }
          100% { opacity: 0; transform: translateY(-70px) scale(0.8); } 
        }
      `}</style>
    </div>,
    document.body
  );
}


// ═══════════════════════════════════════════════════
// Achievement Toast — slides in from top
// ═══════════════════════════════════════════════════
function AchievementToast({ achievement }) {
  if (!achievement) return null;

  return ReactDOM.createPortal(
    <div key={achievement.id} style={{
      position: 'fixed', top: 40, left: '50%', transform: 'translateX(-50%)',
      zIndex: 99999, pointerEvents: 'none',
      animation: 'achIn 0.5s cubic-bezier(0.25,1,0.5,1) forwards, achOut 0.4s 2.5s ease forwards',
    }}>
      <div style={{
        background: 'rgba(10,10,20,0.95)',
        border: '1px solid rgba(251,191,36,0.3)',
        borderRadius: 16, padding: '12px 22px',
        display: 'flex', alignItems: 'center', gap: 12,
        backdropFilter: 'blur(20px)',
        boxShadow: '0 20px 60px rgba(0,0,0,0.5), 0 0 40px rgba(251,191,36,0.1)',
        fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
      }}>
        <span style={{ fontSize: 28 }}>{achievement.icon}</span>
        <div>
          <div style={{ fontSize: 9, fontWeight: 700, color: '#fbbf24', letterSpacing: '2px', marginBottom: 2 }}>ACHIEVEMENT</div>
          <div style={{ fontSize: 14, fontWeight: 700, color: '#e4e8f1' }}>{achievement.title}</div>
          {achievement.subtitle && <div style={{ fontSize: 11, color: '#6a7394', marginTop: 1 }}>{achievement.subtitle}</div>}
        </div>
      </div>
      <style>{`
        @keyframes achIn { from { opacity: 0; transform: translateX(-50%) translateY(-30px) scale(0.9); } to { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); } }
        @keyframes achOut { from { opacity: 1; } to { opacity: 0; transform: translateX(-50%) translateY(-20px); } }
      `}</style>
    </div>,
    document.body
  );
}


// ═══════════════════════════════════════════════════
// Level Up Overlay — full-screen celebration
// ═══════════════════════════════════════════════════
function LevelUpOverlay({ level, show }) {
  const [visible, setVisible] = useGState(false);

  useGEffect(() => {
    if (show) {
      setVisible(true);
      const t = setTimeout(() => setVisible(false), 2800);
      return () => clearTimeout(t);
    }
  }, [show]);

  if (!visible) return null;

  return ReactDOM.createPortal(
    <div style={{
      position: 'fixed', inset: 0, zIndex: 99999,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      background: 'rgba(0,0,0,0.6)',
      backdropFilter: 'blur(8px)',
      animation: 'lvlIn 0.3s ease forwards, lvlOut 0.5s 2.3s ease forwards',
      pointerEvents: 'none',
    }}>
      <div style={{
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 10,
        animation: 'lvlCardIn 0.6s cubic-bezier(0.34,1.56,0.64,1) forwards',
      }}>
        <div style={{
          fontSize: 11, fontWeight: 800, letterSpacing: '4px', color: '#fbbf24',
          textShadow: '0 0 30px rgba(251,191,36,0.6)',
        }}>LEVEL UP!</div>
        <div style={{
          width: 90, height: 90, borderRadius: '50%',
          background: 'linear-gradient(135deg, #fbbf24, #f59e0b)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 36, fontWeight: 900, color: '#000',
          boxShadow: '0 0 40px rgba(251,191,36,0.5), 0 0 80px rgba(251,191,36,0.2)',
          animation: 'lvlBadgePulse 0.8s ease-in-out infinite alternate',
          fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
        }}>{level}</div>
        <div style={{
          fontSize: 13, fontWeight: 600, color: 'rgba(255,255,255,0.6)',
          fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif",
        }}>Keep going!</div>
      </div>
      <style>{`
        @keyframes lvlIn { from { opacity: 0; } to { opacity: 1; } }
        @keyframes lvlOut { from { opacity: 1; } to { opacity: 0; } }
        @keyframes lvlCardIn { from { opacity: 0; transform: scale(0.5); } to { opacity: 1; transform: scale(1); } }
        @keyframes lvlBadgePulse { from { transform: scale(1); box-shadow: 0 0 40px rgba(251,191,36,0.5); } to { transform: scale(1.05); box-shadow: 0 0 60px rgba(251,191,36,0.7); } }
      `}</style>
    </div>,
    document.body
  );
}


// ═══════════════════════════════════════════════════
// Gamify Context — wraps the app, provides trigger functions
// ═══════════════════════════════════════════════════
const GamifyContext = createContext(null);

const XP_PER_LEVEL = 100;
const XP_TASK = 15;
const XP_ROUTINE = 20;
const XP_COMBO_BONUS = 10;
const COMBO_WINDOW_MS = 4000;

const ACHIEVEMENTS = [
  { trigger: 'firstTask',    icon: '🎯', title: 'First Blood',       subtitle: 'Complete your first task' },
  { trigger: 'combo3',       icon: '🔥', title: 'On Fire!',          subtitle: '3x combo streak' },
  { trigger: 'combo5',       icon: '⚡', title: 'Unstoppable',       subtitle: '5x combo streak' },
  { trigger: 'allTodos',     icon: '🏆', title: 'Clean Slate',       subtitle: 'All tasks done' },
  { trigger: 'allRoutines',  icon: '💎', title: 'Routine Master',    subtitle: 'All routines done' },
  { trigger: 'level5',       icon: '⭐', title: 'Rising Star',       subtitle: 'Reach level 5' },
  { trigger: 'level10',      icon: '👑', title: 'Productivity King', subtitle: 'Reach level 10' },
];

function GamifyProvider({ children, enabled, triggerRef }) {
  const [xp, setXP] = useGState(() => parseInt(localStorage.getItem('el-gamify-xp') || '0'));
  const [level, setLevel] = useGState(() => parseInt(localStorage.getItem('el-gamify-level') || '1'));
  const [totalCompleted, setTotalCompleted] = useGState(() => parseInt(localStorage.getItem('el-gamify-total') || '0'));
  const [bursts, setBursts] = useGState([]);
  const [flash, setFlash] = useGState(null);
  const [combo, setCombo] = useGState(null);
  const [xpPopups, setXPPopups] = useGState([]);
  const [achievement, setAchievement] = useGState(null);
  const [levelUp, setLevelUp] = useGState(null);
  const [unlockedAch, setUnlockedAch] = useGState(() => {
    try { return JSON.parse(localStorage.getItem('el-gamify-ach') || '[]'); } catch { return []; }
  });

  const comboRef = useGRef({ count: 0, timer: null });
  const flashId = useGRef(0);
  const comboId = useGRef(0);
  const popupId = useGRef(0);
  const achId = useGRef(0);
  const levelUpId = useGRef(0);

  // Persist
  useGEffect(() => {
    localStorage.setItem('el-gamify-xp', String(xp));
    localStorage.setItem('el-gamify-level', String(level));
    localStorage.setItem('el-gamify-total', String(totalCompleted));
    localStorage.setItem('el-gamify-ach', JSON.stringify(unlockedAch));
  }, [xp, level, totalCompleted, unlockedAch]);

  const unlock = useGCb((trigger) => {
    if (unlockedAch.includes(trigger)) return;
    const ach = ACHIEVEMENTS.find(a => a.trigger === trigger);
    if (!ach) return;
    setUnlockedAch(prev => [...prev, trigger]);
    achId.current++;
    setAchievement({ ...ach, id: achId.current });
    GamifyAudio.achievement();
  }, [unlockedAch]);

  // The main trigger — call when something is completed
  const triggerCompletion = useGCb(({ x, y, type = 'task', allDoneCheck }) => {
    if (!enabled) return;

    // Combo tracking
    clearTimeout(comboRef.current.timer);
    comboRef.current.count++;
    const cCount = comboRef.current.count;
    comboRef.current.timer = setTimeout(() => {
      comboRef.current.count = 0;
      setCombo(null);
    }, COMBO_WINDOW_MS);

    // XP calculation
    const baseXP = type === 'routine' ? XP_ROUTINE : XP_TASK;
    const comboBonus = cCount >= 2 ? XP_COMBO_BONUS * (cCount - 1) : 0;
    const totalXPGain = baseXP + comboBonus;

    // Update XP + level
    setXP(prev => {
      const newXP = prev + totalXPGain;
      const newLevel = Math.floor(newXP / XP_PER_LEVEL) + 1;
      if (newLevel > level) {
        setLevel(newLevel);
        levelUpId.current++;
        setLevelUp({ level: newLevel, id: levelUpId.current });
        GamifyAudio.levelUp();
        if (newLevel >= 5) unlock('level5');
        if (newLevel >= 10) unlock('level10');
      }
      return newXP;
    });

    setTotalCompleted(prev => prev + 1);

    // Confetti burst
    const burstX = x || window.innerWidth / 2;
    const burstY = y || window.innerHeight / 2;
    const power = cCount >= 5 ? 1.8 : cCount >= 3 ? 1.3 : 1;
    const count = cCount >= 5 ? 80 : cCount >= 3 ? 55 : 35;
    setBursts(prev => [...prev.slice(-5), { x: burstX, y: burstY, count, power, spread: 1.2 }]);

    // Screen flash
    flashId.current++;
    const flashColor = cCount >= 5 ? 'rgba(251,191,36,0.18)' : cCount >= 3 ? 'rgba(0,180,216,0.15)' : 'rgba(0,212,170,0.12)';
    setFlash({ id: flashId.current, color: flashColor });

    // Combo display
    if (cCount >= 2) {
      comboId.current++;
      setCombo({ count: cCount, id: comboId.current });
      GamifyAudio.combo(cCount);
    } else {
      GamifyAudio.complete();
    }

    // XP popup
    popupId.current++;
    setXPPopups(prev => [...prev.slice(-8), {
      id: popupId.current,
      x: burstX, y: burstY - 20,
      amount: totalXPGain,
      bonus: comboBonus > 0,
    }]);
    // Clean old popups
    setTimeout(() => {
      setXPPopups(prev => prev.slice(1));
    }, 1500);

    // Achievements
    if (totalCompleted === 0) unlock('firstTask');
    if (cCount === 3) unlock('combo3');
    if (cCount === 5) unlock('combo5');

    // Check all done
    if (allDoneCheck) {
      const { type: checkType, allDone } = allDoneCheck;
      if (allDone && checkType === 'todos') {
        unlock('allTodos');
        GamifyAudio.allDone();
        // Extra confetti burst
        setTimeout(() => {
          setBursts(prev => [...prev, { x: window.innerWidth / 2, y: window.innerHeight / 2, count: 100, power: 2, spread: 1.5 }]);
        }, 300);
      }
      if (allDone && checkType === 'routines') {
        unlock('allRoutines');
        GamifyAudio.allDone();
        setTimeout(() => {
          setBursts(prev => [...prev, { x: window.innerWidth / 2, y: window.innerHeight / 2, count: 100, power: 2, spread: 1.5 }]);
        }, 300);
      }
    }
  }, [enabled, level, totalCompleted, unlock]);

  const value = { triggerCompletion, xp, level, totalCompleted, enabled };

  // Expose trigger to parent via ref (so parent doesn't need useContext)
  useGEffect(() => {
    if (triggerRef) triggerRef.current = triggerCompletion;
  }, [triggerCompletion, triggerRef]);

  return (
    <GamifyContext.Provider value={value}>
      {children}
      {enabled && (
        <>
          <ConfettiCanvas bursts={bursts} />
          <ScreenFlash flash={flash} />
          <XPBar xp={xp} level={level} show={enabled} />
          <ComboIndicator combo={combo} />
          <XPPopup popups={xpPopups} />
          <AchievementToast achievement={achievement} />
          <LevelUpOverlay level={levelUp?.level} show={!!levelUp} />
        </>
      )}
    </GamifyContext.Provider>
  );
}

function useGamify() {
  return useContext(GamifyContext) || { triggerCompletion: () => {}, xp: 0, level: 1, enabled: false };
}

// ═══════════════════════════════════════════════════
// Gamify Toggle Row — for avatar/settings menus
// ═══════════════════════════════════════════════════
function GamifyToggleRow({ enabled, onToggle, theme = 'dark' }) {
  const isDark = theme === 'dark';
  const DK_pri = isDark ? '#e4e8f1' : '#1a1816';
  const DK_sec = isDark ? '#6a7394' : '#5a534a';
  const DK_accent = isDark ? '#00d4aa' : '#007a5e';
  const hoverBg = isDark ? 'rgba(255,255,255,0.07)' : 'rgba(26,24,22,0.05)';

  return (
    <div onClick={onToggle}
      style={{
        display: 'flex', alignItems: 'center', gap: 10,
        padding: '9px 13px', borderRadius: 10, cursor: 'pointer',
        fontSize: 13, fontWeight: 500, color: DK_pri,
        userSelect: 'none', transition: 'background .12s',
      }}
      onMouseEnter={e => e.currentTarget.style.background = hoverBg}
      onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
      <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke={DK_sec} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
        <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
      </svg>
      <span style={{ flex: 1 }}>Gamify</span>
      <div style={{
        width: 36, height: 20, borderRadius: 10,
        background: enabled ? DK_accent : (isDark ? '#3a4260' : '#9a9388'),
        position: 'relative', flexShrink: 0, transition: 'background .2s',
      }}>
        <div style={{
          position: 'absolute', width: 14, height: 14, borderRadius: '50%',
          background: '#fff', top: 3,
          left: enabled ? 19 : 3,
          transition: 'left .2s cubic-bezier(.25,1,.5,1)',
          pointerEvents: 'none',
        }} />
      </div>
    </div>
  );
}


// Export everything
Object.assign(window, {
  GamifyProvider, useGamify, GamifyContext,
  GamifyToggleRow, GamifyAudio,
  ConfettiCanvas, ScreenFlash, XPBar, ComboIndicator, XPPopup, AchievementToast, LevelUpOverlay,
});
