// ─────────────────────────────────────────────────────────────
// Pixel Paulyn + Sparkle Hunt mini game
// Hand-drawn 16×20 sprite (long balayage hair, warm skin, black top, gold necklace)
// Arrow keys / A+D or tap to move. Catch ✦ sparkles, dodge ☕ coffee spills.
// ─────────────────────────────────────────────────────────────

// ─────────────────────────────────────────────────────────────
// Hand-drawn pixel Paulyn — 24×32 sprite, 4 frames
// Legend: .=transparent  a=hairD  b=hairM  c=hairL  d=hairH
//         s=skin  k=skinShadow  p=blush  e=eye  l=lip  m=tooth
//         t=suitBlack  u=suitHighlight  g=gold  w=sparkleWhite
// ─────────────────────────────────────────────────────────────
const CHAR_MAP = {
  '.': null,
  a: '#2a1810', // darkest hair
  b: '#4a2c1a', // mid brown
  c: '#7a4a2a', // warm highlight
  d: '#b8834a', // balayage tip
  s: '#f0c8a8', // skin
  k: '#d49a78', // skin shadow
  p: '#f28aa0', // blush
  e: '#1a0f08', // eye
  l: '#c76a6a', // lip
  m: '#ffe8d8', // tooth
  t: '#0e0e10', // suit black
  u: '#1e1e24', // suit highlight
  g: '#e8c56a', // gold
  w: '#ffffff', // sparkle highlight
};

// Common head + hair (rows 0–15) — shared across all frames
const HEAD = [
  '........aabbbb..........',
  '......aabbccbba.........',
  '.....abbccddccba........',
  '....abbccddddccba.......',
  '...abccddsssdccba.......',   // hair frames face
  '..abccdssssssdccba......',
  '..abcdssskkssddcba......',
  '..abcdseeskseedcba......',   // eyes
  '..abcdsssksssddcba......',
  '..abcdspsksspsdcba......',   // blush
  '..abcdsssksssddcba......',
  '..abcdssslllsddcba......',   // lips
  '..abcdsssmmmsddcba......',   // smile
  '..abccddssssddccba......',
  '.abbccdddddddccbba......',
  'abbccddddddddddccbba....',
];

// Body variants (rows 16–31) per frame
const BODY_IDLE = [
  'abccdddddddddddddcccba..',
  'abbcdddddddddddddcccbba.',
  '.bttuuuuuuuuuuuuuutttb..',   // shoulders + suit
  '.btuuuuggggggguuuuutb...',   // gold necklace
  '.tuuuuuuuuuuuuuuuuutt...',
  '.tuuuuuuuuuuuuuuuuutt...',
  '.tuuuuuuuuuuuuuuuuutt...',
  '.tuuuuu.......uuuuutt...',   // waist gap illusion
  '.ttttt.........ttttt....',   // arms by side → pants start
  '..ttt...........ttt.....',
  '..ttt...........ttt.....',
  '..ttt...........ttt.....',
  '..ttt...........ttt.....',
  '..ttt...........ttt.....',
  '..ttt...........ttt.....',
  '..tt.............tt.....',
];

const BODY_WALK_A = [
  'abccdddddddddddddcccba..',
  'abbcdddddddddddddcccbba.',
  '.bttuuuuuuuuuuuuuutttb..',
  '.btuuuuggggggguuuuutb...',
  '.tuuuuuuuuuuuuuuuuutt...',
  '.tuuuuuuuuuuuuuuuuutt...',
  '.tuuuuuuuuuuuuuuuuutt...',
  '.tuuuuu.......uuuuutt...',
  '.ttttt.........ttttt....',
  '.ttt.............ttt....',   // left leg forward
  '.ttt..............tt....',
  '..tt..............tt....',
  '..tt...............t....',
  '..tt................t...',
  '..tt................t...',
  '..ttt................tt.',
];

