// Cedar Hills · Seating chart machinery
//
// Provides:
//   useGuestList(storageKey)  — React hook holding the guest array + ops
//   GuestEditor               — screen-only editor below the sign
//   formatGuests(...)         — small helpers for display
//
// Each collection's SeatingChart sign uses the hook to drive its display,
// and renders <GuestEditor> as a sibling to the .sign (NOT inside it) so
// html2canvas's `.sign` capture skips the editor entirely.

const { useState, useEffect, useRef } = React;

const MAX_GUESTS = 200;

const DEFAULT_GUESTS = [
  { id: 'g1',  first: 'Sarah',      last: 'Anderson',  table: 3 },
  { id: 'g2',  first: 'James',      last: 'Brooks',    table: 7 },
  { id: 'g3',  first: 'Emma',       last: 'Carter',    table: 2 },
  { id: 'g4',  first: 'Michael',    last: 'Chen',      table: 5 },
  { id: 'g5',  first: 'Lily',       last: 'Davis',     table: 1 },
  { id: 'g6',  first: 'Robert',     last: 'Davis',     table: 1 },
  { id: 'g7',  first: 'Olivia',     last: 'Evans',     table: 4 },
  { id: 'g8',  first: 'Daniel',     last: 'Fischer',   table: 6 },
  { id: 'g9',  first: 'Sophia',     last: 'Garcia',    table: 8 },
  { id: 'g10', first: 'William',    last: 'Harris',    table: 3 },
  { id: 'g11', first: 'Charlotte',  last: 'Hayes',     table: 2 },
  { id: 'g12', first: 'Benjamin',   last: 'Hill',      table: 5 },
  { id: 'g13', first: 'Ava',        last: 'Jackson',   table: 4 },
  { id: 'g14', first: 'Lucas',      last: 'Kim',       table: 7 },
  { id: 'g15', first: 'Mia',        last: 'Lee',       table: 1 },
  { id: 'g16', first: 'Henry',      last: 'Martin',    table: 6 },
  { id: 'g17', first: 'Amelia',     last: 'Miller',    table: 2 },
  { id: 'g18', first: 'Theodore',   last: 'Murphy',    table: 8 },
  { id: 'g19', first: 'Harper',     last: 'Nelson',    table: 5 },
  { id: 'g20', first: 'Owen',       last: "O'Brien",   table: 3 },
  { id: 'g21', first: 'Isla',       last: 'Patel',     table: 4 },
  { id: 'g22', first: 'Sebastian',  last: 'Reed',      table: 6 },
  { id: 'g23', first: 'Ellie',      last: 'Rivera',    table: 7 },
  { id: 'g24', first: 'Jacob',      last: 'Scott',     table: 1 },
  { id: 'g25', first: 'Aurora',     last: 'Smith',     table: 8 },
  { id: 'g26', first: 'Nathan',     last: 'Taylor',    table: 2 },
  { id: 'g27', first: 'Eleanor',    last: 'Thompson',  table: 5 },
  { id: 'g28', first: 'Caleb',      last: 'Walker',    table: 3 },
  { id: 'g29', first: 'Hazel',      last: 'Williams',  table: 4 },
  { id: 'g30', first: 'Wyatt',      last: 'Young',     table: 6 },
];

function newId() {
  return 'g' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
}

// Computes inline { fontSize, lineHeight, columnCount } for the seating list
// so common-length names fit horizontally AND rows fit vertically. Uses
// fewer columns for short lists (wider per-name budget) and bumps to 5
// columns when the guest count approaches 200.
function seatingListStyle(count) {
  const c = Math.max(count, 8);
  let columnCount;
  if      (c <= 45)  columnCount = 3;
  else if (c <= 130) columnCount = 4;
  else               columnCount = 5;
  const perCol = Math.ceil(c / columnCount);
  const budget = 460;
  // Cap at 10.5px so even at 30 guests the rows don't get cartoonishly large
  // and crowd the column width (~150px) — "Hollingsworth-Bennett, Christopher"
  // still won't fit, but typical 15-20 char names do.
  const fs = Math.max(6.5, Math.min(10.5, budget / (perCol * 1.45)));
  return {
    fontSize: fs.toFixed(2) + 'px',
    lineHeight: 1.45,
    columnCount,
  };
}

