import { useEffect, useMemo, useRef, useState, useCallback } from 'react';
import { View, Text, Pressable, TouchableOpacity, TouchableWithoutFeedback, Dimensions, PanResponder, Platform } from 'react-native';
import Svg, { Defs, Pattern, Path, Rect, Polyline, Circle, Line } from 'react-native-svg';
import { SvgXml } from 'react-native-svg';
import { Ionicons } from '@expo/vector-icons';
import { useTranslation } from 'react-i18next';
import * as Haptics from 'expo-haptics';
import Slider from '@react-native-community/slider';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { memorySvg, snakeSvg, tetrisSvg, tictactoeSvg } from './gameSvgs';
import { useColors } from '../../lib/theme';
import { GameOverScreen } from '../games/GameOverScreen';
import { ScoreProgressBar } from '../games/ScoreProgressBar';
import { getBestScore, saveBestScore } from '../../lib/gameScores';
import { useSnakeSounds } from '../../hooks/useSnakeSounds';

// Haptic helper — fire-and-forget, swallow errors on platforms without taptic engine
function tapHaptic() {
  Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {});
}
function mediumHaptic() {
  Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium).catch(() => {});
}

export type GameType = 'memory' | 'tictactoe' | 'snake' | 'tetris';

export const GAME_META: Array<{ id: GameType; svg: string; titleKey: string; descKey: string }> = [
  { id: 'memory', svg: memorySvg, titleKey: 'urge.game_memory', descKey: 'urge.game_memory_desc' },
  { id: 'tictactoe', svg: tictactoeSvg, titleKey: 'urge.game_tictactoe', descKey: 'urge.game_tictactoe_desc' },
  { id: 'snake', svg: snakeSvg, titleKey: 'urge.game_snake', descKey: 'urge.game_snake_desc' },
  { id: 'tetris', svg: tetrisSvg, titleKey: 'urge.game_tetris', descKey: 'urge.game_tetris_desc' },
];

// ── Game picker grid ──────────────────────────────────────────────────────────

export function GamePickerGrid({ onSelect }: { onSelect: (game: GameType) => void }) {
  const { t } = useTranslation();
  return (
    <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 10 }}>
      {GAME_META.map((game) => (
        <TouchableOpacity
          key={game.id}
          onPress={() => onSelect(game.id)}
          activeOpacity={0.75}
          style={{ width: '47%' }}
        >
          <View style={{
            aspectRatio: 1,
            borderRadius: 16,
            borderWidth: 1,
            borderColor: '#e5e7eb',
            backgroundColor: '#f9fafb',
            alignItems: 'center',
            justifyContent: 'center',
            padding: 12,
          }}>
          <SvgXml xml={game.svg} width={56} height={56} />
          <Text style={{ marginTop: 10, fontFamily: 'Nunito_700Bold', color: '#111827', fontSize: 14 }}>
            {t(game.titleKey)}
          </Text>
          <Text
            numberOfLines={2}
            style={{
              marginTop: 4,
              textAlign: 'center',
              fontFamily: 'Nunito_400Regular',
              color: '#6b7280',
              fontSize: 11,
              lineHeight: 14,
              minHeight: 28,
            }}
          >
            {t(game.descKey)}
          </Text>
          </View>
        </TouchableOpacity>
      ))}
    </View>
  );
}

// ── Helpers ───────────────────────────────────────────────────────────────────

function shuffle<T>(arr: T[]): T[] {
  const out = [...arr];
  for (let i = out.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    const tmp = out[i]!;
    out[i] = out[j]!;
    out[j] = tmp;
  }
  return out;
}

// ═══════════════════════════════════════════════════════════════════════════════
// SNAKE — 1:1 Port von apps/rebreak/app/components/sos/GameSnake.vue
// ═══════════════════════════════════════════════════════════════════════════════

const SNAKE_ROWS = 20;
const SNAKE_COLS = 15;
const SNAKE_TICK_MS = 180;
type Dir = 'up' | 'down' | 'left' | 'right';
interface Pos { row: number; col: number }
const OPPOSITES: Record<Dir, Dir> = { up: 'down', down: 'up', left: 'right', right: 'left' };