const BODY_WALK_B = [
  'abccdddddddddddddcccba..',
  'abbcdddddddddddddcccbba.',
  '.bttuuuuuuuuuuuuuutttb..',
  '.btuuuuggggggguuuuutb...',
  '.tuuuuuuuuuuuuuuuuutt...',
  '.tuuuuuuuuuuuuuuuuutt...',
  '.tuuuuuuuuuuuuuuuuutt...',
  '.tuuuuu.......uuuuutt...',
  '.ttttt.........ttttt....',
  '..ttt............ttt....',   // right leg forward
  '..tt..............ttt...',
  '..tt..............tt....',
  '.t...............tt.....',
  't................tt.....',
  't................tt.....',
  '.tt...............ttt...',
];

const BODY_WAVE = [
  'abccdddddddddddddcccba.w',   // sparkle top-right
  'abbcdddddddddddddcccbbaw',
  '.bttuuuuuuuuuuuuuutttbww',   // raised arm glint
  '.btuuuuggggggguuuuuttss.',   // waving arm up
  '.tuuuuuuuuuuuuuuuuututss',
  '.tuuuuuuuuuuuuuuuuuuttts',
  '.tuuuuuuuuuuuuuuuuuuutt.',
  '.tuuuuu.......uuuuuutt..',
  '.ttttt.........tttttt...',
  '..ttt...........ttt.....',
  '..ttt...........ttt.....',
  '..ttt...........ttt.....',
  '..ttt...........ttt.....',
  '..ttt...........ttt.....',
  '..ttt...........ttt.....',
  '..tt.............tt.....',
];

const SPRITE_IDLE   = [...HEAD, ...BODY_IDLE];
const SPRITE_WALK_A = [...HEAD, ...BODY_WALK_A];
const SPRITE_WALK_B = [...HEAD, ...BODY_WALK_B];
const SPRITE_WAVE   = [...HEAD, ...BODY_WAVE];

function drawSprite(ctx, sprite, x, y, scale = 1) {
  for (let r = 0; r < sprite.length; r++) {
    const row = sprite[r];
    for (let c = 0; c < row.length; c++) {
      const color = CHAR_MAP[row[c]];
      if (!color) continue;
      ctx.fillStyle = color;
      ctx.fillRect(x + c * scale, y + r * scale, scale, scale);
    }
  }
}

// Render a sprite to an offscreen canvas of given pixel size (for <img>-style use)
function spriteToCanvas(sprite, size) {
  const sw = sprite[0].length;
  const sh = sprite.length;
  const scale = Math.floor(size / Math.max(sw, sh));
  const canvas = document.createElement('canvas');
  canvas.width = sw * scale;
  canvas.height = sh * scale;
  const ctx = canvas.getContext('2d');
  ctx.imageSmoothingEnabled = false;
  drawSprite(ctx, sprite, 0, 0, scale);
  return canvas;
}

// ─────────────────────────────────────────────────────────────
// Standalone static avatar (for hero-section use) — uses real chibi art
// ─────────────────────────────────────────────────────────────
function PixelAvatar({ size = 160, animated = true }) {
  // Just idle — no wave
  return (
    <img
      src="assets/paulyn-idle.png"
      alt="Paulyn"
      style={{ imageRendering: 'pixelated', display: 'block', height: size, width: 'auto' }}
    />
  );
}

