/* tools.jsx — Tools landing page + 3 tool modals.
   Globals: ToolsPage, shuffleIntoGroups, GROUP_COLORS,
            GroupsModal, PairLocksEditor, RandomPupilModal
*/

(function () {
const { useState, useEffect, useMemo } = React;

// ===========================================================
// ToolsPage — 3 launcher tiles
// ===========================================================
function ToolsPage({ students, onOpenStudent, pushToast, onOpenTool }) {
  const tiles = [
    { id: 'groups',  title: 'Random Groepen', sub: 'Verdeel de klas willekeurig in groepen', icon: '◫', color: 'blue'  },
    { id: 'random',  title: 'Random Inwoner', sub: 'Kies één leerling',                       icon: '★', color: 'orange' },
    { id: 'timer',   title: 'Timer & Stopwatch', sub: 'Tik tok',                              icon: '⧗', color: 'purple' },
  ];
  return (
    <>
      <p className="page-eyebrow">Klastools</p>
      <h1 className="page-title">Tools voor de klas</h1>
      <p className="page-sub">Klik een tegel om te starten.</p>
      <div style={{ height: 22 }} />
      <div className="tool-grid">
        {tiles.map(t => (
          <button key={t.id} className={`tool-tile color-${t.color}`} onClick={() => onOpenTool(t.id)}>
            <div className="tool-tile-icon">{t.icon}</div>
            <div className="tool-tile-title">{t.title}</div>
            <div className="tool-tile-sub">{t.sub}</div>
          </button>
        ))}
      </div>
    </>
  );
}

// ===========================================================
// shuffleIntoGroups — Fisher-Yates + together/apart constraints
// ===========================================================
function fisherYates(arr) {
  const a = arr.slice();
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}

function shuffleIntoGroups(students, n, { together = [], apart = [] } = {}) {
  const idToCluster = new Map();
  const clusters = [];
  for (const set of together) {
    const ids = Array.from(set).filter(id => students.some(s => s.id === id));
    if (ids.length < 2) continue;
    const cluster = { ids: [], members: [] };
    for (const id of ids) {
      if (idToCluster.has(id)) continue;
      idToCluster.set(id, cluster);
      cluster.ids.push(id);
      cluster.members.push(students.find(s => s.id === id));
    }
    if (cluster.ids.length >= 2) clusters.push(cluster);
  }
  const singles = [];
  for (const s of students) {
    if (!idToCluster.has(s.id)) {
      singles.push({ ids: [s.id], members: [s] });
    }
  }
  const items = [
    ...clusters.sort((a, b) => b.ids.length - a.ids.length),
    ...fisherYates(singles),
  ];

  const bins = Array.from({ length: n }, () => []);
  const sizeOf = b => b.reduce((sum, it) => sum + it.ids.length, 0);
  for (const it of items) {
    let smallest = 0;
    for (let i = 1; i < n; i++) {
      if (sizeOf(bins[i]) < sizeOf(bins[smallest])) smallest = i;
    }
    bins[smallest].push(it);
  }

  const violates = () => {
    for (const set of apart) {
      const ids = Array.from(set);
      for (let i = 0; i < bins.length; i++) {
        const hereIds = bins[i].flatMap(it => it.ids);
        const hits = ids.filter(id => hereIds.includes(id));
        if (hits.length >= 2) return { binIdx: i, offenderId: hits[1] };
      }
    }
    return null;
  };
  for (let attempt = 0; attempt < 50; attempt++) {
    const v = violates();
    if (!v) break;
    const fromBin = bins[v.binIdx];
    const fromIdx = fromBin.findIndex(it => it.ids.includes(v.offenderId));
    if (fromIdx < 0) break;
    const item = fromBin[fromIdx];
    let moved = false;
    const order = fisherYates(bins.map((_, i) => i).filter(i => i !== v.binIdx));
    for (const toIdx of order) {
      const toIds = bins[toIdx].flatMap(it => it.ids);
      const conflicts = apart.some(set => {
        const ids = Array.from(set);
        return ids.some(id => item.ids.includes(id))
            && ids.some(id => toIds.includes(id));
      });
      if (conflicts) continue;
      fromBin.splice(fromIdx, 1);
      bins[toIdx].push(item);
      moved = true;
      break;
    }
    if (!moved) break;
  }
  const finalViolation = violates();
  return {
    groups: bins.map(b => b.flatMap(it => it.members)),
    feasible: !finalViolation,
  };
}

// ===========================================================
// GROUP_COLORS
// ===========================================================
const GROUP_COLORS = [
  { color: 'blue',         label: 'WATER',      cssVar: '--blue'        },
  { color: 'red',          label: 'PADDENSTOEL',cssVar: '--red'         },
  { color: 'yellow',       label: 'ZAND',       cssVar: '--yellow'      },
  { color: 'green',        label: 'ZEEWIER',    cssVar: '--green'       },
  { color: 'orange',       label: 'LAVA',       cssVar: '--orange'      },
  { color: 'purple',       label: 'ENDERPAREL', cssVar: '--ender'       },
  { color: 'stone',        label: 'FLES',       cssVar: '--line-strong' },
  { color: 'gold',         label: 'GOUD',       cssVar: '--gold'        },
];

// ===========================================================
// GroupsModal — pair locks + projection mode
// ===========================================================
function GroupsModal({ students, pushToast, onCancel }) {
  const visible = useMemo(() => students.filter(s => !s.hidden), [students]);
  const [mode, setMode]             = useState('size'); // 'size' | 'count'
  const [count, setCount]           = useState(4);
  const [size, setSize]             = useState(3);
  const [together, setTogether]     = useState([]);
  const [apart, setApart]           = useState([]);
  const [showLocks, setShowLocks]   = useState(false);
  const [projecting, setProjecting] = useState(false);
  const [result, setResult]         = useState(null);

  const maxGroups = Math.max(2, visible.length);
  const maxSize   = Math.max(1, visible.length);

  const derivedCount = mode === 'size'
    ? Math.max(1, Math.ceil(visible.length / Math.max(1, size)))
    : count;
  const derivedAvgSize = mode === 'count'
    ? Math.max(1, Math.round(visible.length / Math.max(1, count)))
    : size;

  const doShuffle = () => {
    const { groups, feasible } = shuffleIntoGroups(visible, derivedCount, {
      together: together.map(s => new Set(s)),
      apart:    apart.map(s => new Set(s)),
    });
    setResult({ groups });
    if (!feasible) pushToast('Beperkingen onmogelijk — pas koppels aan', 'error');
  };

  useEffect(() => { doShuffle(); /* eslint-disable-next-line */ }, []);

  useEffect(() => {
    const h = (e) => {
      if (e.key === 'Escape') {
        if (projecting) { setProjecting(false); return; }
        onCancel();
      }
    };
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  }, [projecting, onCancel]);

  const groupsView = (big = false) => (
    <div className={`group-grid ${big ? 'big' : ''}`}>
      {(result?.groups || []).map((g, i) => {
        const c = GROUP_COLORS[i % GROUP_COLORS.length];
        return (
          <div key={i} className={`group-card group-${c.color}`}>
            <div className="group-card-head">
              <span className="group-card-glyph" />
              <span>GROEP {i + 1}</span>
              <span className="group-card-sub">— {c.label}</span>
            </div>
            <ul className="group-card-list">
              {g.length === 0 && <li className="muted">(leeg)</li>}
              {g.map(m => (
                <li key={m.id}>
                  <span className="group-avatar"><Avatar student={m} /></span>
                  <span>{m.name.split(' ')[0]}</span>
                </li>
              ))}
            </ul>
          </div>
        );
      })}
    </div>
  );

  if (projecting) {
    return (
      <div className="projection-overlay">
        <button className="projection-close" onClick={() => setProjecting(false)} title="Sluit projectie (Esc)">✕</button>
        {groupsView(true)}
        <button className="btn-pixel violet projection-shuffle" onClick={doShuffle}>↻ Shuffle</button>
      </div>
    );
  }

  return (
    <div className="modal-overlay" onClick={onCancel}>
      <div className="modal wide" onClick={(e) => e.stopPropagation()} style={{ width: 'min(1400px, 98vw)', maxHeight: '92vh', display: 'flex', flexDirection: 'column' }}>
        <div className="modal-inner" style={{ overflow: 'auto', flex: 1 }}>
          <h3>Random Groepen</h3>
          <p>Verdeel {visible.length} inwoners willekeurig in groepen.</p>

          <div className="groups-controls">
            <div className="mode-toggle">
              <button
                className={`btn-pixel ${mode === 'size'  ? 'violet' : 'ghost'}`}
                style={{ padding: '8px 14px', fontSize: 11 }}
                onClick={() => setMode('size')}
              >per groep</button>
              <button
                className={`btn-pixel ${mode === 'count' ? 'violet' : 'ghost'}`}
                style={{ padding: '8px 14px', fontSize: 11 }}
                onClick={() => setMode('count')}
              >aantal groepen</button>
            </div>

            {mode === 'size' ? (
              <>
                <label style={{ fontFamily: 'var(--font-disp)', fontSize: 12, letterSpacing: 1, textTransform: 'uppercase' }}>
                  Kinderen per groep
                </label>
                <div className="qty-picker">
                  <button onClick={() => setSize(s => Math.max(1, s - 1))} disabled={size <= 1}>−</button>
                  <span className="val">{size}</span>
                  <button onClick={() => setSize(s => Math.min(maxSize, s + 1))} disabled={size >= maxSize}>+</button>
                </div>
                <span style={{ fontFamily: 'var(--font-disp)', fontSize: 11, color: 'var(--ink-faint)', letterSpacing: 1 }}>
                  → {derivedCount} groep{derivedCount === 1 ? '' : 'en'}
                </span>
              </>
            ) : (
              <>
                <label style={{ fontFamily: 'var(--font-disp)', fontSize: 12, letterSpacing: 1, textTransform: 'uppercase' }}>
                  Aantal groepen
                </label>
                <div className="qty-picker">
                  <button onClick={() => setCount(c => Math.max(2, c - 1))} disabled={count <= 2}>−</button>
                  <span className="val">{count}</span>
                  <button onClick={() => setCount(c => Math.min(maxGroups, c + 1))} disabled={count >= maxGroups}>+</button>
                </div>
                <span style={{ fontFamily: 'var(--font-disp)', fontSize: 11, color: 'var(--ink-faint)', letterSpacing: 1 }}>
                  → ~{derivedAvgSize} per groep
                </span>
              </>
            )}

            <button className="btn-pixel violet" onClick={doShuffle}>↻ Shuffle</button>
            <button className="btn-pixel ghost" onClick={() => setShowLocks(true)}>
              Koppels {(together.length + apart.length) > 0 && `(${together.length + apart.length})`}
            </button>
            <button className="btn-pixel ghost" onClick={() => setProjecting(true)}>⛶ Projectie</button>
          </div>

          <div style={{ height: 18 }} />
          {groupsView()}

          <div className="modal-actions" style={{ marginTop: 18 }}>
            <button className="btn-pixel ghost" onClick={onCancel}>Sluit</button>
          </div>
        </div>
      </div>

      {showLocks && (
        <PairLocksEditor
          students={visible}
          together={together}
          apart={apart}
          onSave={(t, a) => { setTogether(t); setApart(a); setShowLocks(false); }}
          onCancel={() => setShowLocks(false)}
        />
      )}
    </div>
  );
}

// ===========================================================
// PairLocksEditor — manage together/apart clusters
// ===========================================================
function PairLocksEditor({ students, together, apart, onSave, onCancel }) {
  const [t, setT] = useState(() => together.map(s => new Set(s)));
  const [a, setA] = useState(() => apart.map(s => new Set(s)));
  const [picking, setPicking] = useState(null);

  const startNew = (kind) => setPicking({ kind, chosen: new Set() });

  const toggleId = (id) => {
    setPicking(p => {
      const next = new Set(p.chosen);
      if (next.has(id)) next.delete(id); else next.add(id);
      return { ...p, chosen: next };
    });
  };

  const commitPick = () => {
    if (!picking || picking.chosen.size < 2) return;
    if (picking.kind === 'together') setT(arr => [...arr, picking.chosen]);
    else setA(arr => [...arr, picking.chosen]);
    setPicking(null);
  };

  const removeCluster = (kind, idx) => {
    if (kind === 'together') setT(arr => arr.filter((_, i) => i !== idx));
    else setA(arr => arr.filter((_, i) => i !== idx));
  };

  const renderClusters = (kind, arr) => (
    <div className="lock-cluster-list">
      {arr.length === 0 && <p className="muted" style={{ fontSize: 18 }}>Geen koppels.</p>}
      {arr.map((set, i) => (
        <div key={i} className="lock-cluster">
          {Array.from(set).map(id => {
            const s = students.find(x => x.id === id);
            return s ? (
              <span key={id} className="lock-chip" title={s.name}>
                <span className="group-avatar small"><Avatar student={s} /></span>
                {s.name.split(' ')[0]}
              </span>
            ) : null;
          })}
          <button className="lock-cluster-del" onClick={() => removeCluster(kind, i)} title="Verwijder">✕</button>
        </div>
      ))}
      <button className="btn-pixel ghost" style={{ marginTop: 8, fontSize: 11 }} onClick={() => startNew(kind)}>
        + Nieuwe koppel
      </button>
    </div>
  );

  return (
    <div className="modal-overlay" onClick={onCancel} style={{ zIndex: 110 }}>
      <div className="modal wide" onClick={(e) => e.stopPropagation()} style={{ width: 'min(720px, 96%)', maxHeight: '90vh', display: 'flex', flexDirection: 'column' }}>
        <div className="modal-inner" style={{ overflow: 'auto', flex: 1 }}>
          <h3>Koppels bewerken</h3>
          <p>Geef aan wie samen of net niet samen mogen.</p>

          <h4 style={{ fontFamily: 'var(--font-disp)', fontSize: 13, color: 'var(--green)', margin: '14px 0 8px', letterSpacing: 1 }}>
            ▰ MOETEN SAMEN
          </h4>
          {renderClusters('together', t)}

          <h4 style={{ fontFamily: 'var(--font-disp)', fontSize: 13, color: 'var(--danger)', margin: '20px 0 8px', letterSpacing: 1 }}>
            ▰ MOETEN APART
          </h4>
          {renderClusters('apart', a)}

          <div className="modal-actions" style={{ marginTop: 22 }}>
            <button className="btn-pixel ghost" onClick={onCancel}>Annuleer</button>
            <button className="btn-pixel violet" onClick={() => onSave(t, a)}>Opslaan</button>
          </div>
        </div>
      </div>

      {picking && (
        <div className="modal-overlay" onClick={() => setPicking(null)} style={{ zIndex: 120 }}>
          <div className="modal wide" onClick={(e) => e.stopPropagation()} style={{ width: 'min(680px, 96%)', maxHeight: '85vh', display: 'flex', flexDirection: 'column' }}>
            <div className="modal-inner" style={{ overflow: 'auto', flex: 1 }}>
              <h3>Kies inwoners ({picking.chosen.size} gekozen)</h3>
              <p>{picking.kind === 'together' ? 'Deze inwoners belanden samen in een groep.' : 'Deze inwoners worden uit elkaar gehouden.'}</p>
              <div className="pick-grid">
                {students.map(s => {
                  const on = picking.chosen.has(s.id);
                  return (
                    <button key={s.id} className={`pick-cell ${on ? 'on' : ''}`} onClick={() => toggleId(s.id)}>
                      <span className="group-avatar small"><Avatar student={s} /></span>
                      <span>{s.name.split(' ')[0]}</span>
                    </button>
                  );
                })}
              </div>
              <div className="modal-actions" style={{ marginTop: 18 }}>
                <button className="btn-pixel ghost" onClick={() => setPicking(null)}>Annuleer</button>
                <button className="btn-pixel violet" disabled={picking.chosen.size < 2}
                  style={picking.chosen.size < 2 ? { opacity: 0.5, cursor: 'not-allowed' } : null}
                  onClick={commitPick}>
                  Toevoegen
                </button>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ===========================================================
// RandomPupilModal — re-roll with no-repeats
// ===========================================================
function RandomPupilModal({ students, onOpenStudent, pushToast, onCancel }) {
  const visible = useMemo(() => students.filter(s => !s.hidden), [students]);
  const [picked, setPicked]     = useState(null);
  const [excluded, setExcluded] = useState(new Set());

  const pickOne = () => {
    const pool = visible.filter(s => !excluded.has(s.id));
    if (pool.length === 0) {
      pushToast('Iedereen is aan de beurt geweest', 'info');
      return;
    }
    const next = pool[Math.floor(Math.random() * pool.length)];
    setPicked(next);
    setExcluded(prev => new Set(prev).add(next.id));
  };

  useEffect(() => { pickOne(); /* eslint-disable-next-line */ }, []);

  const poolLeft = visible.length - excluded.size;

  return (
    <div className="modal-overlay" onClick={onCancel}>
      <div className="modal" onClick={(e) => e.stopPropagation()} style={{ width: 'min(560px, 96%)' }}>
        <div className="modal-inner" style={{ textAlign: 'center' }}>
          <h3>Random Inwoner</h3>
          <p>{poolLeft > 0 ? `${poolLeft} nog over in de pool` : 'Pool is leeg'}</p>

          {picked ? (
            <div className="random-pupil-card">
              <div className="random-avatar"><Avatar student={picked} /></div>
              <div className="random-name">{picked.name.split(' ')[0].toUpperCase()}</div>
            </div>
          ) : (
            <p className="muted">Geen keuze.</p>
          )}

          <div className="modal-actions" style={{ justifyContent: 'center', marginTop: 22, flexWrap: 'wrap' }}>
            <button className="btn-pixel violet" onClick={pickOne} disabled={poolLeft === 0}
              style={poolLeft === 0 ? { opacity: 0.5, cursor: 'not-allowed' } : null}>
              ↻ Re-roll
            </button>
            {picked && (
              <button className="btn-pixel" onClick={() => onOpenStudent(picked.id)}>
                Open profiel
              </button>
            )}
          </div>
          <div style={{ marginTop: 14 }}>
            <button
              onClick={() => { setExcluded(new Set()); pushToast('Pool gereset', 'info'); }}
              style={{ fontFamily: 'var(--font-disp)', fontSize: 11, color: 'var(--ink-faint)', letterSpacing: 1, textTransform: 'uppercase' }}>
              Reset pool
            </button>
          </div>
          <div className="modal-actions" style={{ marginTop: 16 }}>
            <button className="btn-pixel ghost" onClick={onCancel}>Sluit</button>
          </div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  ToolsPage, shuffleIntoGroups, GROUP_COLORS,
  GroupsModal, PairLocksEditor, RandomPupilModal,
});
})();
