/*
 * memory/game.jsx — PHASE 2 of the two-phase memory curriculum.
 *
 * 2×4 grid of 8 cards = 4 pairs per round. (Phase 1 = pick-pairs,
 * picture-led; this is Phase 2, letter-led.) See
 * docs/two-phase-letter-game.md + ADR 0019.
 *
 * PHASE 2 IDENTITY — "letters with a small illustration cue":
 *   - Every card FACE is the LETTER large, with a SMALL PHOTO CUE in
 *     the corner. On every card, every round. The cue is never
 *     removed — that's the whole point of Phase 2 (the child reads the
 *     letter, the photo is the safety net). Phase 1 owns the
 *     photo-led ramp; Phase 2 is uniformly letter-led-with-cue.
 *     (This replaces the old v1.60 three-stage face ramp whose final
 *     letter-only stage dropped the cue.)
 *   - Card BACK is a neutral closed-card pattern (no letter).
 *   - All 22 letters covered across 6 rounds (4 pairs × 6 = 24 slots;
 *     the bank is shuffled once per session and consumed in 4-letter
 *     slices with wrap so every letter appears).
 *
 * Locked-in suite behaviours we reuse:
 *   - Pointerdown commits (every card uses onPointerDown + preventDefault).
 *   - Sounds via window.SOUNDS — tap / correct / wrong / end.
 *   - Mastery via window.MASTERY.recordRound on each matched pair.
 *   - Progress via window.PROGRESS.save(PROGRESS_KEY, ...).
 *   - EndScreen shows the gallery of items the kid went through.
 *
 * No break screen — memory rounds are short (4 pairs, ~12 taps).
 */

const TWEAK_DEFAULTS = window.TWEAK_DEFAULTS;

// Phase 2 board: 4 pairs (8 cards) — matches Phase 1's board size so
// the two phases feel like one game (and is "more tiles" than the old
// 3-pair board the user flagged as too sparse).
const PAIRS_PER_ROUND = 4;
// 6 rounds × 4 pairs = 24 pair slots, covers all 22 letters with the
// last 2 wrapping from the start of the (shuffled) bank.
const ROUNDS_TOTAL = 6;

// One memory engine, two levels (#73-era merge of pick-pairs + memory,
// ADR 0023). `?level=1` = photo-led (the old pick-pairs / "זיכרון תמונות");
// `?level=2` = letter-led with a photo cue (the old memory / "זיכרון אותיות").
// They share this entire engine + the GAME_CONFIG letter bank; the level only
// picks the card FACE, the palette (set on <html data-game> by index.html),
// and the progress slot. Default level 2.
const LEVEL = (new URLSearchParams(window.location.search).get("level") === "1") ? 1 : 2;
const FACE_BY_LEVEL = { 1: "photo-letter", 2: "letter-photo" };
const LEVEL_FACE   = FACE_BY_LEVEL[LEVEL];
const PROGRESS_KEY = LEVEL === 1 ? "memory-1" : "memory-2";

const _qs = new URLSearchParams(window.location.search);
const _ms = (key, def) => {
  const v = _qs.get(key);
  return v === null || v === "" ? def : Math.max(0, +v) || def;
};
const FLIP_BACK_MS  = _ms("flipBackMs",  1100);
const MATCH_HOLD_MS = _ms("matchHoldMs",  700);

const MEMORY_SPARK_ANGLES = [0, 45, 90, 135, 180, 225, 270, 315];

function buildCards(items) {
  const pairs = items.slice(0, PAIRS_PER_ROUND);
  const deck = [];
  pairs.forEach((item, pi) => {
    deck.push({ id: `${pi}-a`, slotId: item.slotId, letter: item.letter, word: item.word, image: item.image, alt: item.alt });
    deck.push({ id: `${pi}-b`, slotId: item.slotId, letter: item.letter, word: item.word, image: item.image, alt: item.alt });
  });
  return shuffle(deck);
}

function pickRoundItems(bank, roundIdx) {
  const start = (roundIdx * PAIRS_PER_ROUND) % bank.length;
  if (start + PAIRS_PER_ROUND <= bank.length) {
    return bank.slice(start, start + PAIRS_PER_ROUND);
  }
  return bank.slice(start).concat(bank.slice(0, PAIRS_PER_ROUND - (bank.length - start)));
}