// ─────────────────────────────────────────────────────────────
// Sparkle Hunt mini game
// ─────────────────────────────────────────────────────────────
function SparkleHunt({ accent = '#ff9ec7', chromeGradient }) {
  const canvasRef = React.useRef(null);
  const stateRef = React.useRef({
    player: { x: 200, y: 0, vx: 0, frame: 0, frameT: 0, facing: 1 },
    items: [],
    particles: [],
    keys: {},
    lastSpawn: 0,
    lastT: 0,
    hazardLastSpawn: 0,
  });
  const [score, setScore] = React.useState(0);
  const [lives, setLives] = React.useState(3);
  const [timeLeft, setTimeLeft] = React.useState(60);
  const [status, setStatus] = React.useState('idle'); // idle | playing | over
  const [hi, setHi] = React.useState(() => {
    try { return parseInt(localStorage.getItem('paulyn-sparkle-hi') || '0', 10); } catch (e) { return 0; }
  });

  const W = 560, H = 320;
  const PW = 64, PH = 120;
  const GROUND_Y = H - PH - 6;

  // Load real chibi PNG sprites
  const spritesRef = React.useRef({ loaded: false, idle: null, walkA: null, walkB: null });
  React.useEffect(() => {
    const load = (src) => new Promise(res => { const img = new Image(); img.onload = () => res(img); img.onerror = () => res(null); img.src = src; });
    Promise.all([
      load('assets/paulyn-idle.png'),
      load('assets/paulyn-walk-a.png'),
      load('assets/paulyn-walk-b.png'),
    ]).then(([idle, walkA, walkB]) => {
      spritesRef.current = { loaded: true, idle, walkA, walkB };
    });
  }, []);

  const reset = React.useCallback(() => {
    stateRef.current = {
      player: { x: W / 2 - PW / 2, y: GROUND_Y, vx: 0, frame: 0, frameT: 0, facing: 1 },
      items: [], particles: [], keys: {},
      lastSpawn: 0, lastT: 0, hazardLastSpawn: 0,
    };
    setScore(0); setLives(3); setTimeLeft(60);
  }, [GROUND_Y, PW]);

  const start = () => { reset(); setStatus('playing'); };

  // Input handlers (keyboard)
  React.useEffect(() => {
    const gameKeys = ['ArrowLeft','ArrowRight','a','A','d','D',' '];
    const down = (e) => {
      if (gameKeys.includes(e.key)) {
        e.preventDefault();
      }
      stateRef.current.keys[e.key] = true;
      if (e.key === ' ' && status !== 'playing') { start(); }
    };
    const up = (e) => { stateRef.current.keys[e.key] = false; };
    window.addEventListener('keydown', down);
    window.addEventListener('keyup', up);
    return () => {
      window.removeEventListener('keydown', down);
      window.removeEventListener('keyup', up);
    };
  }, [status]);

  // Touch controls via button presses (bound below)
  const press = (key, v) => { stateRef.current.keys[key] = v; };

  // Main loop
  React.useEffect(() => {
    if (status !== 'playing') return;
    let raf;
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    ctx.imageSmoothingEnabled = false;

    const step = (t) => {
      const s = stateRef.current;
      const dt = Math.min(0.05, (t - (s.lastT || t)) / 1000);
      s.lastT = t;

      // --- update player ---
      const left = s.keys['ArrowLeft'] || s.keys['a'] || s.keys['A'];
      const right = s.keys['ArrowRight'] || s.keys['d'] || s.keys['D'];
      const speed = 260;
      s.player.vx = (right ? speed : 0) - (left ? speed : 0);
      s.player.x += s.player.vx * dt;
      s.player.x = Math.max(4, Math.min(W - PW - 4, s.player.x));

      // Walk frame + facing
      if (Math.abs(s.player.vx) > 1) {
        s.player.facing = s.player.vx > 0 ? 1 : -1;
        s.player.frameT += dt;
        if (s.player.frameT > 0.22) { s.player.frameT = 0; s.player.frame = 1 - s.player.frame; }
      } else s.player.frame = 0;

      // --- spawn sparkles ---
      const spawnGap = Math.max(0.35, 0.9 - score * 0.01);
      if (t - s.lastSpawn > spawnGap * 1000) {
        s.lastSpawn = t;
        s.items.push({
          type: 'sparkle',
          x: 20 + Math.random() * (W - 40),
          y: -20,
          vy: 80 + Math.random() * 80 + score * 2,
          rot: Math.random() * Math.PI * 2,
          size: 14 + Math.random() * 8,
          hue: Math.random() < 0.15 ? 'rare' : 'normal',
        });
      }
      // --- spawn hazards (coffee spill) ---
      if (t - s.hazardLastSpawn > 2200 && score > 5) {
        s.hazardLastSpawn = t;
        s.items.push({
          type: 'coffee',
          x: 30 + Math.random() * (W - 60),
          y: -20,
          vy: 120 + Math.random() * 60,
          rot: 0,
          size: 20,
        });
      }

      // --- update items ---
      for (let i = s.items.length - 1; i >= 0; i--) {
        const it = s.items[i];
        it.y += it.vy * dt;
        it.rot += dt * 2;
        // collision with head area (top half of sprite)
        const hx = s.player.x + PW * 0.2;
        const hy = s.player.y + PH * 0.05;
        const hw = PW * 0.6;
        const hh = PH * 0.55;
        if (it.y + it.size > hy && it.y < hy + hh &&
            it.x + it.size > hx && it.x < hx + hw) {
          if (it.type === 'sparkle') {
            const gain = it.hue === 'rare' ? 5 : 1;
            setScore(sc => sc + gain);
            // particles
            for (let p = 0; p < 8; p++) {
              s.particles.push({
                x: it.x, y: it.y,
                vx: (Math.random() - 0.5) * 160,
                vy: -60 - Math.random() * 60,
                life: 0.7,
                color: it.hue === 'rare' ? '#fff5a8' : accent,
              });
            }
          } else if (it.type === 'coffee') {
            setLives(l => l - 1);
            for (let p = 0; p < 12; p++) {
              s.particles.push({
                x: it.x, y: it.y,
                vx: (Math.random() - 0.5) * 200,
                vy: -80 - Math.random() * 80,
                life: 0.8,
                color: '#6b4226',
              });
            }
          }
          s.items.splice(i, 1);
          continue;
        }
        if (it.y > H + 30) s.items.splice(i, 1);
      }

      // --- update particles ---
      for (let i = s.particles.length - 1; i >= 0; i--) {
        const p = s.particles[i];
        p.x += p.vx * dt; p.y += p.vy * dt; p.vy += 300 * dt; p.life -= dt;
        if (p.life <= 0) s.particles.splice(i, 1);
      }

      // --- render ---
      // sky gradient
      const bg = ctx.createLinearGradient(0, 0, 0, H);
      bg.addColorStop(0, '#2a1040');
      bg.addColorStop(0.5, '#6b2d6a');
      bg.addColorStop(1, '#ff9ec7');
      ctx.fillStyle = bg;
      ctx.fillRect(0, 0, W, H);

      // stars
      ctx.fillStyle = 'rgba(255,255,255,0.7)';
      for (let i = 0; i < 30; i++) {
        const sx = (i * 47) % W;
        const sy = (i * 31) % (H - 100);
        const tw = Math.sin(t / 400 + i) * 0.5 + 0.5;
        ctx.globalAlpha = 0.3 + tw * 0.6;
        ctx.fillRect(sx, sy, 2, 2);
      }
      ctx.globalAlpha = 1;

      // ground platform
      ctx.fillStyle = 'rgba(255,255,255,0.15)';
      ctx.fillRect(0, H - 10, W, 10);
      ctx.fillStyle = 'rgba(255,255,255,0.3)';
      for (let gx = 0; gx < W; gx += 8) ctx.fillRect(gx, H - 10, 4, 1);

      // items
      s.items.forEach(it => {
        ctx.save();
        ctx.translate(it.x + it.size / 2, it.y + it.size / 2);
        ctx.rotate(it.rot);
        if (it.type === 'sparkle') {
          const col = it.hue === 'rare' ? '#fff5a8' : '#ff9ec7';
          drawSparkle(ctx, it.size, col);
        } else {
          drawCoffee(ctx, it.size);
        }
        ctx.restore();
      });

      // player — real chibi sprites (2-frame cycle: idle + walk-a)
      const sp = spritesRef.current;
      const moving = Math.abs(s.player.vx) > 1;
      let spriteImg = null;
      if (sp.loaded) {
        if (moving) {
          spriteImg = s.player.frame === 0 ? sp.idle : sp.walkA;
        } else {
          spriteImg = sp.idle;
        }
      }
      if (spriteImg) {
        ctx.save();
        ctx.imageSmoothingEnabled = false;
        // All sprites are uniform 239×330 bottom-anchored — draw at fixed box
        const aspect = spriteImg.width / spriteImg.height;
        const drawH = PH;
        const drawW = drawH * aspect;
        const drawX = s.player.x + (PW - drawW) / 2;
        const facing = s.player.facing || 1;
        if (facing < 0) {
          ctx.translate(drawX + drawW, s.player.y);
          ctx.scale(-1, 1);
          ctx.drawImage(spriteImg, 0, 0, drawW, drawH);
        } else {
          ctx.drawImage(spriteImg, drawX, s.player.y, drawW, drawH);
        }
        ctx.restore();
      }

      // player shadow
      ctx.fillStyle = 'rgba(0,0,0,0.25)';
      ctx.beginPath();
      ctx.ellipse(s.player.x + PW / 2, s.player.y + PH - 2, PW * 0.35, 4, 0, 0, Math.PI * 2);
      ctx.fill();

      // particles
      s.particles.forEach(p => {
        ctx.globalAlpha = Math.max(0, p.life / 0.8);
        ctx.fillStyle = p.color;
        ctx.fillRect(p.x - 2, p.y - 2, 4, 4);
      });
      ctx.globalAlpha = 1;

      raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [status, accent, PW, PH, score]);

  // Countdown timer
  React.useEffect(() => {
    if (status !== 'playing') return;
    const id = setInterval(() => {
      setTimeLeft(t => {
        if (t <= 1) {
          clearInterval(id);
          return 0;
        }
        return t - 1;
      });
    }, 1000);
    return () => clearInterval(id);
  }, [status]);

  // Game over check
  React.useEffect(() => {
    if (status === 'playing' && (lives <= 0 || timeLeft <= 0)) {
      setStatus('over');
      setHi(prev => {
        const n = Math.max(prev, score);
        try { localStorage.setItem('paulyn-sparkle-hi', String(n)); } catch (e) {}
        return n;
      });
    }
  }, [lives, timeLeft, status, score]);

  return (
    <div style={{ width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, userSelect: 'none', WebkitUserSelect: 'none' }}>
      <div style={{ position: 'relative', width: W, maxWidth: '100%', borderRadius: 16, overflow: 'hidden', border: '2px solid rgba(255,255,255,0.2)', boxShadow: '0 20px 60px rgba(255,158,199,0.25)', touchAction: 'manipulation' }}>
        <canvas ref={canvasRef} width={W} height={H} style={{ display: 'block', width: '100%', imageRendering: 'pixelated', background: '#2a1040' }} />
        {/* HUD */}
        <div style={{ position: 'absolute', top: 10, left: 14, right: 14, display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontFamily: '"JetBrains Mono", monospace', fontSize: 13, color: '#fff', textShadow: '0 1px 4px rgba(0,0,0,0.6)', pointerEvents: 'none' }}>
          <div style={{ display: 'flex', gap: 16 }}>
            <span>✦ {score}</span>
            <span>{'♡'.repeat(Math.max(0, lives))}{'·'.repeat(Math.max(0, 3 - lives))}</span>
          </div>
          <div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
            <span style={{ color: timeLeft <= 10 ? '#ff6b9d' : '#fff', fontWeight: timeLeft <= 10 ? 700 : 400 }}>◷ {String(Math.floor(timeLeft / 60)).padStart(1,'0')}:{String(timeLeft % 60).padStart(2,'0')}</span>
            <span style={{ opacity: 0.7 }}>HI {hi}</span>
          </div>
        </div>
        {/* overlays */}
        {status === 'idle' && (
          <Overlay>
            <div style={{ fontFamily: '"Fraunces", serif', fontSize: 36, fontStyle: 'italic', background: chromeGradient || accent, WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', letterSpacing: -1, marginBottom: 8 }}>sparkle hunt ♡</div>
            <div style={{ fontSize: 13, color: 'rgba(255,255,255,0.8)', lineHeight: 1.6, maxWidth: 360, textAlign: 'center', marginBottom: 20, fontFamily: '"JetBrains Mono", monospace' }}>
              catch ✦ sparkles · dodge ☕ coffee spills<br/>
              gold ✦ = 5 points · 3 lives · 60 seconds
            </div>
            <button onClick={start} style={btnStyle(accent)}>press space or tap → play</button>
          </Overlay>
        )}
        {status === 'over' && (
          <Overlay>
            <div style={{ fontFamily: '"Fraunces", serif', fontSize: 42, fontStyle: 'italic', color: '#fff', marginBottom: 4 }}>
              {timeLeft <= 0 ? "time's up!" : 'game over'}
            </div>
            <div style={{ fontSize: 14, color: 'rgba(255,255,255,0.8)', marginBottom: 4, fontFamily: '"JetBrains Mono", monospace' }}>you caught ✦ <strong style={{ color: accent }}>{score}</strong> sparkles</div>
            {score >= hi && score > 0 && (
              <div style={{ fontSize: 12, color: '#fff5a8', marginBottom: 16, fontFamily: '"JetBrains Mono", monospace' }}>✦ new high score ✦</div>
            )}
            <button onClick={start} style={{ ...btnStyle(accent), marginTop: 12 }}>play again ↻</button>
          </Overlay>
        )}
      </div>
      {/* Touch controls */}
      <div style={{ display: 'flex', gap: 8, justifyContent: 'center' }}>
        <TouchBtn onDown={() => press('ArrowLeft', true)} onUp={() => press('ArrowLeft', false)}>◀ left</TouchBtn>
        <TouchBtn onDown={() => press('ArrowRight', true)} onUp={() => press('ArrowRight', false)}>right ▶</TouchBtn>
      </div>
    </div>
  );
}

function Overlay({ children }) {
  return (
    <div style={{ position: 'absolute', inset: 0, background: 'rgba(20,5,40,0.75)', backdropFilter: 'blur(8px)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: 20 }}>
      {children}
    </div>
  );
}

function btnStyle(accent) {
  return {
    padding: '12px 24px',
    borderRadius: 24,
    border: 'none',
    background: `linear-gradient(135deg, ${accent}, #a99eff)`,
    color: '#1a0f2e',
    fontFamily: '"JetBrains Mono", monospace',
    fontSize: 13,
    fontWeight: 600,
    letterSpacing: 0.5,
    cursor: 'pointer',
    boxShadow: '0 8px 24px rgba(255,158,199,0.4)',
  };
}

function TouchBtn({ children, onDown, onUp }) {
  return (
    <button
      onMouseDown={onDown} onMouseUp={onUp} onMouseLeave={onUp}
      onTouchStart={(e) => { e.preventDefault(); onDown(); }}
      onTouchEnd={(e) => { e.preventDefault(); onUp(); }}
      style={{
        padding: '10px 20px', borderRadius: 20, border: '1px solid var(--hairline)',
        background: 'var(--surface)', color: 'var(--fg)', fontFamily: '"JetBrains Mono", monospace',
        fontSize: 12, fontWeight: 500, cursor: 'pointer',
        userSelect: 'none', WebkitUserSelect: 'none', touchAction: 'manipulation',
        WebkitTouchCallout: 'none', WebkitTapHighlightColor: 'transparent',
      }}
    >{children}</button>
  );
}

// 4-pointed star sparkle
function drawSparkle(ctx, size, color) {
  const s = size / 2;
  ctx.fillStyle = color;
  ctx.beginPath();
  ctx.moveTo(0, -s);
  ctx.lineTo(s * 0.28, -s * 0.28);
  ctx.lineTo(s, 0);
  ctx.lineTo(s * 0.28, s * 0.28);
  ctx.lineTo(0, s);
  ctx.lineTo(-s * 0.28, s * 0.28);
  ctx.lineTo(-s, 0);
  ctx.lineTo(-s * 0.28, -s * 0.28);
  ctx.closePath();
  ctx.fill();
  // core glow
  ctx.fillStyle = 'rgba(255,255,255,0.8)';
  ctx.fillRect(-2, -2, 4, 4);
}

// Coffee cup hazard
function drawCoffee(ctx, size) {
  const s = size;
  // cup body
  ctx.fillStyle = '#f5e8d8';
  ctx.fillRect(-s / 2, -s / 3, s, s * 0.7);
  // coffee
  ctx.fillStyle = '#6b4226';
  ctx.fillRect(-s / 2 + 2, -s / 3 + 2, s - 4, 5);
  // handle
  ctx.strokeStyle = '#f5e8d8';
  ctx.lineWidth = 3;
  ctx.beginPath();
  ctx.arc(s / 2 + 2, 0, s / 4, -Math.PI / 2, Math.PI / 2);
  ctx.stroke();
  // steam
  ctx.fillStyle = 'rgba(255,255,255,0.6)';
  ctx.fillRect(-3, -s / 2 - 4, 2, 4);
  ctx.fillRect(2, -s / 2 - 6, 2, 4);
}

Object.assign(window, { PixelAvatar, SparkleHunt });