export function SnakeGame({
  onComplete,
  onAbandon,
  mode = 'standalone',
}: {
  onComplete: (score: number) => void;
  onAbandon: () => void;
  /** 'sos' = no GameOverScreen, fire onComplete(score) immediately when game ends.
   *  'standalone' = render GameOverScreen with retry/exit/share. */
  mode?: 'sos' | 'standalone';
}) {
  const insets = useSafeAreaInsets();
  // Cell size aus Bildschirmgröße — Header(80) + Drawer-Padding(40) + DPad(220) + Spacing(40) + Home-Indicator
  const cell = useMemo(() => {
    const win = Dimensions.get('window');
    const maxW = Math.min(win.width - 32, 400);
    const maxH = win.height - 80 - 40 - 220 - 40 - Math.max(insets.bottom, 16);
    const cellByW = Math.floor(maxW / SNAKE_COLS);
    const cellByH = Math.floor(maxH / SNAKE_ROWS);
    return Math.max(12, Math.min(cellByW, cellByH));
  }, [insets.bottom]);
  const boardW = SNAKE_COLS * cell;
  const boardH = SNAKE_ROWS * cell;

  const [snake, setSnake] = useState<Pos[]>([
    { row: 10, col: 7 }, { row: 10, col: 6 }, { row: 10, col: 5 },
  ]);
  const [food, setFood] = useState<Pos>({ row: 3, col: 10 });
  const snakeRef = useRef<Pos[]>(snake);
  const foodRef = useRef<Pos>(food);
  useEffect(() => { snakeRef.current = snake; }, [snake]);
  useEffect(() => { foodRef.current = food; }, [food]);
  const dirRef = useRef<Dir>('right');
  const nextDirRef = useRef<Dir>('right');
  const [score, setScore] = useState(0);
  const [highScore, setHighScore] = useState(0);
  const [gameOver, setGameOver] = useState(false);
  const [isNewBest, setIsNewBest] = useState(false);
  const [activeDPad, setActiveDPad] = useState<Dir>('right');
  const [elapsed, setElapsed] = useState(0);
  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
  const sounds = useSnakeSounds(true);
  const newRecordFiredRef = useRef(false);

  useEffect(() => {
    if (gameOver) {
      if (timerRef.current) clearInterval(timerRef.current);
      return;
    }
    timerRef.current = setInterval(() => setElapsed((s) => s + 1), 1000);
    return () => {
      if (timerRef.current) clearInterval(timerRef.current);
    };
  }, [gameOver]);

  useEffect(() => {
    if (gameOver && mode === 'sos') {
      onComplete(score);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [gameOver, mode]);

  useEffect(() => {
    getBestScore('snake').then(setHighScore);
  }, []);

  function setDir(d: Dir) {
    if (OPPOSITES[d] !== dirRef.current) nextDirRef.current = d;
  }
  function onDPad(d: Dir) {
    setDir(d);
    setActiveDPad(d);
  }

  function randomFood(currentSnake: Pos[]): Pos {
    const occupied = new Set(currentSnake.map((p) => p.row * SNAKE_COLS + p.col));
    let pos: Pos;
    do {
      pos = { row: Math.floor(Math.random() * SNAKE_ROWS), col: Math.floor(Math.random() * SNAKE_COLS) };
    } while (occupied.has(pos.row * SNAKE_COLS + pos.col));
    return pos;
  }

  function endGame(finalScore: number) {
    if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; }
    setGameOver(true);
    if (finalScore > highScore) {
      setIsNewBest(true);
      setHighScore(finalScore);
      saveBestScore('snake', finalScore).catch(() => {});
      sounds.playNewRecord();
    } else {
      sounds.playGameOver();
    }
  }

  function resetSnake() {
    if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; }
    setSnake([{ row: 10, col: 7 }, { row: 10, col: 6 }, { row: 10, col: 5 }]);
    snakeRef.current = [{ row: 10, col: 7 }, { row: 10, col: 6 }, { row: 10, col: 5 }];
    setFood({ row: 3, col: 10 });
    foodRef.current = { row: 3, col: 10 };
    dirRef.current = 'right';
    nextDirRef.current = 'right';
    setScore(0);
    setGameOver(false);
    setIsNewBest(false);
    setActiveDPad('right');
    setElapsed(0);
    newRecordFiredRef.current = false;
  }

  // Game tick loop — single setInterval, side-effects driven via refs (NOT inside reducers).
  // Reading snake/food from refs avoids stale closures and prevents duplicate setFood calls
  // when React (StrictMode) invokes a state-updater twice.
  useEffect(() => {
    if (gameOver) return;
    intervalRef.current = setInterval(() => {
      dirRef.current = nextDirRef.current;
      const prev = snakeRef.current;
      const head = prev[0];
      if (!head) return;
      const next: Pos = { row: head.row, col: head.col };
      if (dirRef.current === 'up') next.row--;
      else if (dirRef.current === 'down') next.row++;
      else if (dirRef.current === 'left') next.col--;
      else if (dirRef.current === 'right') next.col++;
      if (next.row < 0 || next.row >= SNAKE_ROWS || next.col < 0 || next.col >= SNAKE_COLS) {
        endGame(score);
        return;
      }
      if (prev.some((s) => s.row === next.row && s.col === next.col)) {
        endGame(score);
        return;
      }
      const currentFood = foodRef.current;
      const ate = next.row === currentFood.row && next.col === currentFood.col;
      const newSnake = [next, ...prev];
      if (!ate) newSnake.pop();
      snakeRef.current = newSnake;
      setSnake(newSnake);
      if (ate) {
        const newFood = randomFood(newSnake);
        foodRef.current = newFood;
        setFood(newFood);
        setScore((s) => {
          const next = s + 1;
          // Record-Pulse genau im Moment des Überschreitens (einmal pro Run)
          if (highScore > 0 && next > highScore && !newRecordFiredRef.current) {
            newRecordFiredRef.current = true;
            setIsNewBest(true);
            sounds.playNewRecord();
          } else {
            sounds.playEat();
          }
          return next;
        });
      }
    }, SNAKE_TICK_MS);
    return () => {
      if (intervalRef.current) clearInterval(intervalRef.current);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [gameOver, score, highScore]);

  // Swipe gestures
  const panResponder = useMemo(
    () =>
      PanResponder.create({
        onStartShouldSetPanResponder: () => true,
        onMoveShouldSetPanResponder: () => true,
        onPanResponderRelease: (_, g) => {
          const dx = g.dx, dy = g.dy;
          if (Math.abs(dx) < 10 && Math.abs(dy) < 10) return;
          if (Math.abs(dx) > Math.abs(dy)) onDPad(dx > 0 ? 'right' : 'left');
          else onDPad(dy > 0 ? 'down' : 'up');
        },
      }),
    [],
  );

  // Lyra message based on score
  const lyraMessage =
    score >= 8 ? 'Voll im Flow – der Impuls hat keine Chance!' :
    score >= 5 ? 'Sehr gut! Bleib konzentriert.' :
    score >= 3 ? 'Super! Weiter so.' :
    'Sammle die roten Äpfel.';

  // SVG geometry
  const snakePoints = snake
    .map((s) => `${s.col * cell + cell / 2},${s.row * cell + cell / 2}`)
    .join(' ');

  const head = snake[0];
  const eyePositions = head
    ? (() => {
        const cx = head.col * cell + cell / 2;
        const cy = head.row * cell + cell / 2;
        const off = cell * 0.22;
        switch (dirRef.current) {
          case 'right': return [{ x: cx + off * 0.8, y: cy - off }, { x: cx + off * 0.8, y: cy + off }];
          case 'left':  return [{ x: cx - off * 0.8, y: cy - off }, { x: cx - off * 0.8, y: cy + off }];
          case 'up':    return [{ x: cx - off, y: cy - off * 0.8 }, { x: cx + off, y: cy - off * 0.8 }];
          case 'down':  return [{ x: cx - off, y: cy + off * 0.8 }, { x: cx + off, y: cy + off * 0.8 }];
        }
      })()
    : [];
  const pupilPositions = eyePositions.map((e) => {
    const d = cell * 0.04;
    switch (dirRef.current) {
      case 'right': return { x: e.x + d, y: e.y };
      case 'left':  return { x: e.x - d, y: e.y };
      case 'up':    return { x: e.x, y: e.y - d };
      case 'down':  return { x: e.x, y: e.y + d };
    }
  });
  const tongue = head
    ? (() => {
        const cx = head.col * cell + cell / 2;
        const cy = head.row * cell + cell / 2;
        const len = cell * 0.45;
        const base = cell * 0.4;
        switch (dirRef.current) {
          case 'right': return { x1: cx + base, y1: cy, x2: cx + base + len, y2: cy };
          case 'left':  return { x1: cx - base, y1: cy, x2: cx - base - len, y2: cy };
          case 'up':    return { x1: cx, y1: cy - base, x2: cx, y2: cy - base - len };
          case 'down':  return { x1: cx, y1: cy + base, x2: cx, y2: cy + base + len };
        }
      })()
    : null;

  return (
    <View style={{ paddingHorizontal: 16, paddingTop: 4, paddingBottom: Math.max(insets.bottom, 16), position: 'relative' }}>
      {!gameOver && (
        <>
          <DigitalScore
            score={score}
            best={highScore}
            extra={`${String(Math.floor(elapsed / 60)).padStart(2, '0')}:${String(elapsed % 60).padStart(2, '0')}`}
            extraLabel="TIME"
            boardWidth={boardW}
          />
          <ScoreProgressBar
            score={score}
            best={highScore}
            isNewBest={isNewBest}
            boardWidth={boardW}
          />
        </>
      )}

      {/* Board */}
      <View style={{ alignItems: 'center' }} {...panResponder.panHandlers}>
        <View style={{ width: boardW, height: boardH, borderRadius: 12, overflow: 'hidden', borderWidth: 1, borderColor: 'rgba(0,230,128,0.2)', backgroundColor: '#0d1117' }}>
          <Svg width={boardW} height={boardH}>
            <Defs>
              <Pattern id="sg" width={cell} height={cell} patternUnits="userSpaceOnUse">
                <Path d={`M ${cell} 0 L 0 0 0 ${cell}`} fill="none" stroke="#00e680" strokeWidth={0.5} opacity={0.06} />
              </Pattern>
            </Defs>
            <Rect width="100%" height="100%" fill="url(#sg)" />
            {snake.length >= 2 && (
              <Polyline points={snakePoints} fill="none" stroke="#009954" strokeWidth={cell * 0.68} strokeLinecap="round" strokeLinejoin="round" />
            )}
            {head && <Circle cx={head.col * cell + cell / 2} cy={head.row * cell + cell / 2} r={cell * 0.42} fill="#00e680" />}
            {eyePositions.map((eye, ei) => (
              <Circle key={'e' + ei} cx={eye.x} cy={eye.y} r={cell * 0.09} fill="white" />
            ))}
            {pupilPositions.map((p, pi) => (
              <Circle key={'p' + pi} cx={p.x} cy={p.y} r={cell * 0.045} fill="#0d1117" />
            ))}
            {tongue && !gameOver && (
              <Line x1={tongue.x1} y1={tongue.y1} x2={tongue.x2} y2={tongue.y2} stroke="#d32f2f" strokeWidth={1.5} strokeLinecap="round" />
            )}
            <Circle cx={food.col * cell + cell / 2} cy={food.row * cell + cell / 2} r={cell * 0.33} fill="#d32f2f" />
          </Svg>
        </View>
      </View>

      {/* D-Pad */}
      {!gameOver && (
        <View style={{ alignItems: 'center', marginTop: 18, gap: 10 }}>
          <DPadBtn dir="up" active={activeDPad === 'up'} onPress={() => onDPad('up')} />
          <View style={{ flexDirection: 'row', gap: 14, alignItems: 'center' }}>
            <DPadBtn dir="left" active={activeDPad === 'left'} onPress={() => onDPad('left')} />
            <View style={{ width: 48, height: 48, borderRadius: 24, backgroundColor: 'transparent', alignItems: 'center', justifyContent: 'center' }}>
              <View style={{ width: 8, height: 8, borderRadius: 4, backgroundColor: '#d1d5db' }} />
            </View>
            <DPadBtn dir="right" active={activeDPad === 'right'} onPress={() => onDPad('right')} />
          </View>
          <DPadBtn dir="down" active={activeDPad === 'down'} onPress={() => onDPad('down')} />
        </View>
      )}

      {gameOver && mode === 'standalone' && (
        <GameOverScreen
          score={score}
          bestScore={highScore}
          gameName="Snake"
          scoreLabel="Äpfel"
          goodScore={10}
          isNewBest={isNewBest}
          onRetry={resetSnake}
          onExit={() => onAbandon()}
        />
      )}
    </View>
  );
}

function DPadBtn({ dir, active, onPress }: { dir: Dir; active: boolean; onPress: () => void }) {
  const icons: Record<Dir, 'chevron-up' | 'chevron-down' | 'chevron-back' | 'chevron-forward'> = {
    up: 'chevron-up', down: 'chevron-down', left: 'chevron-back', right: 'chevron-forward',
  };
  const tint = '#007aff';
  // Hard rule (siehe docs/internal/RECOVERY_LOG_2026-05-10.md §7.2):
  // KEINE Pressable mit style-Funktion {({pressed}) => ...} — RN-Quirk schluckt
  // Background-Properties manchmal. Stattdessen: TouchableWithoutFeedback + View
  // mit static style. Visual-Active-State über `active`-Prop (nicht press-state).
  return (
    <TouchableWithoutFeedback
      onPress={() => { tapHaptic(); onPress(); }}
      hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
    >
      <View
        style={{
          width: 48, height: 48, borderRadius: 24,
          backgroundColor: active ? 'rgba(0,122,255,0.32)' : 'rgba(0,122,255,0.20)',
          borderWidth: 1,
          borderColor: active ? tint : 'rgba(0,122,255,0.25)',
          alignItems: 'center', justifyContent: 'center',
        }}
      >
        <Ionicons name={icons[dir]} size={22} color={tint} />
      </View>
    </TouchableWithoutFeedback>
  );
}

// Action button für Tetris (Rotate, Drop) — größer & mit Label.
// Idle: tönt sich leicht in accent-Farbe ein (kein white-on-white-Verlust auf weißem Screen).
function TetrisActionBtn({
  icon, label, onPress,
}: {
  icon: 'sync' | 'arrow-down';
  label: string;
  onPress: () => void;
  accent?: string; // ignored — vereinheitlicht auf iOS-blau
}) {
  // Hard rule (siehe RECOVERY_LOG §7.2): kein Pressable mit style-Funktion.
  const tint = '#007AFF';
  return (
    <TouchableWithoutFeedback
      onPress={() => { mediumHaptic(); onPress(); }}
      hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
    >
      <View
        style={{
          width: 56, height: 56, borderRadius: 16,
          backgroundColor: 'rgba(0,122,255,0.20)',
          borderWidth: 1,
          borderColor: 'rgba(0,122,255,0.25)',
          alignItems: 'center', justifyContent: 'center',
        }}
      >
        <Ionicons name={icon} size={22} color={tint} />
        <Text style={{ fontSize: 9, marginTop: 1, fontFamily: 'Nunito_700Bold', color: tint }}>{label}</Text>
      </View>
    </TouchableWithoutFeedback>
  );
}

// ═══════════════════════════════════════════════════════════════════════════════
// MEMORY — 1:1 Port von apps/rebreak/app/components/sos/GameMemory.vue
// ═══════════════════════════════════════════════════════════════════════════════

const MEMORY_PAIRS = 8;
const MEMORY_EMOJIS = ['🛡️', '💪', '🌟', '🧠', '🌊', '🎯', '🌱', '🔑'];

export function MemoryGame({
  onComplete,
  onAbandon,
  mode = 'standalone',
}: {
  onComplete: (score: number) => void;
  onAbandon: () => void;
  mode?: 'sos' | 'standalone';
}) {
  type Card = { id: number; emoji: string; matched: boolean; revealed: boolean };
  const [cards, setCards] = useState<Card[]>([]);
  const [flipped, setFlipped] = useState<number[]>([]);
  const [moveCount, setMoveCount] = useState(0);
  const [matchedCount, setMatchedCount] = useState(0);
  const [blocked, setBlocked] = useState(false);
  const [showGameOver, setShowGameOver] = useState(false);
  const [bestMoves, setBestMoves] = useState(0);
  const [isNewBestMemory, setIsNewBestMemory] = useState(false);

  useEffect(() => { getBestScore('memory').then(setBestMoves); }, []);

  // SOS-Mode: kein GameOverScreen, sofort onComplete(score) feuern
  useEffect(() => {
    if (showGameOver && mode === 'sos') {
      onComplete(moveCount);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [showGameOver, mode]);

  function init() {
    const pairs = shuffle([...MEMORY_EMOJIS, ...MEMORY_EMOJIS]);
    setCards(pairs.map((emoji, id) => ({ id, emoji, matched: false, revealed: false })));
    setFlipped([]);
    setMoveCount(0);
    setMatchedCount(0);
    setBlocked(false);
    setShowGameOver(false);
    setIsNewBestMemory(false);
  }
  useEffect(() => { init(); }, []);

  const lyraMessage = (() => {
    const r = matchedCount / MEMORY_PAIRS;
    if (r === 0) return 'Dreh die erste Karte um – nimm dir Zeit.';
    if (r < 0.25) return 'Gut. Merk dir die Positionen genau.';
    if (r < 0.5) return 'Du machst das super! Konzentrier dich weiter.';
    if (r < 0.75) return 'Mehr als die Hälfte! Du bist fast da.';
    return 'Wow – nur noch wenige Paare! 🔥';
  })();

  function flip(id: number) {
    if (blocked) return;
    const card = cards[id];
    if (!card || card.matched || card.revealed) return;
    if (flipped.length >= 2) return;
    const next = cards.slice();
    next[id] = { ...next[id]!, revealed: true };
    setCards(next);
    const nextFlipped = [...flipped, id];
    setFlipped(nextFlipped);
    if (nextFlipped.length === 2) {
      const newMoveCount = moveCount + 1;
      setMoveCount(newMoveCount);
      const [a, b] = nextFlipped;
      const ca = next[a]!, cb = next[b]!;
      if (ca.emoji === cb.emoji) {
        const matched = next.slice();
        matched[a] = { ...ca, matched: true };
        matched[b] = { ...cb, matched: true };
        setCards(matched);
        setFlipped([]);
        const newMatched = matchedCount + 1;
        setMatchedCount(newMatched);
        if (newMatched === MEMORY_PAIRS) {
          const newBest = bestMoves === 0 || newMoveCount < bestMoves;
          if (newBest) {
            setIsNewBestMemory(true);
            setBestMoves(newMoveCount);
            saveBestScore('memory', newMoveCount).catch(() => {});
          }
          setTimeout(() => setShowGameOver(true), 600);
        }
      } else {
        setBlocked(true);
        setTimeout(() => {
          const reverted = next.slice();
          reverted[a] = { ...reverted[a]!, revealed: false };
          reverted[b] = { ...reverted[b]!, revealed: false };
          setCards(reverted);
          setFlipped([]);
          setBlocked(false);
        }, 900);
      }
    }
  }

  return (
    <View style={{ paddingHorizontal: 12, position: 'relative' }}>
      {/* Lyra Header */}
      <View style={{ flexDirection: 'row', alignItems: 'center', backgroundColor: '#f9fafb', borderWidth: 1, borderColor: '#e5e7eb', borderRadius: 16, paddingHorizontal: 14, paddingVertical: 10, gap: 12 }}>
        <View style={{ flex: 1 }}>
          <Text style={{ fontSize: 11, color: '#9ca3af', fontFamily: 'Nunito_600SemiBold' }}>Lyra</Text>
          <Text style={{ fontSize: 13, color: '#111827', lineHeight: 18 }}>{lyraMessage}</Text>
        </View>
        <View style={{ alignItems: 'flex-end' }}>
          <Text style={{ fontSize: 10, color: '#9ca3af' }}>Züge</Text>
          <Text style={{ fontSize: 18, fontFamily: 'Nunito_800ExtraBold', color: '#111827' }}>{moveCount}</Text>
        </View>
        <Pressable onPress={onAbandon} hitSlop={10}>
          <Text style={{ fontSize: 18, color: '#6b7280' }}>✕</Text>
        </Pressable>
      </View>

      {/* Progress */}
      <View style={{ height: 6, backgroundColor: '#e5e7eb', borderRadius: 999, overflow: 'hidden', marginTop: 12 }}>
        <View style={{ height: '100%', width: `${(matchedCount / MEMORY_PAIRS) * 100}%`, backgroundColor: '#16a34a', borderRadius: 999 }} />
      </View>

      {/* Card Grid 4x4 */}
      <View style={{ flexDirection: 'row', flexWrap: 'wrap', marginTop: 12, gap: 8 }}>
        {cards.map((card) => {
          const showFace = card.revealed || card.matched;
          return (
            <Pressable
              key={card.id}
              onPress={() => flip(card.id)}
              style={{
                width: '22.7%', aspectRatio: 1, borderRadius: 12, borderWidth: 1.5,
                borderColor: card.matched ? '#86efac' : showFace ? '#93c5fd' : '#e5e7eb',
                backgroundColor: card.matched ? '#f0fdf4' : showFace ? '#eff6ff' : '#f9fafb',
                alignItems: 'center', justifyContent: 'center',
                opacity: blocked && !showFace ? 0.6 : 1,
                transform: [{ scale: card.matched ? 0.95 : 1 }],
              }}
            >
              <Text style={{ fontSize: 28 }}>{showFace ? card.emoji : '🛡️'}</Text>
            </Pressable>
          );
        })}
      </View>
      {showGameOver && mode === 'standalone' && (
        <GameOverScreen
          score={moveCount}
          bestScore={bestMoves}
          gameName="Memory"
          scoreLabel="Züge"
          goodScore={30}
          isNewBest={isNewBestMemory}
          onRetry={init}
          onExit={() => onAbandon()}
        />
      )}
    </View>
  );
}

// ═══════════════════════════════════════════════════════════════════════════════
// TIC-TAC-TOE — 1:1 Port von apps/rebreak/app/components/sos/GameTicTacToe.vue
// ═══════════════════════════════════════════════════════════════════════════════

type TTCell = 'X' | 'O' | null;
type TTResult = 'player' | 'lyra' | 'draw' | null;
const TT_WIN_LINES = [
  [0, 1, 2], [3, 4, 5], [6, 7, 8],
  [0, 3, 6], [1, 4, 7], [2, 5, 8],
  [0, 4, 8], [2, 4, 6],
];

function ttCheckWinner(b: TTCell[]): { winner: 'X' | 'O' | null; line: number[] } {
  for (const line of TT_WIN_LINES) {
    const [a, c, d] = line as [number, number, number];
    if (b[a] && b[a] === b[c] && b[a] === b[d]) return { winner: b[a]!, line };
  }
  return { winner: null, line: [] };
}
function ttEmpty(b: TTCell[]) { return b.map((c, i) => c === null ? i : -1).filter((i) => i !== -1); }
function ttWouldWin(b: TTCell[], idx: number, mark: 'X' | 'O') {
  const next = [...b]; next[idx] = mark;
  return ttCheckWinner(next).winner === mark;
}
function ttLyraAI(b: TTCell[]): number {
  const empty = ttEmpty(b);
  for (const i of empty) if (ttWouldWin(b, i, 'O')) return i; // win
  for (const i of empty) if (ttWouldWin(b, i, 'X')) return i; // block
  if (b[4] === null) return 4; // center
  const corners = [0, 2, 6, 8].filter((i) => b[i] === null);
  if (corners.length) return corners[Math.floor(Math.random() * corners.length)]!;
  return empty[Math.floor(Math.random() * empty.length)]!;
}

export function TicTacToeGame({
  onComplete,
  onAbandon,
  mode = 'standalone',
}: {
  onComplete: (score: number) => void;
  onAbandon: () => void;
  mode?: 'sos' | 'standalone';
}) {
  const [board, setBoard] = useState<TTCell[]>(Array(9).fill(null));
  const [gameOver, setGameOver] = useState(false);
  const [result, setResult] = useState<TTResult>(null);
  const [winLine, setWinLine] = useState<number[]>([]);
  const [lyraThinking, setLyraThinking] = useState(false);
  const [playerScore, setPlayerScore] = useState(0);
  const [lyraScore, setLyraScore] = useState(0);
  const [round, setRound] = useState(1);

  const resultText =
    result === 'player' ? '🎉 Du gewinnst diese Runde!' :
    result === 'lyra' ? '🤖 Lyra gewinnt diese Runde' :
    '🤝 Unentschieden';
  const resultColor = result === 'player' ? '#16a34a' : result === 'lyra' ? '#f43f5e' : '#eab308';

  const lyraMessage = (() => {
    if (lyraThinking) return 'Hmm, lass mich kurz nachdenken…';
    if (result === 'player') return 'Gut gespielt! Du hast diese Runde gewonnen. 👏';
    if (result === 'lyra') return 'Diesmal war ich schneller. Versuch es nochmal!';
    if (result === 'draw') return 'Unentschieden! Gut gekämpft.';
    const empty = board.filter((c) => c === null).length;
    if (empty === 9) return 'Du fängst an – ich bin Lyra, du spielst X.';
    if (empty <= 3) return 'Das wird spannend – noch wenige Felder frei!';
    return 'Dein Zug. Ich beobachte genau.';
  })();

  function applyResult(mark: 'X' | 'O', line: number[]) {
    setWinLine(line);
    setGameOver(true);
    if (mark === 'X') { setResult('player'); setPlayerScore((s) => s + 1); }
    else { setResult('lyra'); setLyraScore((s) => s + 1); }
  }

  function playerMove(i: number) {
    if (board[i] || gameOver || lyraThinking) return;
    const next = [...board]; next[i] = 'X';
    setBoard(next);
    const r = ttCheckWinner(next);
    if (r.winner) { applyResult('X', r.line); return; }
    if (ttEmpty(next).length === 0) { setGameOver(true); setResult('draw'); return; }

    setLyraThinking(true);
    setTimeout(() => {
      const idx = ttLyraAI(next);
      const after = [...next]; after[idx] = 'O';
      setBoard(after);
      const r2 = ttCheckWinner(after);
      if (r2.winner) applyResult('O', r2.line);
      else if (ttEmpty(after).length === 0) { setGameOver(true); setResult('draw'); }
      setLyraThinking(false);
    }, 600);
  }

  function newRound() {
    setBoard(Array(9).fill(null));
    setGameOver(false);
    setResult(null);
    setWinLine([]);
    setRound((r) => r + 1);
  }

  return (
    <View style={{ paddingHorizontal: 16, gap: 12 }}>
      {/* Lyra Header */}
      <View style={{ flexDirection: 'row', alignItems: 'center', backgroundColor: '#f9fafb', borderWidth: 1, borderColor: '#e5e7eb', borderRadius: 16, paddingHorizontal: 14, paddingVertical: 10, gap: 12 }}>
        <View style={{ flex: 1 }}>
          <Text style={{ fontSize: 11, color: '#9ca3af', fontFamily: 'Nunito_600SemiBold' }}>Lyra</Text>
          <Text style={{ fontSize: 13, color: '#111827', lineHeight: 18 }}>{lyraMessage}</Text>
        </View>
        <View style={{ flexDirection: 'row', gap: 10, alignItems: 'center' }}>
          <View style={{ alignItems: 'center' }}>
            <Text style={{ fontSize: 16, fontFamily: 'Nunito_800ExtraBold', color: '#111827' }}>{playerScore}</Text>
            <Text style={{ fontSize: 10, color: '#9ca3af' }}>Du</Text>
          </View>
          <Text style={{ color: '#9ca3af' }}>:</Text>
          <View style={{ alignItems: 'center' }}>
            <Text style={{ fontSize: 16, fontFamily: 'Nunito_800ExtraBold', color: '#111827' }}>{lyraScore}</Text>
            <Text style={{ fontSize: 10, color: '#9ca3af' }}>Lyra</Text>
          </View>
        </View>
      </View>

      {/* Board */}
      <View style={{ flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', maxWidth: 320, alignSelf: 'center', width: '100%', gap: 8 }}>
        {board.map((cell, i) => {
          const isWin = winLine.includes(i);
          return (
            <Pressable
              key={i}
              onPress={() => playerMove(i)}
              disabled={!!cell || gameOver || lyraThinking}
              style={{
                width: '31%', aspectRatio: 1, borderRadius: 16, borderWidth: 1.5,
                borderColor: isWin ? '#facc15' : cell === 'X' ? '#3b82f6' : cell === 'O' ? '#f43f5e' : '#e5e7eb',
                backgroundColor: isWin ? '#fef9c3' : cell === 'X' ? '#eff6ff' : cell === 'O' ? '#fee2e2' : '#f9fafb',
                alignItems: 'center', justifyContent: 'center',
              }}
            >
              <Text style={{ fontSize: 36, fontFamily: 'Nunito_800ExtraBold', color: cell === 'X' ? '#3b82f6' : cell === 'O' ? '#f43f5e' : '#111827' }}>{cell ?? ''}</Text>
            </Pressable>
          );
        })}
      </View>

      {/* Status */}
      <View style={{ backgroundColor: '#f9fafb', borderWidth: 1, borderColor: '#e5e7eb', borderRadius: 12, paddingHorizontal: 14, paddingVertical: 12, alignItems: 'center' }}>
        {lyraThinking ? (
          <Text style={{ fontSize: 13, color: '#6b7280' }}>Lyra denkt nach…</Text>
        ) : gameOver ? (
          <>
            <Text style={{ fontSize: 15, fontFamily: 'Nunito_700Bold', color: resultColor }}>{resultText}</Text>
            <Text style={{ fontSize: 11, color: '#9ca3af', marginTop: 2 }}>Runde {round}</Text>
          </>
        ) : (
          <Text style={{ fontSize: 13, color: '#6b7280' }}>Dein Zug – du spielst <Text style={{ fontFamily: 'Nunito_800ExtraBold', color: '#3b82f6' }}>X</Text></Text>
        )}
      </View>

      {/* Actions */}
      {gameOver ? (
        <View style={{ flexDirection: 'row', gap: 10 }}>
          <Pressable onPress={newRound} style={{ flex: 1, paddingVertical: 12, borderRadius: 12, backgroundColor: '#f3f4f6', alignItems: 'center' }}>
            <Text style={{ fontFamily: 'Nunito_700Bold', color: '#374151' }}>Nochmal</Text>
          </Pressable>
          <Pressable onPress={() => onComplete(playerScore)} style={{ flex: 1, paddingVertical: 12, borderRadius: 12, backgroundColor: '#16a34a', alignItems: 'center' }}>
            <Text style={{ fontFamily: 'Nunito_700Bold', color: '#fff' }}>Fertig →</Text>
          </Pressable>
        </View>
      ) : (
        <Pressable onPress={onAbandon} style={{ paddingVertical: 10, alignItems: 'center' }}>
          <Text style={{ color: '#6b7280', fontFamily: 'Nunito_600SemiBold' }}>Abbrechen</Text>
        </Pressable>
      )}
    </View>
  );
}

// ═══════════════════════════════════════════════════════════════════════════════
// TETRIS — 1:1 Port von apps/rebreak/app/components/sos/GameTetris.vue
// ═══════════════════════════════════════════════════════════════════════════════

const TETRIS_COLS = 10;
const TETRIS_ROWS = 20;
const TETRIS_SPEED_BASES = [1400, 1000, 700, 450, 250] as const;
const TETRIS_PIECES = [
  { shape: [[1, 1, 1, 1]], color: '#22d3ee' }, // I
  { shape: [[1, 1], [1, 1]], color: '#fbbf24' }, // O
  { shape: [[0, 1, 0], [1, 1, 1]], color: '#a78bfa' }, // T
  { shape: [[0, 1, 1], [1, 1, 0]], color: '#34d399' }, // S
  { shape: [[1, 1, 0], [0, 1, 1]], color: '#f87171' }, // Z
  { shape: [[1, 0, 0], [1, 1, 1]], color: '#60a5fa' }, // J
  { shape: [[0, 0, 1], [1, 1, 1]], color: '#fb923c' }, // L
];
type TetrisPiece = { shape: number[][]; color: string; x: number; y: number };

function tetrisEmptyBoard(): string[][] {
  return Array.from({ length: TETRIS_ROWS }, () => Array(TETRIS_COLS).fill(''));
}
function tetrisRandomPiece() {
  const p = TETRIS_PIECES[Math.floor(Math.random() * TETRIS_PIECES.length)]!;
  return { shape: p.shape.map((r) => [...r]), color: p.color };
}
function tetrisRotate(shape: number[][]) {
  return shape[0]!.map((_, i) => shape.map((row) => row[i]!).reverse());
}

export function TetrisGame({
  onComplete,
  onAbandon,
  mode = 'standalone',
}: {
  onComplete: (score: number) => void;
  onAbandon: () => void;
  mode?: 'sos' | 'standalone';
}) {
  const insets = useSafeAreaInsets();
  // CELL aus Bildschirmgröße — Header(80) + Padding(40) + Speed-Stepper(50) + Controls(110) + Spacing(20) + Home-Indicator
  const CELL = useMemo(() => {
    const win = Dimensions.get('window');
    const maxW = Math.min(win.width - 32, 400);
    const maxH = win.height - 80 - 40 - 50 - 110 - 20 - Math.max(insets.bottom, 16);
    const cellByW = Math.floor(maxW / TETRIS_COLS);
    const cellByH = Math.floor(maxH / TETRIS_ROWS);
    return Math.max(12, Math.min(cellByW, cellByH));
  }, [insets.bottom]);

  const [board, setBoard] = useState<string[][]>(tetrisEmptyBoard());
  const [current, setCurrent] = useState<TetrisPiece | null>(null);
  const nextPieceRef = useRef(tetrisRandomPiece());
  const [score, setScore] = useState(0);
  const [level, setLevel] = useState(1);
  const [lines, setLines] = useState(0);
  const [gameOver, setGameOver] = useState(false);
  const [isNewBestTetris, setIsNewBestTetris] = useState(false);
  const [highScore, setHighScore] = useState(0);
  const [speedLevel, setSpeedLevel] = useState(3);
  const tickTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);

  // SOS-Mode: kein GameOverScreen, sofort onComplete(score) feuern
  useEffect(() => {
    if (gameOver && mode === 'sos') {
      onComplete(score);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [gameOver, mode]);

  const boardRef = useRef(board);
  const currentRef = useRef(current);
  useEffect(() => { boardRef.current = board; }, [board]);
  useEffect(() => { currentRef.current = current; }, [current]);

  useEffect(() => {
    getBestScore('tetris').then(setHighScore);
  }, []);

  function isValid(piece: TetrisPiece, px: number, py: number, shape = piece.shape): boolean {
    const b = boardRef.current;
    for (let r = 0; r < shape.length; r++) {
      for (let c = 0; c < shape[r]!.length; c++) {
        if (!shape[r]![c]) continue;
        const nx = px + c, ny = py + r;
        if (nx < 0 || nx >= TETRIS_COLS || ny >= TETRIS_ROWS) return false;
        if (ny >= 0 && b[ny]![nx]) return false;
      }
    }
    return true;
  }

  const spawnPiece = useCallback(() => {
    const p = nextPieceRef.current;
    nextPieceRef.current = tetrisRandomPiece();
    const x = Math.floor((TETRIS_COLS - p.shape[0]!.length) / 2);
    const newPiece: TetrisPiece = { ...p, x, y: 0 };
    if (!isValid(newPiece, newPiece.x, newPiece.y)) {
      setGameOver(true);
      stopTick();
      const finalScore = score;
      if (finalScore > highScore) {
        setIsNewBestTetris(true);
        setHighScore(finalScore);
        saveBestScore('tetris', finalScore).catch(() => {});
      }
      return;
    }
    setCurrent(newPiece);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [score, highScore, onComplete]);

  function lockPiece() {
    const piece = currentRef.current;
    if (!piece) return;
    const b = boardRef.current.map((r) => [...r]);
    for (let r = 0; r < piece.shape.length; r++) {
      for (let c = 0; c < piece.shape[r]!.length; c++) {
        if (!piece.shape[r]![c]) continue;
        const ny = piece.y + r, nx = piece.x + c;
        if (ny >= 0) b[ny]![nx] = piece.color;
      }
    }
    const cleared = b.filter((row) => row.every((c) => c !== ''));
    const kept = b.filter((row) => row.some((c) => c === ''));
    const newLines = cleared.length;
    if (newLines > 0) {
      setLines((l) => l + newLines);
      const pts = [0, 100, 300, 500, 800][newLines] ?? 800;
      setScore((s) => s + pts * level);
      setLevel((lv) => Math.floor((lines + newLines) / 10) + 1);
      resetTick();
    }
    const nextBoard = [
      ...Array.from({ length: newLines }, () => Array(TETRIS_COLS).fill('')),
      ...kept,
    ];
    setBoard(nextBoard);
    boardRef.current = nextBoard;
    setCurrent(null);
    setTimeout(() => spawnPiece(), 0);
  }

  function moveLeft() {
    const p = currentRef.current; if (!p || gameOver) return;
    if (isValid(p, p.x - 1, p.y)) setCurrent({ ...p, x: p.x - 1 });
  }
  function moveRight() {
    const p = currentRef.current; if (!p || gameOver) return;
    if (isValid(p, p.x + 1, p.y)) setCurrent({ ...p, x: p.x + 1 });
  }
  function rotatePiece() {
    const p = currentRef.current; if (!p || gameOver) return;
    const rotated = tetrisRotate(p.shape);
    for (const kick of [0, -1, 1, -2, 2]) {
      if (isValid(p, p.x + kick, p.y, rotated)) {
        setCurrent({ ...p, shape: rotated, x: p.x + kick });
        return;
      }
    }
  }
  function softDrop() {
    const p = currentRef.current; if (!p || gameOver) return;
    if (isValid(p, p.x, p.y + 1)) {
      setCurrent({ ...p, y: p.y + 1 });
      setScore((s) => s + 1);
    } else {
      lockPiece();
    }
  }

  function tickInterval() {
    const base = TETRIS_SPEED_BASES[speedLevel - 1]!;
    return Math.max(80, base - (level - 1) * 40);
  }
  function startTick() {
    tickTimerRef.current = setInterval(() => {
      const p = currentRef.current;
      if (!p || gameOver) return;
      if (isValid(p, p.x, p.y + 1)) setCurrent({ ...p, y: p.y + 1 });
      else lockPiece();
    }, tickInterval());
  }
  function stopTick() {
    if (tickTimerRef.current) { clearInterval(tickTimerRef.current); tickTimerRef.current = null; }
  }
  function resetTick() { stopTick(); startTick(); }

  function resetTetris() {
    stopTick();
    const freshBoard = tetrisEmptyBoard();
    setBoard(freshBoard);
    boardRef.current = freshBoard;
    setCurrent(null);
    currentRef.current = null;
    nextPieceRef.current = tetrisRandomPiece();
    setScore(0);
    setLevel(1);
    setLines(0);
    setGameOver(false);
    setIsNewBestTetris(false);
    setTimeout(() => { spawnPiece(); startTick(); }, 0);
  }

  // Init
  useEffect(() => {
    spawnPiece();
    startTick();
    return () => stopTick();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  // Re-tick when speed/level changes
  useEffect(() => { if (!gameOver) resetTick(); /* eslint-disable-next-line */ }, [speedLevel, level]);

  // Display board (board + current piece)
  const displayBoard = useMemo(() => {
    const b = board.map((r) => [...r]);
    if (current) {
      for (let r = 0; r < current.shape.length; r++) {
        for (let c = 0; c < current.shape[r]!.length; c++) {
          if (!current.shape[r]![c]) continue;
          const ny = current.y + r, nx = current.x + c;
          if (ny >= 0 && ny < TETRIS_ROWS && nx >= 0 && nx < TETRIS_COLS) b[ny]![nx] = current.color;
        }
      }
    }
    return b;
  }, [board, current]);

  // Ghost piece
  const ghostCells = useMemo(() => {
    if (!current) return [];
    let gy = current.y;
    while (isValid(current, current.x, gy + 1)) gy++;
    if (gy === current.y) return [];
    const cells: [number, number][] = [];
    for (let r = 0; r < current.shape.length; r++)
      for (let c = 0; c < current.shape[r]!.length; c++)
        if (current.shape[r]![c]) cells.push([current.x + c, gy + r]);
    return cells;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [current, board]);

  const lyraMessage =
    lines >= 10 ? 'Unglaublich! Du bist voll im Flow! 🔥' :
    lines >= 3 ? 'Super Linie! Weiter so.' :
    'Stapel die Blöcke – du schaffst das!';

  const speedColors = ['#22c55e', '#84cc16', '#eab308', '#f97316', '#ef4444'];

  const boardWidth = TETRIS_COLS * CELL;

  return (
    <View style={{ paddingHorizontal: 16, paddingTop: 4, paddingBottom: Math.max(insets.bottom, 16), position: 'relative' }}>
      {!gameOver && (
        <DigitalScore score={score} best={highScore} extra={level} extraLabel="LVL" boardWidth={boardWidth} />
      )}

      {/* Board */}
      <View style={{ alignItems: 'center', marginVertical: 4 }}>
        <View style={{ width: TETRIS_COLS * CELL, height: TETRIS_ROWS * CELL, borderRadius: 12, overflow: 'hidden', backgroundColor: '#0d1117', borderWidth: 1, borderColor: '#1f2937' }}>
          {displayBoard.map((row, y) => (
            <View key={y} style={{ flexDirection: 'row', height: CELL }}>
              {row.map((color, x) => (
                <View key={x} style={{
                  width: CELL, height: CELL, padding: 0.5,
                }}>
                  <View style={{
                    flex: 1, borderRadius: 2,
                    backgroundColor: color || 'transparent',
                  }} />
                </View>
              ))}
            </View>
          ))}
          {/* Ghost piece overlay */}
          {ghostCells.map(([gx, gy], i) => (
            <View key={'g' + i} style={{
              position: 'absolute', left: gx * CELL, top: gy * CELL, width: CELL - 1, height: CELL - 1,
              borderRadius: 2, backgroundColor: current?.color, opacity: 0.2,
            }} />
          ))}
        </View>
      </View>

      {/* Speed — native rendered slider (UISlider on iOS, SeekBar on Android) */}
      <View style={{ marginTop: 14 }}>
        <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
          <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
            <Ionicons name="flash" size={14} color={speedColors[speedLevel - 1]} />
            <Text style={{ fontSize: 12, color: '#6b7280', fontFamily: 'Nunito_600SemiBold' }}>Tempo</Text>
          </View>
          <Text style={{ fontSize: 13, fontFamily: 'Nunito_800ExtraBold', color: speedColors[speedLevel - 1] }}>
            Speed {speedLevel}
          </Text>
        </View>
        <Slider
          style={{ width: '100%', height: Platform.OS === 'ios' ? 32 : 40 }}
          minimumValue={1}
          maximumValue={5}
          step={1}
          value={speedLevel}
          onValueChange={(v) => {
            const nv = Math.round(v);
            if (nv !== speedLevel) {
              tapHaptic();
              setSpeedLevel(nv);
            }
          }}
          minimumTrackTintColor={speedColors[speedLevel - 1]}
          maximumTrackTintColor="#e5e7eb"
          thumbTintColor={Platform.OS === 'android' ? speedColors[speedLevel - 1] : undefined}
        />
      </View>

      {/* Controls — alle 4 Buttons zentriert in einer Reihe (besser thumb-reachable
          als links/rechts gespalten am Board-Rand). */}
      <View style={{ alignItems: 'center', marginTop: 18 }}>
        <View style={{ flexDirection: 'row', gap: 12, alignItems: 'center', justifyContent: 'center' }}>
          <DPadBtn dir="left" active={false} onPress={moveLeft} />
          <TetrisActionBtn icon="sync" label="Drehen" onPress={rotatePiece} />
          <TetrisActionBtn icon="arrow-down" label="Drop" onPress={softDrop} />
          <DPadBtn dir="right" active={false} onPress={moveRight} />
        </View>
      </View>

      {gameOver && mode === 'standalone' && (
        <GameOverScreen
          score={score}
          bestScore={highScore}
          gameName="Tetris"
          scoreLabel="Punkte"
          goodScore={1000}
          isNewBest={isNewBestTetris}
          onRetry={resetTetris}
          onExit={() => onAbandon()}
        />
      )}
    </View>
  );
}

function Stat({ label, value, color }: { label: string; value: number; color: string }) {
  const colors = useColors();
  return (
    <View style={{ alignItems: 'center' }}>
      <Text style={{ fontSize: 9, color: colors.textMuted, textTransform: 'uppercase', letterSpacing: 0.5 }}>{label}</Text>
      <Text style={{ fontSize: 16, fontFamily: 'Nunito_800ExtraBold', color }}>{value}</Text>
    </View>
  );
}

function DigitalScore({
  score,
  best,
  extra,
  extraLabel,
  boardWidth,
}: {
  score: number;
  best: number;
  /** Number → zero-padded to 2 digits. String → rendered as-is (e.g. "01:23" for time). */
  extra?: number | string;
  extraLabel?: string;
  boardWidth: number;
}) {
  const fmt = (n: number, digits = 5) => String(n).padStart(digits, '0');
  const extraDisplay = typeof extra === 'string' ? extra : extra !== undefined ? fmt(extra, 2) : '';
  return (
    <View style={{
      width: boardWidth,
      alignSelf: 'center',
      flexDirection: 'row',
      justifyContent: 'space-between',
      alignItems: 'center',
      backgroundColor: '#0d1117',
      borderRadius: 10,
      borderWidth: 1,
      borderColor: '#1f2937',
      paddingHorizontal: 12,
      paddingVertical: 7,
      marginBottom: 6,
    }}>
      <ScoreCell label="SCORE" value={fmt(score)} bright />
      <View style={{ width: 1, height: 28, backgroundColor: '#1f2937' }} />
      <ScoreCell label="BEST" value={fmt(best)} />
      {extra !== undefined && extraLabel !== undefined && (
        <>
          <View style={{ width: 1, height: 28, backgroundColor: '#1f2937' }} />
          <ScoreCell label={extraLabel} value={extraDisplay} />
        </>
      )}
    </View>
  );
}

function ScoreCell({ label, value, bright }: { label: string; value: string; bright?: boolean }) {
  return (
    <View style={{ alignItems: 'center', flex: 1 }}>
      <Text style={{
        fontSize: 9,
        color: '#4b5563',
        letterSpacing: 1.5,
        fontFamily: 'Nunito_600SemiBold',
        textTransform: 'uppercase',
      }}>{label}</Text>
      <Text style={{
        fontSize: 18,
        fontFamily: 'Courier New' as any,
        fontVariant: ['tabular-nums'],
        color: bright ? '#00e680' : '#6b7280',
        letterSpacing: 2,
        lineHeight: 22,
      }}>{value}</Text>
    </View>
  );
}
