/* global React, ReactDOM */
const { useState, useEffect, useRef, useMemo, useCallback } = React;

// ----- storage ---------------------------------------------------------------
const STORAGE_KEY = 'suguru:v1';

function loadStore() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (!raw) return { history: [], favorites: [], lastId: null, lang: 'en', theme: 'kori', regionStyle: 'hybrid', stats: {} };
    const v = JSON.parse(raw);
    return {
      history: v.history || [],
      favorites: v.favorites || [],
      lastId: v.lastId || null,
      lang: v.lang || 'en',
      theme: v.theme || 'kori',
      regionStyle: v.regionStyle || 'hybrid',
      stats: v.stats || {},
    };
  } catch (_) {
    return { history: [], favorites: [], lastId: null, lang: 'en', theme: 'kori', regionStyle: 'hybrid', stats: {} };
  }
}
function saveStore(store) {
  try { localStorage.setItem(STORAGE_KEY, JSON.stringify(store)); } catch (_) {}
}

function fmtTime(sec) {
  if (sec == null) return '—';
  const m = Math.floor(sec / 60);
  const s = sec % 60;
  return `${m}:${String(s).padStart(2, '0')}`;
}

function uid() { return 'p' + Math.random().toString(36).slice(2, 10) + Date.now().toString(36).slice(-4); }

