/*
 * game.jsx — Game 5 ("find-letter"): big target letter at the top, a
 * 3×4 grid of letter tiles below — some are the target, some are
 * distractors. Kid taps every occurrence of the target. When all
 * targets are tapped, the round advances.
 *
 * Why this concept?
 *   - Pure visual scanning — a 4-5yo can play purely by shape matching.
 *   - Reinforces what shadow-letter introduced (silhouette → button) but
 *     reverses the search: instead of one big letter and 3 candidates,
 *     it's one target glyph against 12 candidates, training the kid to
 *     pick familiar shapes out of a busier field.
 *   - Zero asset creation. Like shadow-letter, every glyph is just the
 *     Hebrew character in --letter-font.
 *   - Per round structure: { letter (the target), tiles, correctIdxs }.
 *     `correctIdxs` are the grid positions that hold the target. We
 *     always plant between 3 and 5 copies of the target per 12-tile
 *     round so the kid has multiple chances + a clear "I got them all"
 *     finish; distractors fill the rest.
 *
 * Locked-in suite behaviours we reuse:
 *   - Pointerdown commits (every tile uses onPointerDown + preventDefault).
 *   - Sounds via window.SOUNDS — tap / correct / wrong / break / end.
 *   - Mastery via window.MASTERY.recordRound on round complete.
 *   - Progress via window.PROGRESS.save("find-letter", ...).
 *   - Dopamine on each correct tap (scale pop + 8 sparks per tile).
 *   - BreakScreen every 5 rounds; EndScreen at the end of the 22-round bank.
 */

const TWEAK_DEFAULTS = window.TWEAK_DEFAULTS;

const GRID_SIZE = 12;            // 3 cols × 4 rows
const MIN_TARGETS = 3;
const MAX_TARGETS = 5;

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;
};
// Wrong tap surfaces the X for this long before clearing.
const WRONG_REVEAL_MS  = _ms("wrongMs", 700);
// Brief hold after the last target so the kid sees the burst before advance.
const ROUND_END_HOLD_MS = _ms("roundEndMs", 700);
// Idle-cue timing + mapping are shared from game-shared.jsx (HINT_STEPS
// [5/10/15s], HINT_CYCLE_MS, hintCueFor) — the one pattern, ADR 0028. The
// cycle still resets on every tap (tapNonce) so it only fires when stuck.

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

// Build a 12-tile grid for round `roundIdx`. Plants `n` copies of the
// target letter (3..5) and fills the rest with random distractor letters
// from the 22-letter pool. Distractors are NOT necessarily unique — the
// task is "find this letter", not "find the duplicates" — but no slot
// next to itself collisions are forbidden, the kid can see them all.
function buildGrid(target, pool) {
  const targetCount = MIN_TARGETS + Math.floor(Math.random() * (MAX_TARGETS - MIN_TARGETS + 1));
  const tiles = new Array(GRID_SIZE).fill(null);
  // Random positions for target.
  const positions = shuffle(Array.from({ length: GRID_SIZE }, (_, i) => i)).slice(0, targetCount);
  const correctIdxs = new Set(positions);
  for (const i of positions) tiles[i] = target;
  // Fill remaining slots with distractor letters drawn from the pool
  // (excluding the target). Allow repeats among distractors so the
  // visual scanning task is realistic.
  const distractors = pool.filter((x) => x !== target);
  for (let i = 0; i < GRID_SIZE; i++) {
    if (tiles[i] === null) {
      tiles[i] = distractors[Math.floor(Math.random() * distractors.length)];
    }
  }
  return { tiles, correctIdxs };
}

// ── Target card (the big letter the kid is searching for) ──────────────
function TargetCard({ letter }) {
  return (
    <div className="stage stage--find-letter">
      <div className="find-target-card" aria-hidden="true">
        <span className="find-target-glyph">{letter}</span>
      </div>
    </div>
  );
}