function CardFront({ card, faceMode, letterFont }) {
  const letterStyle = { fontFamily: `"${letterFont || "Fredoka"}", "Heebo", system-ui, sans-serif` };
  if (faceMode === "letter") {
    return <span className="mem-card-letter" style={letterStyle}>{card.letter}</span>;
  }
  if (faceMode === "photo-letter") {
    return (
      <>
        <img className="mem-card-photo" src={card.image} alt="" />
        <span className="mem-card-corner-letter" style={letterStyle}>{card.letter}</span>
      </>
    );
  }
  if (faceMode === "letter-photo") {
    return (
      <>
        <span className="mem-card-letter" style={letterStyle}>{card.letter}</span>
        <img className="mem-card-corner-photo" src={card.image} alt="" />
      </>
    );
  }
  return null;
}

function Card({ card, faceUp, matched, lockOut, onPick, cardRef, faceMode, letterFont }) {
  const ariaLabel = faceUp || matched ? `אות ${card.letter}` : "קלף";
  return (
    <button
      ref={cardRef}
      type="button"
      className="mem-card"
      data-face-up={faceUp ? "1" : "0"}
      data-matched={matched ? "1" : "0"}
      data-locked={lockOut ? "1" : "0"}
      data-face={faceMode}
      aria-label={ariaLabel}
      onPointerDown={(e) => { e.preventDefault(); if (!lockOut && !faceUp && !matched) onPick(card.id); }}
    >
      <span className="mem-card-inner">
        <span className="mem-card-face mem-card-back" aria-hidden="true">
          <span className="mem-card-back-glyph">?</span>
        </span>
        <span className="mem-card-face mem-card-front" aria-hidden="true">
          <CardFront card={card} faceMode={faceMode} letterFont={letterFont} />
        </span>
      </span>
      {matched && (
        <span className="mem-card-burst" aria-hidden="true">
          {MEMORY_SPARK_ANGLES.map((a) => (
            <span key={a} className="mem-card-spark" style={{ "--angle": `${a}deg` }} />
          ))}
        </span>
      )}
    </button>
  );
}

function Grid({ cards, flipped, matchedSlotIds, lockOut, onPick, cardRefs, faceMode, letterFont }) {
  return (
    <div className="mem-grid" role="group" aria-label="זיכרון">
      {cards.map((card, i) => {
        const isFlipped = flipped.includes(card.id);
        const isMatched = matchedSlotIds.includes(card.slotId);
        return (
          <Card
            key={card.id}
            card={card}
            faceUp={isFlipped}
            matched={isMatched}
            lockOut={lockOut}
            onPick={onPick}
            cardRef={(el) => { cardRefs.current[i] = el; }}
            faceMode={faceMode}
            letterFont={letterFont}
          />
        );
      })}
    </div>
  );
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  useApplyTweaks(t);

  const { rounds } = window.GAME_CONFIG;

  // Shuffle the full 22-letter bank once per session — pickRoundItems
  // slices in 3-item chunks with wrap so every letter gets covered.
  // useMemo so re-renders don't re-shuffle (which would shift which
  // letters belong to which round mid-game).
  const bank = React.useMemo(() => shuffle(rounds), [rounds]);

  // Resume from saved progress. Memory's saved state is just roundIdx;
  // every match is "clean" so firstTries stays [] and there's no break.
  const saved = (typeof window !== "undefined" && window.PROGRESS) ? window.PROGRESS.load(PROGRESS_KEY) : null;
  const initialRound = saved && saved.roundIdx < ROUNDS_TOTAL ? saved.roundIdx : 0;

  const [roundIdx, setRoundIdx]   = React.useState(initialRound);
  const [cards, setCards]         = React.useState(() => buildCards(pickRoundItems(bank, initialRound)));
  const [flipped, setFlipped]     = React.useState([]);
  const [matchedSlotIds, setMatched] = React.useState([]);
  const [lockOut, setLockOut]     = React.useState(false);
  const [done, setDone]           = React.useState(false);
  const [playedRounds, setPlayedRounds] = React.useState([]);

  const cardRefs = React.useRef([]);

  // Phase 2: always letter-led with a corner photo cue (no round ramp).
  const faceMode = LEVEL_FACE;

  const advance = React.useCallback(() => {
    const justPlayed = pickRoundItems(bank, roundIdx);
    setPlayedRounds((prev) => [...prev, ...justPlayed]);
    if (roundIdx + 1 >= ROUNDS_TOTAL) {
      setDone(true);
      return;
    }
    const next = roundIdx + 1;
    setRoundIdx(next);
    setCards(buildCards(pickRoundItems(bank, next)));
    setFlipped([]);
    setMatched([]);
    setLockOut(false);
  }, [roundIdx, bank]);

  const restart = () => {
    setDone(false);
    setRoundIdx(0);
    setCards(buildCards(pickRoundItems(bank, 0)));
    setFlipped([]);
    setMatched([]);
    setLockOut(false);
    setPlayedRounds([]);
  };

  React.useEffect(() => { if (done && window.SOUNDS) window.SOUNDS.play("end"); }, [done]);

  React.useEffect(() => {
    if (!window.PROGRESS) return;
    if (done) { window.PROGRESS.clear(PROGRESS_KEY); return; }
    window.PROGRESS.save(PROGRESS_KEY, { roundIdx, firstTries: [], lastBreak: 0 });
  }, [roundIdx, done]);

  const onPick = (cardId) => {
    if (lockOut) return;
    if (flipped.includes(cardId)) return;

    if (window.SOUNDS) window.SOUNDS.play("tap");

    const nextFlipped = [...flipped, cardId];
    setFlipped(nextFlipped);

    if (nextFlipped.length < 2) return;

    setLockOut(true);

    const [aId, bId] = nextFlipped;
    const a = cards.find((c) => c.id === aId);
    const b = cards.find((c) => c.id === bId);

    if (a && b && a.slotId === b.slotId) {
      if (window.SOUNDS) window.SOUNDS.play("correct");
      if (window.MASTERY) window.MASTERY.recordRound(a.letter, true);
      const nextMatched = [...matchedSlotIds, a.slotId];
      setMatched(nextMatched);
      window.setTimeout(() => {
        setFlipped([]);
        setLockOut(false);
        if (nextMatched.length >= PAIRS_PER_ROUND) {
          window.setTimeout(advance, 400);
        }
      }, MATCH_HOLD_MS);
    } else {
      if (window.SOUNDS) window.SOUNDS.play("wrong");
      window.setTimeout(() => {
        setFlipped([]);
        setLockOut(false);
      }, FLIP_BACK_MS);
    }
  };

  return (
    <div className="app">
      {/* progress: 1 dot = 1 round of 4 pairs (6 rounds spanning all 22 letters) */}
      <Header index={roundIdx} total={ROUNDS_TOTAL} />
      <div className="mem-stage">
        <Grid
          key={`grid-${roundIdx}`}
          cards={cards}
          flipped={flipped}
          matchedSlotIds={matchedSlotIds}
          lockOut={lockOut}
          onPick={onPick}
          cardRefs={cardRefs}
          faceMode={faceMode}
          letterFont={t.letterFont}
        />
      </div>

      {done && (
        <EndScreen
          onRestart={restart}
          playedRounds={playedRounds}
          playedFirstTries={playedRounds.map(() => true)}
        />
      )}

      <TweaksPanel title="Tweaks">
        <SharedTweakControls t={t} setTweak={setTweak}>
          <TweakSection label="פעולות" />
          <TweakButton label="שחק מההתחלה" onClick={restart} />
        </SharedTweakControls>
      </TweaksPanel>
    </div>
  );
}

