// CrewMealScreen.jsx — iPad Crew Meal logging (v10.00).
//
// Crew pick their meal from admin-configured groups (crew_meal_groups /
// crew_meal_items — required groups say "Choose one", optional ones say
// "Optional"), then tap "Log meal" → StaffPicker ("Who's eating?") →
// PIN confirm (CmPinSheet, a copy of TrainingScreen's PinConfirmSheet)
// → insert into crew_meal_logs with a full { group, item } name snapshot
// so later menu edits never rewrite history. Today's logged meals render
// below the pickers, newest first, kept live via realtime.
//
// All top-level idents are cm/Cm-prefixed — Babel-standalone scripts
// share one global scope (HANDBOOK §9), so nothing here may collide
// with other components' top-level consts.

const { useState: useCm, useEffect: useCmEffect, useMemo: useCmMemo, useRef: useCmRef } = React;

// Selected-card palette — same amber "pending" family as ChecklistScreen.
const CM_SEL_PALETTE = { bg: '#FEF3C7', border: '#F59E0B', text: '#92400E' };

// Device-local YYYY-MM-DD (same helper every screen carries — matches
// the portal's isoDate() so day-keyed tables agree across apps).
const cmComputeKey = () => {
  const d = new Date();
  return d.getFullYear() + '-' +
    String(d.getMonth() + 1).padStart(2, '0') + '-' +
    String(d.getDate()).padStart(2, '0');
};

// Client-generated UUID for crew_meal_logs rows so the optimistic local
// entry and its realtime INSERT echo share one identity (dedupe), same
// rationale as PrepScreen's newCompletionId.
const cmNewLogId = () => {
  if (window.crypto && window.crypto.randomUUID) return window.crypto.randomUUID();
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
    const r = Math.random() * 16 | 0;
    return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
  });
};

// "Entree" -> "an entree", "Rice" -> "a rice" — for the CTA hint line.
const cmArticleFor = (name) => {
  const n = String(name || 'option').trim().toLowerCase();
  return (/^[aeiou]/.test(n) ? 'an ' : 'a ') + n;
};

const cmFmtTime = (iso) =>
  new Date(iso).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });

