/* global React, Icon, IconBtn, db */
const { useState, useEffect, useMemo, useCallback, useRef } = React;

// ============================================================
// Word search — themed puzzles. Tap first and last letter of a
// word to select it (or click-drag). Diagonal & reversed
// placements supported.
// ============================================================

const WS_THEMES = [
  { id: 'animals', label: 'Animals', words: ['TIGER','EAGLE','OTTER','PANDA','GECKO','RAVEN','MOOSE','SHARK','LLAMA','FERRET','BADGER','LION'] },
  { id: 'foods',   label: 'Foods',   words: ['BREAD','MANGO','OLIVE','CURRY','LEMON','SUSHI','TACOS','PASTA','APPLE','SALAD','PIZZA','RAMEN'] },
  { id: 'cities',  label: 'Cities',  words: ['TOKYO','PARIS','LAGOS','OSLO','LIMA','DUBAI','SEOUL','BERLIN','SYDNEY','HANOI','CAIRO','PORTO'] },
  { id: 'tech',    label: 'Tech',    words: ['PIXEL','LINUX','REACT','REGEX','PROXY','CACHE','TOKEN','LATEX','ROUTER','SOCKET','BINARY','CLOUD'] },
  { id: 'nature',  label: 'Nature',  words: ['RIVER','FOREST','CANYON','MEADOW','DESERT','CORAL','TUNDRA','LAGOON','GLADE','MOSS','FERN','PINE'] },
  { id: 'space',   label: 'Space',   words: ['ORBIT','COMET','NEBULA','PLUTO','QUASAR','MARS','GALAXY','ECLIPSE','METEOR','SATURN','VENUS','LUNAR'] },
];

const WS_SIZE = 12;
const WS_DIRS = [
  [0, 1],   // right
  [0, -1],  // left
  [1, 0],   // down
  [-1, 0],  // up
  [1, 1],   // down-right
  [-1, -1], // up-left
  [1, -1],  // down-left
  [-1, 1],  // up-right
];

function tryPlace(grid, word, size) {
  for (let tries = 0; tries < 80; tries++) {
    const dir = WS_DIRS[Math.floor(Math.random() * WS_DIRS.length)];
    const [dr, dc] = dir;
    const endR = (word.length - 1) * dr;
    const endC = (word.length - 1) * dc;
    const r = Math.floor(Math.random() * size - Math.max(0, endR)) + Math.max(0, -endR);
    const c = Math.floor(Math.random() * size - Math.max(0, endC)) + Math.max(0, -endC);
    if (r < 0 || r >= size || c < 0 || c >= size) continue;
    if (r + endR < 0 || r + endR >= size) continue;
    if (c + endC < 0 || c + endC >= size) continue;

    let ok = true;
    for (let i = 0; i < word.length; i++) {
      const rr = r + dr * i, cc = c + dc * i;
      const existing = grid[rr][cc];
      if (existing && existing !== word[i]) { ok = false; break; }
    }
    if (!ok) continue;
    // Place
    const positions = [];
    for (let i = 0; i < word.length; i++) {
      const rr = r + dr * i, cc = c + dc * i;
      grid[rr][cc] = word[i];
      positions.push([rr, cc]);
    }
    return positions;
  }
  return null;
}

function generatePuzzle(words, size = WS_SIZE) {
  const grid = Array.from({ length: size }, () => Array(size).fill(null));
  const placed = [];
  // Try longest first for better packing
  const sorted = [...words].sort((a, b) => b.length - a.length);
  for (const w of sorted) {
    const positions = tryPlace(grid, w, size);
    if (positions) placed.push({ word: w, positions });
  }
  // Fill remaining cells with random uppercase letters
  for (let r = 0; r < size; r++) for (let c = 0; c < size; c++) {
    if (!grid[r][c]) grid[r][c] = String.fromCharCode(65 + Math.floor(Math.random() * 26));
  }
  return { grid, placed };
}

function lineBetween(a, b) {
  if (!a || !b) return null;
  const dr = b[0] - a[0], dc = b[1] - a[1];
  // Must be horizontal, vertical or 45-deg diagonal
  if (dr === 0 && dc === 0) return null;
  if (dr !== 0 && dc !== 0 && Math.abs(dr) !== Math.abs(dc)) return null;
  const len = Math.max(Math.abs(dr), Math.abs(dc)) + 1;
  const sr = Math.sign(dr), sc = Math.sign(dc);
  const out = [];
  for (let i = 0; i < len; i++) out.push([a[0] + sr * i, a[1] + sc * i]);
  return out;
}