// ----- Grid (SVG) ------------------------------------------------------------
function SuguruGrid({
  puzzle,        // initial givens (0 = empty)
  regions,       // array of [[r,c],...]
  userGrid,      // user entries (0 = empty)
  notes,         // notes[r][c] = Set of ints
  solution,      // full solution
  selected,      // [r, c] | null
  onSelect,      // (r,c)=>void
  showMistakes,  // bool
  regionStyle,   // 'ink' | 'wash' | 'hybrid'
  theme,
  completed,
}) {
  const R = puzzle.length;
  const C = puzzle[0].length;
  const CELL = 56; // design unit; SVG scales
  const PAD = 6;
  const width = C * CELL + PAD * 2;
  const height = R * CELL + PAD * 2;

  // region id grid
  const regionId = useMemo(() => {
    const g = Array.from({ length: R }, () => new Array(C).fill(-1));
    regions.forEach((cells, i) => cells.forEach(([r, c]) => (g[r][c] = i)));
    return g;
  }, [regions, R, C]);

  // Region tints — cycle through a small palette; theme-aware via CSS vars
  const regionTint = useCallback((i) => {
    if (regionStyle === 'ink') return 'transparent';
    // Use alpha over background so it adapts to theme
    const palette = [
      'var(--wash-a)',
      'var(--wash-b)',
      'var(--wash-c)',
      'var(--wash-d)',
      'var(--wash-e)',
    ];
    // Ensure neighbouring regions don't get same tint via greedy color assignment
    return palette[i % palette.length];
  }, [regionStyle]);

  // Greedy recolor so adjacent regions differ
  const regionColorIdx = useMemo(() => {
    const adj = regions.map(() => new Set());
    for (let r = 0; r < R; r++) {
      for (let c = 0; c < C; c++) {
        const a = regionId[r][c];
        for (const [dr, dc] of [[1,0],[0,1],[1,1],[1,-1]]) {
          const nr = r+dr, nc = c+dc;
          if (nr>=0&&nr<R&&nc>=0&&nc<C) {
            const b = regionId[nr][nc];
            if (b !== a) { adj[a].add(b); adj[b].add(a); }
          }
        }
      }
    }
    const colors = new Array(regions.length).fill(-1);
    for (let i = 0; i < regions.length; i++) {
      const used = new Set();
      adj[i].forEach(j => { if (colors[j] >= 0) used.add(colors[j]); });
      let k = 0; while (used.has(k)) k++;
      colors[i] = k;
    }
    return colors;
  }, [regions, regionId, R, C]);

  // Build region border path segments: edges between cells of DIFFERENT regions, and grid boundary.
  const borderPaths = useMemo(() => {
    const segs = [];
    for (let r = 0; r < R; r++) {
      for (let c = 0; c < C; c++) {
        const rid = regionId[r][c];
        const x = PAD + c * CELL;
        const y = PAD + r * CELL;
        // top
        if (r === 0 || regionId[r - 1][c] !== rid)
          segs.push(`M ${x} ${y} L ${x + CELL} ${y}`);
        // left
        if (c === 0 || regionId[r][c - 1] !== rid)
          segs.push(`M ${x} ${y} L ${x} ${y + CELL}`);
        // bottom
        if (r === R - 1 || regionId[r + 1][c] !== rid)
          segs.push(`M ${x} ${y + CELL} L ${x + CELL} ${y + CELL}`);
        // right
        if (c === C - 1 || regionId[r][c + 1] !== rid)
          segs.push(`M ${x + CELL} ${y} L ${x + CELL} ${y + CELL}`);
      }
    }
    return segs.join(' ');
  }, [regionId, R, C]);

  const thinGridPaths = useMemo(() => {
    const segs = [];
    for (let r = 1; r < R; r++) segs.push(`M ${PAD} ${PAD + r*CELL} L ${PAD + C*CELL} ${PAD + r*CELL}`);
    for (let c = 1; c < C; c++) segs.push(`M ${PAD + c*CELL} ${PAD} L ${PAD + c*CELL} ${PAD + R*CELL}`);
    return segs.join(' ');
  }, [R, C]);

  const cells = [];
  for (let r = 0; r < R; r++) {
    for (let c = 0; c < C; c++) {
      const given = puzzle[r][c] !== 0;
      const val = given ? puzzle[r][c] : userGrid[r][c];
      const noteSet = notes[r][c];
      const isSel = selected && selected[0] === r && selected[1] === c;
      const sameValSelected = selected && userGrid[selected[0]]?.[selected[1]] &&
        (val === userGrid[selected[0]][selected[1]] || val === puzzle[selected[0]][selected[1]]);
      const selVal = selected ? (puzzle[selected[0]][selected[1]] || userGrid[selected[0]][selected[1]]) : 0;
      const highlightVal = selVal !== 0 && val === selVal && !isSel;
      const wrong = showMistakes && !given && val !== 0 && solution && solution[r][c] !== val;

      const x = PAD + c * CELL;
      const y = PAD + r * CELL;

      cells.push(
        <g key={`${r}-${c}`} onClick={() => onSelect(r, c)} style={{ cursor: 'pointer' }}>
          <rect
            x={x} y={y} width={CELL} height={CELL}
            fill={regionStyle === 'ink' ? 'transparent' : regionTint(regionColorIdx[regionId[r][c]])}
          />
          {isSel && (
            <rect x={x+2} y={y+2} width={CELL-4} height={CELL-4}
              fill="var(--select-fill)" stroke="var(--select-stroke)" strokeWidth="2" rx="2" />
          )}
          {!isSel && highlightVal && (
            <rect x={x+2} y={y+2} width={CELL-4} height={CELL-4}
              fill="var(--match-fill)" rx="2" />
          )}
          {val !== 0 ? (
            <text
              x={x + CELL/2} y={y + CELL/2 + 2}
              textAnchor="middle" dominantBaseline="middle"
              fontSize={CELL * 0.52}
              fontFamily="'Shippori Mincho', 'Noto Serif JP', Georgia, serif"
              fill={wrong ? 'var(--error)' : (given ? 'var(--ink-given)' : 'var(--ink-user)')}
              fontWeight={given ? 600 : 400}
            >{val}</text>
          ) : noteSet && noteSet.size > 0 ? (
            [...noteSet].sort().slice(0, 5).map((n, idx) => {
              // Up to 5 notes, arranged in a row at top
              const count = Math.min(5, noteSet.size);
              const nx = x + CELL*0.5 + (idx - (count-1)/2) * (CELL*0.17);
              const ny = y + CELL*0.28;
              return <text key={n}
                x={nx} y={ny}
                textAnchor="middle" dominantBaseline="middle"
                fontSize={CELL*0.20}
                fontFamily="'Noto Sans JP', system-ui, sans-serif"
                fill="var(--ink-note)"
              >{n}</text>;
            })
          ) : null}
        </g>
      );
    }
  }

  return (
    <div className={`grid-wrap ${completed ? 'completed' : ''}`}>
      <svg viewBox={`0 0 ${width} ${height}`} className="suguru-svg" preserveAspectRatio="xMidYMid meet">
        {/* Paper background */}
        <rect x={PAD} y={PAD} width={C*CELL} height={R*CELL} fill="var(--paper-inner)" />
        {cells}
        {/* Thin grid lines */}
        <path d={thinGridPaths} stroke="var(--grid-thin)" strokeWidth="1" fill="none" vectorEffect="non-scaling-stroke" />
        {/* Thick region borders */}
        <path d={borderPaths} stroke="var(--ink-border)" strokeWidth="3.2" fill="none" strokeLinecap="square" />
      </svg>
    </div>
  );
}

window.SuguruGrid = SuguruGrid;
window.SuguruStorage = { loadStore, saveStore, fmtTime, uid, STORAGE_KEY };