function sortGuests(arr) {
  return [...arr].sort((a, b) => {
    const al = (a.last || '').toLowerCase();
    const bl = (b.last || '').toLowerCase();
    if (al !== bl) return al.localeCompare(bl);
    return (a.first || '').toLowerCase().localeCompare((b.first || '').toLowerCase());
  });
}

// Parse pasted text. Accepts these per-line shapes (each row → one guest):
//   "Last, First, 3"     ← comma-separated
//   "First Last, 3"
//   "First Last\t3"      ← TSV (Sheets/Excel default)
//   "First Last  3"      ← whitespace-delimited
// Empty lines are skipped. Unparseable lines are skipped silently.
function parseBulkPaste(text) {
  if (!text || !text.trim()) return [];
  const out = [];
  for (const raw of text.split(/\r?\n/)) {
    const line = raw.trim();
    if (!line) continue;

    // Split on tab first (spreadsheet copy), then commas, then whitespace
    let parts;
    if (line.includes('\t')) parts = line.split('\t').map((s) => s.trim()).filter(Boolean);
    else if (line.includes(',')) parts = line.split(',').map((s) => s.trim()).filter(Boolean);
    else {
      // Whitespace split — table # must be the LAST token and numeric.
      const m = line.match(/^(.+?)\s+(\d+)\s*$/);
      if (!m) continue;
      parts = [m[1].trim(), m[2]];
    }

    if (parts.length < 2) continue;
    const tableRaw = parts[parts.length - 1];
    const table = parseInt(tableRaw, 10);
    if (!isFinite(table)) continue;

    let first = '';
    let last = '';
    if (parts.length === 2) {
      // "First Last"
      const name = parts[0];
      const sp = name.lastIndexOf(' ');
      if (sp < 0) { first = name; last = ''; }
      else { first = name.slice(0, sp).trim(); last = name.slice(sp + 1).trim(); }
    } else {
      // 3+ tokens — first two are name parts, depending on order:
      // If part[0] ends with a comma in the original line ("Last, First, 3")
      // use it as last. We've already stripped commas, so check the line.
      if (/^[^,]+,/.test(line)) {
        last = parts[0];
        first = parts.slice(1, -1).join(' ');
      } else {
        // "First Last, 3" with multi-word first or last → treat first n-1 tokens as name
        const nameTokens = parts.slice(0, -1);
        const allName = nameTokens.join(' ');
        const sp = allName.lastIndexOf(' ');
        if (sp < 0) { first = allName; last = ''; }
        else { first = allName.slice(0, sp).trim(); last = allName.slice(sp + 1).trim(); }
      }
    }

    if (!first && !last) continue;
    out.push({ id: newId(), first, last, table });
  }
  return out;
}

function useGuestList(storageKey) {
  const fullKey = `cedar-hills-guests-${storageKey}`;
  const [guests, setGuests] = useState(() => {
    try {
      const raw = localStorage.getItem(fullKey);
      if (raw) {
        const parsed = JSON.parse(raw);
        if (Array.isArray(parsed) && parsed.length) return parsed;
      }
    } catch {}
    return DEFAULT_GUESTS;
  });

  // Persist on every change. Cheap — guest lists are small JSON.
  useEffect(() => {
    try { localStorage.setItem(fullKey, JSON.stringify(guests)); } catch {}
  }, [fullKey, guests]);

  const addGuest = () => {
    setGuests((arr) => (arr.length >= MAX_GUESTS ? arr : [
      ...arr,
      { id: newId(), first: '', last: '', table: 1 },
    ]));
  };
  const removeGuest = (id) => setGuests((arr) => arr.filter((g) => g.id !== id));
  const updateGuest = (id, field, value) => setGuests((arr) =>
    arr.map((g) => (g.id === id ? { ...g, [field]: field === 'table' ? value : value } : g))
  );
  const bulkAdd = (text) => {
    const incoming = parseBulkPaste(text);
    if (!incoming.length) return 0;
    setGuests((arr) => {
      const room = Math.max(0, MAX_GUESTS - arr.length);
      return [...arr, ...incoming.slice(0, room)];
    });
    return Math.min(incoming.length, MAX_GUESTS - guests.length);
  };
  const bulkReplace = (text) => {
    const incoming = parseBulkPaste(text);
    if (!incoming.length) return 0;
    setGuests(incoming.slice(0, MAX_GUESTS));
    return Math.min(incoming.length, MAX_GUESTS);
  };
  const resetDefaults = () => setGuests(DEFAULT_GUESTS);
  const clearAll = () => setGuests([]);

  return {
    guests,
    sortedGuests: sortGuests(guests),
    count: guests.length,
    isFull: guests.length >= MAX_GUESTS,
    addGuest, removeGuest, updateGuest,
    bulkAdd, bulkReplace, resetDefaults, clearAll,
  };
}