function WordSearchTool() {
  const [themeId, setThemeId] = useState('animals');
  const [puzzle, setPuzzle] = useState(null);
  const [found, setFound] = useState([]); // array of word strings
  const [selectStart, setSelectStart] = useState(null);
  const [hover, setHover] = useState(null);
  const [startedAt, setStartedAt] = useState(null);
  const [completedAt, setCompletedAt] = useState(null);
  const [now, setNow] = useState(Date.now());

  // Generate puzzle on theme change
  useEffect(() => {
    const theme = WS_THEMES.find(t => t.id === themeId) || WS_THEMES[0];
    setPuzzle(generatePuzzle(theme.words));
    setFound([]);
    setSelectStart(null);
    setHover(null);
    setStartedAt(Date.now());
    setCompletedAt(null);
  }, [themeId]);

  // Timer tick
  useEffect(() => {
    if (!startedAt || completedAt) return;
    const id = setInterval(() => setNow(Date.now()), 500);
    return () => clearInterval(id);
  }, [startedAt, completedAt]);

  const theme = WS_THEMES.find(t => t.id === themeId) || WS_THEMES[0];
  const elapsed = startedAt ? (completedAt || now) - startedAt : 0;
  const allWords = puzzle ? puzzle.placed.map(p => p.word) : [];
  const remaining = allWords.filter(w => !found.includes(w));

  // Check completion
  useEffect(() => {
    if (puzzle && remaining.length === 0 && !completedAt) {
      setCompletedAt(Date.now());
    }
  }, [remaining.length, puzzle, completedAt]);

  // Selection logic
  const onCellDown = (r, c) => {
    if (completedAt) return;
    setSelectStart([r, c]);
    setHover([r, c]);
  };
  const onCellEnter = (r, c) => {
    if (!selectStart) return;
    setHover([r, c]);
  };
  const onCellUp = (r, c) => {
    if (!selectStart || !puzzle) { setSelectStart(null); return; }
    const line = lineBetween(selectStart, [r, c]);
    if (line) {
      const word = line.map(([rr, cc]) => puzzle.grid[rr][cc]).join('');
      const rev = word.split('').reverse().join('');
      const match = allWords.find(w => (w === word || w === rev) && !found.includes(w));
      if (match) setFound([...found, match]);
    }
    setSelectStart(null);
    setHover(null);
  };

  const previewLine = useMemo(() => lineBetween(selectStart, hover) || [], [selectStart, hover]);
  const previewSet = useMemo(() => new Set(previewLine.map(([r, c]) => `${r}:${c}`)), [previewLine]);
  const previewWord = useMemo(() => previewLine.map(([r, c]) => puzzle?.grid[r][c]).join(''), [previewLine, puzzle]);
  const previewMatch = useMemo(() => {
    if (!previewWord) return false;
    const rev = previewWord.split('').reverse().join('');
    return allWords.some(w => (w === previewWord || w === rev) && !found.includes(w));
  }, [previewWord, allWords, found]);

  const foundCells = useMemo(() => {
    if (!puzzle) return new Map();
    const m = new Map();
    for (const p of puzzle.placed) {
      if (!found.includes(p.word)) continue;
      for (const [r, c] of p.positions) m.set(`${r}:${c}`, p.word);
    }
    return m;
  }, [puzzle, found]);

  if (!puzzle) return <div className="ws-tool" />;

  return (
    <div className="ws-tool">
      <header className="ws-head">
        <div>
          <h1>Word search</h1>
          <p>Drag from the first letter of a word to its last. Diagonal and reversed words count.</p>
        </div>
        <div className="ws-themes">
          {WS_THEMES.map(t => (
            <button
              key={t.id}
              className={`ws-theme ${themeId === t.id ? 'is-active' : ''}`}
              onClick={() => setThemeId(t.id)}
            >{t.label}</button>
          ))}
        </div>
      </header>

      <div className="ws-status">
        <span><Icon name="timer" />{formatWsTime(elapsed)}</span>
        <span><Icon name="check_circle" />{found.length} / {allWords.length}</span>
        {completedAt && <span className="ws-win"><Icon name="emoji_events" /> Completed!</span>}
        <button className="lh-btn ghost" onClick={() => setThemeId(themeId)} title="New puzzle (re-roll)">
          <Icon name="refresh" />New puzzle
        </button>
      </div>

      <div className="ws-layout">
        <div className="ws-board"
          style={{ '--size': WS_SIZE }}
          onMouseLeave={() => { setSelectStart(null); setHover(null); }}
        >
          {puzzle.grid.map((row, r) => row.map((ch, c) => {
            const k = `${r}:${c}`;
            const isFound = foundCells.has(k);
            const isPreview = previewSet.has(k);
            return (
              <button
                key={k}
                className={`ws-cell ${isFound ? 'is-found' : ''} ${isPreview ? (previewMatch ? 'is-preview-match' : 'is-preview') : ''}`}
                onMouseDown={() => onCellDown(r, c)}
                onMouseEnter={() => onCellEnter(r, c)}
                onMouseUp={() => onCellUp(r, c)}
                onTouchStart={(e) => { e.preventDefault(); onCellDown(r, c); }}
                onTouchMove={(e) => {
                  e.preventDefault();
                  const t = e.touches[0];
                  const el = document.elementFromPoint(t.clientX, t.clientY);
                  if (el?.dataset?.cell) {
                    const [rr, cc] = el.dataset.cell.split(':').map(Number);
                    onCellEnter(rr, cc);
                  }
                }}
                onTouchEnd={() => { if (hover) onCellUp(hover[0], hover[1]); }}
                data-cell={k}
              >{ch}</button>
            );
          }))}
        </div>

        <aside className="ws-words">
          <h3>Find these</h3>
          <ul>
            {theme.words.filter(w => allWords.includes(w)).map(w => (
              <li key={w} className={found.includes(w) ? 'is-found' : ''}>
                <Icon name={found.includes(w) ? 'check_circle' : 'circle'} />
                <span>{w}</span>
              </li>
            ))}
          </ul>
          {!allWords.includes(theme.words[0]) && (
            <p className="ws-note">Some words didn't fit — re-roll to try again.</p>
          )}
        </aside>
      </div>
    </div>
  );
}

function formatWsTime(ms) {
  const total = Math.floor(ms / 1000);
  const m = Math.floor(total / 60), s = total % 60;
  return `${m}:${String(s).padStart(2, '0')}`;
}

window.WordSearchTool = WordSearchTool;