// ── Grid of letter tiles ───────────────────────────────────────────────
function Tile({ letter, found, wrong, disabled, hint, onPick, tileRef }) {
  return (
    <button
      ref={tileRef}
      type="button"
      className="find-tile"
      data-found={found ? "1" : "0"}
      data-wrong={wrong ? "1" : "0"}
      data-hint={hint || undefined}
      data-disabled={disabled ? "1" : "0"}
      aria-label={letter}
      onPointerDown={(e) => {
        e.preventDefault();
        if (disabled || found) return;
        onPick();
      }}
    >
      <span className="find-tile-glyph">{letter}</span>
      <span className="find-tile-x" aria-hidden="true">
        <svg viewBox="0 0 60 60" width="40" height="40">
          <path d="M16 16 L44 44 M44 16 L16 44" stroke="#fff" strokeWidth="7" strokeLinecap="round" fill="none" />
        </svg>
      </span>
      {found && (
        <span className="find-tile-burst" aria-hidden="true">
          {SUCCESS_SPARK_ANGLES.map((a) => (
            <span key={a} className="find-tile-spark" style={{ "--angle": `${a}deg` }} />
          ))}
        </span>
      )}
    </button>
  );
}

function Grid({ tiles, foundIdxs, wrongIdx, disabled, hintFor, onPick, tileRefs }) {
  return (
    <div className="find-grid" role="group" aria-label="מצא את כל המופעים של האות">
      {tiles.map((letter, i) => (
        <Tile
          key={`${i}:${letter}`}
          letter={letter}
          found={foundIdxs.includes(i)}
          wrong={wrongIdx === i}
          hint={hintFor(i)}
          disabled={disabled}
          onPick={() => onPick(i, letter)}
          tileRef={(el) => { tileRefs.current[i] = el; }}
        />
      ))}
    </div>
  );
}

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

  const { rounds, letterPool } = window.GAME_CONFIG;

  // Resume from previous progress (per theme + n, scoped by PROGRESS).
  const saved = (typeof window !== "undefined" && window.PROGRESS) ? window.PROGRESS.load("find-letter") : null;
  const initialRound = saved ? saved.roundIdx : 0;
  const initialFirstTries = saved ? saved.firstTries : [];
  const initialLastBreak = saved ? Math.max(saved.lastBreak, Math.floor(saved.roundIdx / 5)) : 0;

  const [roundIdx, setRoundIdx] = React.useState(initialRound);
  const initialBuild = React.useMemo(() => buildGrid(rounds[initialRound].letter, letterPool), []);
  const [tiles, setTiles] = React.useState(initialBuild.tiles);
  const [correctIdxs, setCorrectIdxs] = React.useState(initialBuild.correctIdxs);
  const [foundIdxs, setFoundIdxs] = React.useState([]);
  const [wrongIdx, setWrongIdx]   = React.useState(-1);
  const [disabled, setDisabled]   = React.useState(false);
  const [done, setDone]           = React.useState(false);
  const [lastBreak, setLastBreak] = React.useState(initialLastBreak);

  const [firstTries, setFirstTries] = React.useState(initialFirstTries);
  const [tapNonce, setTapNonce]   = React.useState(0); // resets the hint cycle on each tap
  const roundCleanRef = React.useRef(true);

  const tileRefs = React.useRef({});

  const breakAt = (roundIdx > 0 && roundIdx % 5 === 0) ? Math.floor(roundIdx / 5) : 0;
  const showBreak = breakAt > 0 && lastBreak < breakAt && !done && !disabled;
  const batchRounds = showBreak ? rounds.slice(roundIdx - 5, roundIdx) : [];

  const round = rounds[roundIdx];

  // Scan hint (#179): fires only after the kid pauses (the cycle restarts on
  // each tap via tapNonce). Level 1 fades the non-target tiles; level 2
  // glitters the still-unfound targets. Inactive once the round is solved.
  const allFound = foundIdxs.length >= correctIdxs.size;
  const hintLevel = useHintCycle(
    !done && !showBreak && !allFound,
    HINT_STEPS, HINT_CYCLE_MS, tapNonce
  );
  // Unified idle cue (ADR 0028) via the shared helper. Multi-correct scan
  // → NOT fewOptions: non-targets fade @t+5 & @t+10, unfound targets glitter
  // @t+15. Found tiles get no cue.
  const hintFor = React.useCallback((i) => {
    if (foundIdxs.includes(i)) return null;
    return hintCueFor(hintLevel, {
      correct: correctIdxs.has(i),
      fewOptions: false,
    });
  }, [hintLevel, foundIdxs, correctIdxs]);
  React.useEffect(() => { if (hintLevel > 0) roundCleanRef.current = false; }, [hintLevel]);

  const advance = React.useCallback(() => {
    if (roundIdx + 1 >= rounds.length) { setDone(true); return; }
    const next = roundIdx + 1;
    const built = buildGrid(rounds[next].letter, letterPool);
    setRoundIdx(next);
    setTiles(built.tiles);
    setCorrectIdxs(built.correctIdxs);
    setFoundIdxs([]);
    setWrongIdx(-1);
    setDisabled(false);
    roundCleanRef.current = true;
  }, [roundIdx, rounds, letterPool]);

  const restart = () => {
    const built = buildGrid(rounds[0].letter, letterPool);
    setDone(false);
    setRoundIdx(0);
    setTiles(built.tiles);
    setCorrectIdxs(built.correctIdxs);
    setFoundIdxs([]);
    setWrongIdx(-1);
    setDisabled(false);
    setLastBreak(0);
    setFirstTries([]);
    roundCleanRef.current = true;
  };

  // Sounds for break / end transitions.
  React.useEffect(() => { if (showBreak && window.SOUNDS) window.SOUNDS.play("break"); }, [showBreak]);
  React.useEffect(() => { if (done && window.SOUNDS) window.SOUNDS.play("end"); }, [done]);

  // Persist resume state on every advance.
  React.useEffect(() => {
    if (!window.PROGRESS) return;
    if (done) { window.PROGRESS.clear("find-letter"); return; }
    window.PROGRESS.save("find-letter", { roundIdx, firstTries, lastBreak });
  }, [roundIdx, firstTries, lastBreak, done]);

  const onPick = (idx, letter) => {
    if (disabled) return;
    if (foundIdxs.includes(idx)) return;
    setTapNonce((n) => n + 1);   // a tap restarts the idle hint cycle
    if (window.SOUNDS) window.SOUNDS.play("tap");

    if (correctIdxs.has(idx)) {
      // Correct find. Add to foundIdxs; if all targets found, round is
      // complete → fire correct sound + mastery, hold a beat, then advance.
      if (window.SOUNDS) window.SOUNDS.play("correct");
      const nextFound = [...foundIdxs, idx];
      setFoundIdxs(nextFound);

      if (nextFound.length >= correctIdxs.size) {
        // Round complete.
        setDisabled(true);
        const wasClean = roundCleanRef.current;
        setFirstTries((prev) => [...prev, wasClean]);
        if (window.MASTERY) window.MASTERY.recordRound(round.letter, wasClean);
        window.setTimeout(advance, ROUND_END_HOLD_MS);
      }
    } else {
      // Wrong tile — shake the tile, mark roundCleanRef false, do NOT count.
      if (window.SOUNDS) window.SOUNDS.play("wrong");
      roundCleanRef.current = false;
      setWrongIdx(idx);
      window.setTimeout(() => setWrongIdx((w) => (w === idx ? -1 : w)), WRONG_REVEAL_MS);
    }
  };

  return (
    <div className="app app--find-letter">
      {/* progress: 1 dot = 1 letter as the find-target (default 22; truncated by ?n=) */}
      <Header index={roundIdx} total={rounds.length} />
      <TargetCard letter={round.letter} />
      <Grid
        key={`grid-${roundIdx}`}
        tiles={tiles}
        foundIdxs={foundIdxs}
        wrongIdx={wrongIdx}
        disabled={disabled}
        hintFor={hintFor}
        onPick={onPick}
        tileRefs={tileRefs}
      />

      {showBreak && (
        <BreakScreen
          batchRounds={batchRounds}
          batchFirstTries={firstTries.slice(roundIdx - 5, roundIdx)}
          completed={roundIdx}
          total={rounds.length}
          onContinue={() => setLastBreak(breakAt)}
          onStop={() => {
            window.location.href = document.baseURI;
          }}
        />
      )}
      {done && (
        <EndScreen
          onRestart={restart}
          playedRounds={rounds.slice(0, firstTries.length)}
          playedFirstTries={firstTries}
        />
      )}

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

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