// ─────────── Bulk-paste expanding section ───────────
function BulkPaste({ onAdd, onReplace, onClose }) {
  const [text, setText] = useState('');
  const taRef = useRef(null);

  useEffect(() => { taRef.current?.focus(); }, []);

  const submit = (mode) => {
    if (!text.trim()) return;
    const n = (mode === 'replace' ? onReplace : onAdd)(text);
    if (n > 0) {
      setText('');
      onClose();
    } else {
      alert('No valid lines found. Try one per line:  First Last, 3');
    }
  };

  return (
    <div className="guest-editor__bulk">
      <label className="guest-editor__bulk-label">
        Paste from your spreadsheet — one guest per line.<br />
        <small>Try: <code>Sarah Anderson, 3</code> · <code>Anderson, Sarah, 3</code> · or tab-separated columns.</small>
      </label>
      <textarea
        ref={taRef}
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder={'Sarah Anderson, 3\nJames Brooks, 7\nEmma Carter, 2'}
        rows={6}
      />
      <div className="guest-editor__bulk-actions">
        <button type="button" className="ge-btn ge-btn--primary" onClick={() => submit('add')}>
          Add to list
        </button>
        <button type="button" className="ge-btn" onClick={() => submit('replace')}>
          Replace whole list
        </button>
        <button type="button" className="ge-btn ge-btn--ghost" onClick={onClose}>
          Cancel
        </button>
      </div>
    </div>
  );
}

// ─────────── GuestEditor (screen-only) ───────────
function GuestEditor({ gl }) {
  const { guests, count, isFull, addGuest, removeGuest, updateGuest, resetDefaults, clearAll } = gl;
  const [bulkOpen, setBulkOpen] = useState(false);

  const onReset = () => {
    if (window.confirm('Reset to the sample guest list? This replaces your current entries.')) {
      resetDefaults();
    }
  };
  const onClear = () => {
    if (window.confirm('Clear ALL guests? This empties the list.')) clearAll();
  };

  return (
    <div className="guest-editor">
      <div className="guest-editor__head">
        <div className="guest-editor__title">
          <span className="guest-editor__eyebrow">Your guest list</span>
          <span className="guest-editor__count">{count} / {MAX_GUESTS}</span>
        </div>
        <div className="guest-editor__head-actions">
          {!bulkOpen && (
            <button type="button" className="ge-btn ge-btn--ghost" onClick={() => setBulkOpen(true)}>
              Bulk paste…
            </button>
          )}
          <button type="button" className="ge-btn ge-btn--ghost" onClick={onReset}>
            Reset to sample
          </button>
          <button type="button" className="ge-btn ge-btn--ghost" onClick={onClear}>
            Clear all
          </button>
        </div>
      </div>

      {bulkOpen && (
        <BulkPaste
          onAdd={gl.bulkAdd}
          onReplace={gl.bulkReplace}
          onClose={() => setBulkOpen(false)}
        />
      )}

      <div className="guest-editor__cols-head">
        <span>First name</span>
        <span>Last name</span>
        <span>Table</span>
        <span aria-hidden="true" />
      </div>
      <div className="guest-editor__rows">
        {guests.map((g) => (
          <div key={g.id} className="guest-editor__row">
            <input
              type="text"
              value={g.first}
              onChange={(e) => updateGuest(g.id, 'first', e.target.value)}
              placeholder="First"
            />
            <input
              type="text"
              value={g.last}
              onChange={(e) => updateGuest(g.id, 'last', e.target.value)}
              placeholder="Last"
            />
            <input
              type="number"
              min="1"
              value={g.table}
              onChange={(e) => updateGuest(g.id, 'table', parseInt(e.target.value, 10) || 0)}
            />
            <button
              type="button"
              className="guest-editor__x"
              onClick={() => removeGuest(g.id)}
              aria-label={`Remove ${g.first} ${g.last}`}
              title="Remove"
            >×</button>
          </div>
        ))}
      </div>

      <div className="guest-editor__foot">
        <button
          type="button"
          className="ge-btn ge-btn--primary"
          onClick={addGuest}
          disabled={isFull}
        >
          + Add guest
        </button>
        {isFull && <span className="guest-editor__note">Max {MAX_GUESTS} guests reached.</span>}
      </div>
    </div>
  );
}