const CrewMealScreen = ({ currentStaff, showToast }) => {
  // Today's local date key, re-checked every 30s so the screen rolls
  // over at midnight without a manual reload (PrepScreen pattern).
  const [todayKey, setTodayKey] = useCm(cmComputeKey);
  useCmEffect(() => {
    const t = setInterval(() => {
      const k = cmComputeKey();
      setTodayKey(prev => (prev === k ? prev : k));
    }, 30 * 1000);
    return () => clearInterval(t);
  }, []);

  // ----------------------------------------------------------------
  // Config: groups + items. Fetched on mount; archived filtering stays
  // client-side. One realtime channel per table that simply refetches —
  // config is tiny, so a refetch beats event patching.
  // ----------------------------------------------------------------
  const [cmGroups, setCmGroups] = useCm([]);
  const [cmItems, setCmItems] = useCm([]);
  useCmEffect(() => {
    if (!window.supa) return;
    let cancelled = false;
    const refetchConfig = async () => {
      const [g, i] = await Promise.all([
        window.supa.from('crew_meal_groups').select('*')
          .eq('restaurant_id', window.RESTAURANT_ID).order('sort_order'),
        window.supa.from('crew_meal_items').select('*')
          .eq('restaurant_id', window.RESTAURANT_ID).order('sort_order'),
      ]);
      if (cancelled) return;
      if (g.error) console.error('crew_meal_groups select failed', g.error);
      else setCmGroups(g.data || []);
      if (i.error) console.error('crew_meal_items select failed', i.error);
      else setCmItems(i.data || []);
    };
    refetchConfig();
    const chGroups = window.supa
      .channel('ipad-crew-meal-groups:' + window.RESTAURANT_ID)
      .on('postgres_changes', {
        event: '*', schema: 'public', table: 'crew_meal_groups',
        filter: 'restaurant_id=eq.' + window.RESTAURANT_ID,
      }, () => refetchConfig())
      .subscribe();
    const chItems = window.supa
      .channel('ipad-crew-meal-items:' + window.RESTAURANT_ID)
      .on('postgres_changes', {
        event: '*', schema: 'public', table: 'crew_meal_items',
        filter: 'restaurant_id=eq.' + window.RESTAURANT_ID,
      }, () => refetchConfig())
      .subscribe();
    return () => {
      cancelled = true;
      window.supa.removeChannel(chGroups);
      window.supa.removeChannel(chItems);
    };
  }, []);

  const cmActiveGroups = useCmMemo(() => (
    cmGroups
      .filter(g => !g.archived)
      .slice()
      .sort((a, b) => (Number(a.sort_order) || 0) - (Number(b.sort_order) || 0))
  ), [cmGroups]);
  const cmItemsByGroup = useCmMemo(() => {
    const m = {};
    cmItems.filter(i => !i.archived).forEach(i => {
      (m[i.group_id] = m[i.group_id] || []).push(i);
    });
    Object.values(m).forEach(arr =>
      arr.sort((a, b) => (Number(a.sort_order) || 0) - (Number(b.sort_order) || 0)));
    return m;
  }, [cmItems]);

  // ----------------------------------------------------------------
  // Today's logged meals — fetched per day, newest first, kept live.
  // DELETE handled by id (portal-side removals disappear); INSERT
  // deduped by id against our own optimistic rows.
  // ----------------------------------------------------------------
  const [cmLogs, setCmLogs] = useCm([]);
  useCmEffect(() => {
    if (!window.supa) return;
    let cancelled = false;
    window.supa.from('crew_meal_logs').select('*')
      .eq('restaurant_id', window.RESTAURANT_ID)
      .eq('the_date', todayKey)
      .order('eaten_at', { ascending: false })
      .then(({ data, error }) => {
        if (error) { console.error('crew_meal_logs select failed', error); return; }
        if (cancelled || !data) return;
        setCmLogs(data);
      });
    const ch = window.supa
      .channel('ipad-crew-meal-logs:' + window.RESTAURANT_ID + ':' + todayKey)
      .on('postgres_changes', {
        event: '*', schema: 'public', table: 'crew_meal_logs',
        filter: 'restaurant_id=eq.' + window.RESTAURANT_ID,
      }, (payload) => {
        if (payload.eventType === 'DELETE') {
          // DELETE payloads only carry the PK — match by id, no date check.
          const gone = payload.old;
          if (gone && gone.id) setCmLogs(prev => prev.filter(l => l.id !== gone.id));
          return;
        }
        const row = payload.new;
        if (!row || row.the_date !== todayKey) return; // date filtered client-side
        setCmLogs(prev => {
          if (prev.some(l => l.id === row.id)) {
            return prev.map(l => (l.id === row.id ? row : l)); // echo of our optimistic row
          }
          if (payload.eventType === 'INSERT') return [row, ...prev];
          return prev;
        });
      })
      .subscribe();
    return () => { cancelled = true; window.supa.removeChannel(ch); };
  }, [todayKey]);

  // ----------------------------------------------------------------
  // Selection — { [groupId]: itemId }, one pick per group, local-only
  // until the meal is logged. Tapping the selected card deselects.
  // ----------------------------------------------------------------
  const [cmSel, setCmSel] = useCm({});
  const cmToggleItem = (groupId, itemId) => setCmSel(prev => {
    const next = { ...prev };
    if (next[groupId] === itemId) delete next[groupId];
    else next[groupId] = itemId;
    return next;
  });

  // Drop selections whose group/item vanished (archived or deleted via
  // a config refetch) so the CTA never submits a stale pick. If that
  // happens while the staff/PIN sheets are already up, abort the flow —
  // otherwise a confirmed PIN would log a partial (or empty) meal.
  useCmEffect(() => {
    let changed = false;
    const next = {};
    Object.keys(cmSel).forEach(gid => {
      const alive = cmActiveGroups.some(g => g.id === gid) &&
        (cmItemsByGroup[gid] || []).some(i => i.id === cmSel[gid]);
      if (alive) next[gid] = cmSel[gid];
      else changed = true;
    });
    if (!changed) return;
    setCmSel(next);
    if (phase !== 'pick') {
      setPhase('pick');
      setPinStaff(null);
      showToast && showToast({ message: 'The menu just changed — please re-pick your meal', icon: 'x' });
    }
  }, [cmActiveGroups, cmItemsByGroup, cmSel, phase]);

  const cmSelCount = Object.keys(cmSel).length;
  // Selected item names, in group sort order (drives the picker subtitle
  // and the snapshot written to crew_meal_logs.items).
  const cmSelectedPicks = cmActiveGroups
    .map(g => {
      const it = (cmItemsByGroup[g.id] || []).find(i => i.id === cmSel[g.id]);
      return it ? { group_id: g.id, group_name: g.name, item_id: it.id, item_name: it.name } : null;
    })
    .filter(Boolean);
  // First required group that has options available but no pick yet —
  // blocks the primary CTA until satisfied.
  const cmMissingRequired = cmActiveGroups.find(g =>
    g.required && (cmItemsByGroup[g.id] || []).length > 0 && !cmSel[g.id]) || null;

  // ----------------------------------------------------------------
  // Submit flow — phase machine: 'pick' → 'staff' → 'pin'.
  // ----------------------------------------------------------------
  const [phase, setPhase] = useCm('pick');
  const [pinStaff, setPinStaff] = useCm(null);

  const cmOpenStaff = () => {
    if (cmSelCount === 0 || cmMissingRequired) return;
    setPhase('staff');
  };

  const cmHandlePick = (staff) => {
    if (!staff) return;
    if (!staff.pin) {
      // Stay on the picker — they can grab someone else or bail.
      showToast && showToast({ message: staff.name + ' has no PIN set — ask a manager', icon: 'x' });
      return;
    }
    setPinStaff(staff);
    setPhase('pin');
  };

  const cmSubmitMeal = async (staff) => {
    setPhase('pick');
    setPinStaff(null);
    const snapshot = cmSelectedPicks;
    if (!staff) return;
    // Config can change while the sheets are up (realtime + pruning
    // effect above). Never end a confirmed PIN in a silent no-op or a
    // meal missing a required slot — tell the crew member to re-pick.
    if (snapshot.length === 0 || cmMissingRequired) {
      showToast && showToast({ message: 'The menu just changed — please re-pick your meal', icon: 'x' });
      return;
    }
    const id = cmNewLogId();
    const row = {
      id,
      restaurant_id: window.RESTAURANT_ID,
      the_date: todayKey,
      staff_id: staff.id,
      items: snapshot,
      eaten_at: new Date().toISOString(),
    };
    // Optimistic: show the meal in today's list immediately.
    setCmLogs(prev => [row, ...prev]);
    setCmSel({});
    if (!window.supa) return;
    const { error } = await window.supa.from('crew_meal_logs').insert(row);
    if (error) {
      console.error('crew_meal_logs insert failed', error);
      setCmLogs(prev => prev.filter(l => l.id !== id));
      showToast && showToast({ message: 'Failed to save — try again', icon: 'x' });
    } else {
      // No success toast — the full-screen celebration already confirms
      // the save (checklist precedent).
      window.celebrateCompletion && window.celebrateCompletion();
    }
  };

  return (
    <div style={{
      height: '100%', display: 'flex', flexDirection: 'column',
      background: 'var(--bg-page)', position: 'relative',
    }}>
      {/* Header — same shape as Checklist / Temp Log / Delivery. */}
      <div style={{
        padding: '16px 24px',
        background: 'var(--bg-surface)',
        borderBottom: '1px solid var(--border-1)',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 11, color: 'var(--fg-3)', fontWeight: 500, letterSpacing: '0.06em', textTransform: 'uppercase' }}>
              {window.fmtTodayLabel()}
            </div>
            <div data-reload-tap style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.02em', color: 'var(--fg-1)', marginTop: 2, lineHeight: 1.1 }}>
              Crew Meal
            </div>
          </div>
          <div style={{ fontSize: 14, color: 'var(--fg-2)', fontFamily: 'var(--font-num)' }}>
            <strong style={{ color: 'var(--fg-1)' }}>{cmLogs.length}</strong> today
          </div>
        </div>
      </div>

      {/* Scrollable body — group pickers, then today's logged meals. */}
      <div style={{
        flex: 1, minHeight: 0, overflowY: 'auto',
        padding: '16px 32px',
        // Reserve room for the floating Log-meal bar (which can carry an
        // extra hint line) so the last card never hides under it.
        paddingBottom: cmSelCount > 0 ? 140 : 24,
      }}>
        {cmActiveGroups.length === 0 ? (
          <div style={{ padding: '60px 20px', textAlign: 'center', color: 'var(--fg-3)' }}>
            <div style={{ fontSize: 15 }}>
              Crew Meal isn't set up yet — add options in the admin portal.
            </div>
          </div>
        ) : (
          cmActiveGroups.map(group => (
            <CmGroupCard
              key={group.id}
              group={group}
              items={cmItemsByGroup[group.id] || []}
              selectedId={cmSel[group.id]}
              onTapItem={cmToggleItem}
            />
          ))
        )}

        {(cmActiveGroups.length > 0 || cmLogs.length > 0) && (
          <>
            <div style={{
              margin: '26px 2px 10px',
              fontSize: 12, fontWeight: 600, letterSpacing: '0.06em',
              textTransform: 'uppercase', color: 'var(--fg-3)',
            }}>
              Logged today
            </div>
            {cmLogs.length === 0 ? (
              <div style={{
                padding: 18, textAlign: 'center',
                fontSize: 13, color: 'var(--fg-3)',
                border: '1.5px dashed var(--border-1)', borderRadius: 16,
              }}>
                No crew meals logged yet today.
              </div>
            ) : (
              <div style={{
                background: 'var(--bg-surface)',
                border: '1px solid var(--border-2)',
                borderRadius: 16, overflow: 'hidden',
              }}>
                {cmLogs.map((log, idx) => (
                  <CmLogRow key={log.id} log={log} isLast={idx === cmLogs.length - 1} />
                ))}
              </div>
            )}
          </>
        )}
      </div>

      {/* Floating Log-meal CTA — shows only when something is selected. */}
      {cmSelCount > 0 && (
        <div style={{
          position: 'absolute', left: 0, right: 0, bottom: 0,
          padding: '14px 32px calc(20px + env(safe-area-inset-bottom))',
          background: 'rgba(255,255,255,0.94)',
          backdropFilter: 'saturate(180%) blur(20px)',
          WebkitBackdropFilter: 'saturate(180%) blur(20px)',
          borderTop: '1px solid var(--border-1)',
          display: 'flex', flexDirection: 'column', gap: 10,
        }}>
          {cmMissingRequired && (
            <div style={{ fontSize: 12.5, fontWeight: 500, color: 'var(--fg-3)', textAlign: 'center' }}>
              Pick {cmArticleFor(cmMissingRequired.name)} first
            </div>
          )}
          <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
            <button
              onClick={() => setCmSel({})}
              style={{
                padding: '12px 18px', borderRadius: 12,
                fontSize: 15, fontWeight: 500,
                background: 'var(--bg-sunken)', color: 'var(--fg-2)',
                border: 'none', cursor: 'pointer',
              }}
            >Cancel</button>
            <button
              onClick={cmOpenStaff}
              disabled={!!cmMissingRequired}
              style={{
                flex: 1, height: 56, borderRadius: 12,
                background: '#1D4ED8', color: '#FFFFFF',
                fontSize: 17, fontWeight: 600,
                letterSpacing: '-0.01em',
                border: 'none',
                cursor: cmMissingRequired ? 'default' : 'pointer',
                opacity: cmMissingRequired ? 0.5 : 1,
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 10,
                boxShadow: '0 6px 18px rgba(29,78,216,0.35)',
              }}
            >
              <Icon name="check" size={18} color="#fff" strokeWidth={2.4} />
              Log meal
            </button>
          </div>
        </div>
      )}

      <StaffPicker
        open={phase === 'staff'}
        onClose={() => setPhase('pick')}
        onPick={cmHandlePick}
        title="Who's eating?"
        subtitle={cmSelectedPicks.map(p => p.item_name).join(' · ')}
      />

      {phase === 'pin' && pinStaff && (
        <CmPinSheet
          title="Confirm it's you"
          subtitle={<><b>{pinStaff.name}</b>, enter your PIN to log this meal.</>}
          who={pinStaff}
          expectedPin={pinStaff.pin}
          onCancel={() => setPhase('staff')}
          onSuccess={() => cmSubmitMeal(pinStaff)}
        />
      )}
    </div>
  );
};