// R15 — levels-under-one-tile: the landing shows a single "זיכרון" tile
// linking to ?pick=1, which shows this picker; choosing a level navigates to
// ?level=1|2 and the game plays. Mirrors game 1's ThemePicker (#102) so the
// two memory levels are reached the SAME way game 1's collections are — a
// sub-step inside the game, not sibling landing tiles. Bare /games/memory/
// and existing ?level= deep-links are unchanged (every gameplay spec passes).
// Reuses the shared .theme-picker / .theme-pick surface (generic picker
// chrome defined in 03-initial.css).
function LevelPicker() {
  const levels = [
    { id: "1", label: "תמונות", img: "assets/tiles/memory-1.svg" },
    { id: "2", label: "אותיות", img: "assets/tiles/memory-2.svg" },
  ];
  return (
    <div className="app theme-picker">
      <Crown />
      <h2 className="theme-picker__title">בחרו רמה</h2>
      {/* href is base-relative to the GAME dir (like game 1's picker), NOT a
          bare "?level=" — a query-only href resolves against <base href="../../">
          and would land on the landing. */}
      <div className="theme-picker__grid theme-picker__grid--levels" role="group" aria-label="בחרו רמה">
        {levels.map((lv) => (
          <a key={lv.id} className="theme-pick" href={`games/memory/?level=${lv.id}`}
             aria-label={`רמה ${lv.id}, ${lv.label}`}>
            <span className="theme-pick__art"><img src={lv.img} alt="" /></span>
            <span className="theme-pick__label">{lv.label}</span>
          </a>
        ))}
      </div>
    </div>
  );
}

function Root() {
  const showPicker =
    typeof window !== "undefined" &&
    new URLSearchParams(window.location.search).get("pick") === "1";
  return showPicker ? <LevelPicker /> : <App />;
}

ReactDOM.createRoot(document.getElementById("root")).render(<Root />);