Object.assign(window, { useGuestList, GuestEditor, MAX_GUESTS, seatingListStyle });

// ═══════════════════════════════════════════════════════════════
// TABLE NUMBERS — hook + screen-only stepper
// ═══════════════════════════════════════════════════════════════
// One sign, one big number. The stepper sits as a sibling of `.sign` so
// html2canvas / print only see the sign itself. Workflow:
//   set total table count → print/PDF → ▶ → print/PDF → repeat.
// Table count persists in localStorage; current number does not.

const MAX_TABLES = 50;

function useTableNumber(storageKey, { defaultCount = 12 } = {}) {
  const fullKey = `cedar-hills-tables-${storageKey}`;
  const [count, setCount] = useState(() => {
    try {
      const raw = localStorage.getItem(fullKey);
      const n = raw ? parseInt(raw, 10) : NaN;
      if (isFinite(n) && n >= 1 && n <= MAX_TABLES) return n;
    } catch {}
    return defaultCount;
  });
  const [n, setN] = useState(1);

  useEffect(() => {
    try { localStorage.setItem(fullKey, String(count)); } catch {}
  }, [fullKey, count]);

  // Keep n inside [1, count] when count shrinks
  useEffect(() => { if (n > count) setN(count); }, [count, n]);

  const safeCount = (v) => {
    const x = parseInt(v, 10);
    if (!isFinite(x)) return 1;
    return Math.max(1, Math.min(MAX_TABLES, x));
  };

  return {
    n, count,
    setN: (v) => setN(Math.max(1, Math.min(count, parseInt(v, 10) || 1))),
    setCount: (v) => setCount(safeCount(v)),
    next: () => setN((x) => Math.min(count, x + 1)),
    prev: () => setN((x) => Math.max(1, x - 1)),
    first: () => setN(1),
    last:  () => setN(count),
  };
}

function TableNumberStepper({ tn, label = 'Table' }) {
  const { n, count, setN, setCount, next, prev, first, last } = tn;
  return (
    <div className="tn-editor">
      <div className="tn-editor__head">
        <span className="tn-editor__eyebrow">Table numbers</span>
        <span className="tn-editor__count">
          Showing {label.toLowerCase()} <strong>{n}</strong> of {count}
        </span>
      </div>
      <div className="tn-editor__row">
        <div className="tn-editor__step">
          <button type="button" className="ge-btn ge-btn--ghost" onClick={first} disabled={n <= 1} title="First">|◀</button>
          <button type="button" className="ge-btn" onClick={prev} disabled={n <= 1} aria-label="Previous">◀</button>
          <input
            type="number"
            className="tn-editor__n"
            min="1"
            max={count}
            value={n}
            onChange={(e) => setN(e.target.value)}
            aria-label="Current table number"
          />
          <button type="button" className="ge-btn" onClick={next} disabled={n >= count} aria-label="Next">▶</button>
          <button type="button" className="ge-btn ge-btn--ghost" onClick={last} disabled={n >= count} title="Last">▶|</button>
        </div>
        <label className="tn-editor__count-field">
          <span>How many tables?</span>
          <input
            type="number"
            min="1"
            max={MAX_TABLES}
            value={count}
            onChange={(e) => setCount(e.target.value)}
          />
        </label>
      </div>
      <div className="tn-editor__tip">
        Print or save the current card, then click <strong>▶</strong> to advance — one card per table.
      </div>
    </div>
  );
}

Object.assign(window, { useTableNumber, TableNumberStepper, MAX_TABLES });