// ------------------------------------------------------------------
// One group's card — name + "Choose one"/"Optional" caption, then a
// 2-col grid of big tappable item cards (amber when selected).
// ------------------------------------------------------------------
const CmGroupCard = ({ group, items, selectedId, onTapItem }) => (
  <div style={{
    background: 'var(--bg-surface)',
    border: '1px solid var(--border-2)',
    borderRadius: 16,
    marginBottom: 14,
    overflow: 'hidden',
  }}>
    <div style={{ padding: '14px 18px 0', display: 'flex', alignItems: 'baseline', gap: 10 }}>
      <div style={{ fontSize: 17, fontWeight: 600, color: 'var(--fg-1)', letterSpacing: '-0.01em' }}>
        {group.name}
      </div>
      <div style={{ fontSize: 12, color: 'var(--fg-3)', fontWeight: 500 }}>
        {group.required ? 'Choose one' : 'Optional'}
      </div>
    </div>
    {items.length === 0 ? (
      <div style={{
        margin: '12px 18px 16px', padding: 16,
        border: '1.5px dashed var(--border-1)', borderRadius: 12,
        textAlign: 'center', fontSize: 13, color: 'var(--fg-3)',
      }}>
        No options yet
      </div>
    ) : (
      <div style={{
        display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10,
        padding: '12px 18px 16px',
      }}>
        {items.map(item => {
          const selected = selectedId === item.id;
          return (
            <button
              key={item.id}
              onClick={() => onTapItem(group.id, item.id)}
              style={{
                minHeight: 64, borderRadius: 14, padding: '10px 14px',
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                background: selected ? CM_SEL_PALETTE.bg : '#FFFFFF',
                border: selected
                  ? '2px solid ' + CM_SEL_PALETTE.border
                  : '2px solid var(--border-1)',
                color: selected ? CM_SEL_PALETTE.text : 'var(--fg-1)',
                fontSize: 16, fontWeight: selected ? 600 : 500,
                letterSpacing: '-0.005em', textAlign: 'center',
                cursor: 'pointer',
                transition: 'background 120ms ease, border-color 120ms ease, color 120ms ease',
              }}
            >
              {item.name}
            </button>
          );
        })}
      </div>
    )}
  </div>
);

// ------------------------------------------------------------------
// One logged-meal row — avatar, who, what, when (newest first).
// ------------------------------------------------------------------
const CmLogRow = ({ log, isLast }) => {
  const staff = (window.SAMPLE_STAFF || []).find(s => s.id === log.staff_id)
    || { name: 'Unknown', initials: '?', color: '#94A3B8' };
  const names = (log.items || []).map(x => x.item_name).join(' · ');
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 12,
      padding: '12px 16px',
      borderBottom: isLast ? 'none' : '1px solid var(--border-2)',
    }}>
      <Avatar staff={staff} size={36} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--fg-1)', letterSpacing: '-0.005em' }}>
          {staff.name}
        </div>
        <div style={{
          fontSize: 13, color: 'var(--fg-2)', marginTop: 1,
          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
        }}>
          {names || '—'}
        </div>
      </div>
      <div style={{ fontSize: 13, color: 'var(--fg-3)', fontFamily: 'var(--font-num)', flexShrink: 0 }}>
        {cmFmtTime(log.eaten_at)}
      </div>
    </div>
  );
};

// ------------------------------------------------------------------
// PIN confirmation sheet — copy of TrainingScreen's PinConfirmSheet
// (file-local there, not on window), with the shake keyframes renamed
// cmPinShake so the two never collide.
// ------------------------------------------------------------------
const CmPinSheet = ({ title, subtitle, who, expectedPin, onCancel, onSuccess, accent = '#1D4ED8' }) => {
  const [pin, setPin] = useCm('');
  const [err, setErr] = useCm(false);
  const [shake, setShake] = useCm(false);

  const tryPin = (p) => {
    if (p === expectedPin) {
      onSuccess();
    } else {
      setErr(true); setShake(true);
      setTimeout(() => { setPin(''); setShake(false); }, 480);
      setTimeout(() => setErr(false), 2000);
    }
  };

  const dots = Array.from({ length: 4 }).map((_, i) => (
    <div key={i} style={{
      width: 18, height: 18, borderRadius: 999,
      background: i < pin.length ? 'var(--fg-1)' : 'transparent',
      border: '2px solid ' + (i < pin.length ? 'var(--fg-1)' : 'var(--border-1)'),
      transition: 'all 140ms var(--ease-snap)',
    }} />
  ));

  return (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 70,
      background: 'rgba(0,0,0,0.40)',
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
      animation: 'fade-in 180ms var(--ease-out)',
    }}>
      <div style={{
        width: '100%',
        background: '#FFFFFF',
        borderTopLeftRadius: 24, borderTopRightRadius: 24,
        padding: '18px 28px 28px',
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 18,
        animation: 'slide-up 280ms var(--ease-snap)',
        boxShadow: '0 -10px 40px rgba(0,0,0,0.18)',
      }}>
        <div style={{ width: 40, height: 4, background: 'var(--border-2)', borderRadius: 999, marginTop: -4 }} />
        <div style={{ textAlign: 'center', maxWidth: 440 }}>
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: accent }}>
            {title}
          </div>
          <div style={{ fontSize: 19, fontWeight: 500, color: 'var(--fg-1)', letterSpacing: '-0.01em', marginTop: 8, lineHeight: 1.4 }}>
            {subtitle}
          </div>
        </div>
        <Avatar staff={who} size={56} />
        <div style={{ display: 'flex', gap: 22, animation: shake ? 'cmPinShake 450ms' : 'none' }}>{dots}</div>
        <div style={{ fontSize: 13, height: 16, color: err ? 'var(--danger)' : 'var(--fg-3)', fontWeight: 500 }}>
          {err ? 'PIN does not match' : ''}
        </div>
        <NumPad value={pin} onChange={setPin} onEnter={tryPin} maxLen={4} big />
        <button onClick={onCancel} style={{
          padding: '12px 24px', marginTop: 4, fontSize: 14, fontWeight: 500, color: 'var(--fg-2)',
        }}>Cancel</button>
      </div>
      <style>{`
        @keyframes cmPinShake {
          0%, 100% { transform: translateX(0); }
          20% { transform: translateX(-8px); }
          40% { transform: translateX(8px); }
          60% { transform: translateX(-4px); }
          80% { transform: translateX(4px); }
        }
      `}</style>
    </div>
  );
};

Object.assign(window, { CrewMealScreen });
