// App.jsx — main entry for Snap Operation iPad app

const { useState: useApp, useEffect: useEffectApp } = React;

const LS_KEY = 'snap-op-state-v3';
const LOCKED_KEY = 'snap-locked-v1';

function loadState() {
  try {
    const s = JSON.parse(localStorage.getItem(LS_KEY) || '{}');
    return s;
  } catch { return {}; }
}

function App() {
  const [tweaks, setTweaks] = useApp(/*EDITMODE-BEGIN*/{
    "showFrame": true,
    "prepInteraction": "table",
    "closingLayout": "paper",
    "dataState": "populated"
  }/*EDITMODE-END*/);
  const [editModeOn, setEditModeOn] = useApp(false);

  const [currentStaff, setCurrentStaff] = useApp(null);
  // Auto-bind: if this iPad has a previously-bound code in localStorage AND
  // that code is still valid (not revoked), enter the app immediately. Otherwise
  // show the LockScreen and bind on first successful entry.
  // Start locked. An async effect below resolves the saved code against
  // Supabase and unlocks if it's still valid; otherwise the LockScreen stays.
  const [deviceUnlocked, setDeviceUnlocked] = useApp(false);

  const lockDevice = () => {
    try { localStorage.setItem(LOCKED_KEY, '1'); } catch {}
    setDeviceUnlocked(false);
    setCurrentStaff(null);
    setScreen('prep');
  };
  const handleUnlock = () => {
    try { localStorage.removeItem(LOCKED_KEY); } catch {}
    setDeviceUnlocked(true);
  };

  // On mount: resolve saved binding against Supabase. Skip auto-unlock if the
  // user explicitly locked the iPad.
  useEffectApp(() => {
    if (!window.supa) return;
    if (localStorage.getItem(LOCKED_KEY) === '1') return;
    const saved = localStorage.getItem('snap-bound-code-v1');
    if (!saved) return;
    let cancelled = false;
    window.supa
      .from('devices')
      .select('id,code,status')
      .eq('restaurant_id', window.RESTAURANT_ID)
      .eq('code', saved)
      .maybeSingle()
      .then(({ data }) => {
        if (cancelled) return;
        if (data && data.status !== 'revoked') {
          setDeviceUnlocked(true);
          window.supa.from('devices').update({ last_seen: new Date().toISOString() }).eq('id', data.id).then(() => {});
        } else {
          try { localStorage.removeItem('snap-bound-code-v1'); } catch {}
        }
      });
    return () => { cancelled = true; };
  }, []);

  // Realtime: if the admin portal flips this iPad's code to 'revoked' or
  // deletes the row, kick the iPad back to the lock screen immediately.
  useEffectApp(() => {
    if (!window.supa) return;
    const channel = window.supa
      .channel('ipad-devices:' + window.RESTAURANT_ID)
      .on('postgres_changes', {
        event: '*',
        schema: 'public',
        table: 'devices',
        filter: 'restaurant_id=eq.' + window.RESTAURANT_ID,
      }, (payload) => {
        const saved = localStorage.getItem('snap-bound-code-v1');
        if (!saved) return;
        const row = payload.new || payload.old;
        if (!row || row.code !== saved) return;
        if (payload.eventType === 'DELETE' || (payload.new && payload.new.status === 'revoked')) {
          try { localStorage.removeItem('snap-bound-code-v1'); } catch {}
          setDeviceUnlocked(false);
        }
      })
      .subscribe();
    return () => window.supa.removeChannel(channel);
  }, []);
  const [screen, setScreen] = useApp('prep');
  const saved = loadState();

  const initPrep = tweaks.dataState === 'empty' ? {} : { ...window.SAMPLE_PREP_PROGRESS };
  const initClosing = tweaks.dataState === 'empty' ? {} : { ...window.SAMPLE_CLOSING_PROGRESS };
  const [prepState, setPrepState] = useApp(saved.prepState ?? initPrep);
  const [closingState, setClosingState] = useApp(saved.closingState ?? initClosing);
  const [invState, setInvState] = useApp(saved.invState ?? {});
  const [orderState, setOrderState] = useApp(saved.orderState ?? {});
  // Broadcasts now live in Supabase. Local state mirrors what's in the DB so
  // existing components don't need to be rewritten. Phase 1: hard-coded
  // restaurant; Phase 2 will swap to device-bound JWT auth.
  const [broadcasts, setBroadcasts] = useApp([]);
  const [trainingCompletions, setTrainingCompletions] = useApp(saved.trainingCompletions ?? { ...window.SAMPLE_TRAINING_COMPLETIONS });
  const [arrivalId, setArrivalId] = useApp(null);
  const [arrivalAckOpen, setArrivalAckOpen] = useApp(false);

  const [toast, setToast] = useApp(null);
  const showToast = (t) => setToast({ ...t, key: Date.now() });

  useEffectApp(() => {
    localStorage.setItem(LS_KEY, JSON.stringify({ prepState, closingState, invState, orderState, trainingCompletions }));
  }, [prepState, closingState, invState, orderState, trainingCompletions]);

  // Map a Supabase row to the in-app broadcast shape the existing components expect.
  const rowToBroadcast = (r) => ({
    id: r.id,
    sentAt: r.created_at,
    senderName: r.sender_name || 'Owner',
    body: r.body,
    acks: [],
  });

  // Initial fetch + Realtime subscription for broadcasts (Phase 1).
  useEffectApp(() => {
    if (!window.supa) return;
    let cancelled = false;
    window.supa
      .from('broadcasts')
      .select('*')
      .eq('restaurant_id', window.RESTAURANT_ID)
      .order('created_at', { ascending: false })
      .limit(50)
      .then(({ data, error }) => {
        if (cancelled || error || !data) return;
        setBroadcasts(data.map(rowToBroadcast));
      });
    const channel = window.supa
      .channel('broadcasts:' + window.RESTAURANT_ID)
      .on('postgres_changes', {
        event: 'INSERT',
        schema: 'public',
        table: 'broadcasts',
        filter: 'restaurant_id=eq.' + window.RESTAURANT_ID,
      }, (payload) => {
        const bc = rowToBroadcast(payload.new);
        setBroadcasts(prev => prev.some(b => b.id === bc.id) ? prev : [bc, ...prev]);
        setArrivalId(bc.id);
      })
      .subscribe();
    return () => { cancelled = true; window.supa.removeChannel(channel); };
  }, []);

  // Tweaks wiring
  useEffectApp(() => {
    const handler = (e) => {
      if (e.data?.type === '__activate_edit_mode') setEditModeOn(true);
      if (e.data?.type === '__deactivate_edit_mode') setEditModeOn(false);
    };
    window.addEventListener('message', handler);
    window.parent?.postMessage({ type: '__edit_mode_available' }, '*');
    return () => window.removeEventListener('message', handler);
  }, []);

  const updateTweak = (k, v) => {
    const next = { ...tweaks, [k]: v };
    setTweaks(next);
    window.parent?.postMessage({ type: '__edit_mode_set_keys', edits: { [k]: v } }, '*');
  };

  // Reset sample state when toggling data state
  useEffectApp(() => {
    if (tweaks.dataState === 'empty') {
      setPrepState({}); setClosingState({}); setInvState({}); setOrderState({});
    } else {
      setPrepState({ ...window.SAMPLE_PREP_PROGRESS });
      setClosingState({ ...window.SAMPLE_CLOSING_PROGRESS });
    }
  }, [tweaks.dataState]);

  // Send a test broadcast via Supabase. Realtime will deliver it back to this
  // iPad (and any other) via the subscription above.
  const sendTestBroadcast = async () => {
    const pool = [
      "Beverage delivery moved from 11am to 2pm today. Someone needs to be available at the back door.",
      "New POS build pushed — next login will prompt for update. Takes about 30 seconds. Do it before the next rush.",
      "Staff meal menu for this week is on the fridge. Two protein options daily, pick one. Out by 9pm please.",
      "Health inspector expected this afternoon. Everyone please clean your station now and keep it tight.",
      "Freezer B running warm — move raw chicken to A and text me. Don't use B until further notice.",
      "Running low on to-go containers. Use the backup stack under the pass. Don't waste any.",
      "Jorge is out sick tonight. Mei takes wok, Bo covers expo. Let's go.",
    ];
    const body = pool[Math.floor(Math.random() * pool.length)];
    if (!window.supa) return;
    await window.supa.from('broadcasts').insert({
      restaurant_id: window.RESTAURANT_ID,
      body,
      sender_name: 'Ryan (Manager)',
    });
  };

  const openArrivalAck = () => setArrivalAckOpen(true);

  const saveArrivalAcks = (staffIds) => {
    if (!arrivalId) { setArrivalAckOpen(false); return; }
    const at = new Date().toISOString();
    let newNames = [];
    setBroadcasts(prev => prev.map(b => {
      if (b.id !== arrivalId) return b;
      const existing = new Set((b.acks || []).map(a => a.staffId));
      const merged = [...(b.acks || [])];
      staffIds.forEach(id => {
        if (!existing.has(id)) {
          merged.push({ staffId: id, at });
          const s = window.SAMPLE_STAFF.find(x => x.id === id);
          if (s) newNames.push(s.name);
        }
      });
      return { ...b, acks: merged };
    }));
    if (newNames.length > 0) {
      showToast({
        message: newNames.length === 1 ? `${newNames[0]} acknowledged` : `${newNames.length} acknowledged`,
        icon: 'check',
      });
    }
    setArrivalAckOpen(false);
    setArrivalId(null);
    setScreen('broadcasts');
  };

  // Scale iPad frame to viewport (landscape-only)
  const [scale, setScale] = useApp(1);
  useEffectApp(() => {
    const updateScale = () => {
      const bezel = tweaks.showFrame ? 48 : 0;
      const w = 744 + bezel;
      const h = 1133 + bezel;
      const pad = 40;
      const sx = (window.innerWidth - pad) / w;
      const sy = (window.innerHeight - pad) / h;
      setScale(Math.min(1, Math.min(sx, sy)));
    };
    updateScale();
    window.addEventListener('resize', updateScale);
    return () => window.removeEventListener('resize', updateScale);
  }, [tweaks.showFrame]);

  const content = !deviceUnlocked ? (
    <LockScreen onUnlock={handleUnlock} />
  ) : (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
      <StatusStrip />
      <div style={{ flex: 1, overflow: 'hidden', minHeight: 0 }}>
        {screen === 'home' && (
          <HomeScreen
            staff={currentStaff}
            onNavigate={setScreen}
            prepState={prepState}
            closingState={closingState}
          />
        )}
        {screen === 'prep' && (
          <PrepScreen
            onBack={null}
            prepState={prepState} setPrepState={setPrepState}
            interactionMode={tweaks.prepInteraction}
            setInteractionMode={(m) => updateTweak('prepInteraction', m)}
            currentStaff={currentStaff}
            showToast={showToast}
          />
        )}
        {screen === 'closing' && (
          <ClosingScreen
            onBack={null}
            closingState={closingState} setClosingState={setClosingState}
            layoutMode={tweaks.closingLayout}
            setLayoutMode={(m) => updateTweak('closingLayout', m)}
            currentStaff={currentStaff}
            showToast={showToast}
          />
        )}
        {screen === 'inventory' && (
          <InventoryScreen
            onBack={null}
            invState={invState} setInvState={setInvState}
            currentStaff={currentStaff}
            showToast={showToast}
          />
        )}
        {screen === 'ordering' && (
          <TruckOrderScreen showToast={showToast} />
        )}
        {screen === 'broadcasts' && (
          <BroadcastsScreen
            broadcasts={broadcasts}
            setBroadcasts={setBroadcasts}
            currentStaff={currentStaff}
            showToast={showToast}
          />
        )}
        {screen === 'training' && (
          <TrainingScreen
            completions={trainingCompletions}
            setCompletions={setTrainingCompletions}
            currentStaff={currentStaff}
            showToast={showToast}
          />
        )}
      </div>
      <TabBar
        screen={screen}
        onNavigate={setScreen}
        onLock={lockDevice}
        prepState={prepState}
        closingState={closingState}
        broadcasts={broadcasts}
        trainingCompletions={trainingCompletions}
        currentStaff={currentStaff}
      />
      {toast && <Toast key={toast.key} open={true} onClose={() => setToast(null)} message={toast.message} staff={toast.staff} icon={toast.icon} />}
      {arrivalId && (() => {
        const bc = broadcasts.find(b => b.id === arrivalId);
        if (!bc) return null;
        const alreadyAckedIds = (bc.acks || []).map(a => a.staffId);
        return (
          <>
            <BroadcastArrival broadcast={bc} onAcknowledge={openArrivalAck} />
            <StaffMultiPicker
              open={arrivalAckOpen}
              onClose={() => setArrivalAckOpen(false)}
              selectedIds={alreadyAckedIds}
              lockedIds={alreadyAckedIds}
              onSave={saveArrivalAcks}
              title="Who's seen this?"
              saveLabel="Mark acknowledged"
              subtitle="Select everyone who has read this broadcast"
            />
          </>
        );
      })()}
    </div>
  );

  return (
    <div style={{
      width: '100vw', height: '100vh', overflow: 'hidden',
      background: '#E5E7EB',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      position: 'relative',
    }}>
      <div style={{
        transform: `scale(${scale})`,
        transformOrigin: 'center center',
      }}>
        <IPadFrame showFrame={tweaks.showFrame}>
          {content}
        </IPadFrame>
      </div>

      {editModeOn && (
        <TweaksPanel
          tweaks={tweaks}
          onChange={updateTweak}
          onSendBroadcast={sendTestBroadcast}
          onReset={() => {
            // Wipe ALL app data so the demo starts from an empty slate.
            // Also zero out every prep target so the Daily Prep list is
            // empty until admin assigns tonight's items via "+".
            localStorage.removeItem(LS_KEY);
            localStorage.removeItem('snap-prep-custom');
            const zeroed = {};
            (window.SAMPLE_PREP_ITEMS || []).forEach(it => { zeroed[it.id] = { target: 0 }; });
            try { localStorage.setItem('snap-prep-overrides', JSON.stringify(zeroed)); } catch {}
            setPrepState({});
            setClosingState({});
            setInvState({});
            setOrderState({});
            setBroadcasts([]);
            setArrivalId(null);
            setDeviceUnlocked(false); setCurrentStaff(null); setScreen('prep');
          }}
          onLock={() => {
            try { localStorage.removeItem('snap-bound-code-v1'); } catch {}
            try { localStorage.removeItem(LOCKED_KEY); } catch {}
            setDeviceUnlocked(false);
            setCurrentStaff(null);
          }}
        />
      )}
    </div>
  );
}

function TweaksPanel({ tweaks, onChange, onReset, onLock, onSendBroadcast }) {
  return (
    <div style={{
      position: 'fixed', right: 20, bottom: 20, zIndex: 2000,
      width: 320, background: '#fff', borderRadius: 18,
      boxShadow: 'var(--shadow-pop)', border: '1px solid var(--border-1)',
      padding: 18, fontFamily: 'var(--font-ui)',
    }}>
      <div style={{ fontSize: 16, fontWeight: 800, marginBottom: 12, letterSpacing: '-0.01em' }}>Tweaks</div>

      <TweakSection label="Device frame">
        <SegRow options={[[true,'Show'], [false,'Fullscreen']]} value={tweaks.showFrame} onChange={v => onChange('showFrame', v)} />
      </TweakSection>

      <TweakSection label="Prep interaction">
        <SegRow options={[['table','Table'], ['stepper','Stepper'], ['tap','Tap'], ['sheet','Numpad']]} value={tweaks.prepInteraction} onChange={v => onChange('prepInteraction', v)} />
      </TweakSection>

      <TweakSection label="Data state">
        <SegRow options={[['populated','Populated'], ['empty','Empty']]} value={tweaks.dataState} onChange={v => onChange('dataState', v)} />
      </TweakSection>

      <TweakSection label="Switch view">
        <a
          href="Snap Operation - Web Portal.html"
          style={{
            width: '100%', height: 38, borderRadius: 10,
            background: '#FFFFFF', color: 'var(--fg-1)',
            border: '1px solid var(--border-1)',
            fontSize: 13, fontWeight: 600,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
            cursor: 'pointer', textDecoration: 'none',
            boxSizing: 'border-box',
          }}>
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="4" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>
          Open admin portal →
        </a>
      </TweakSection>

      <TweakSection label="Simulate web portal">
        <button onClick={() => onSendBroadcast()} style={{
          width: '100%', height: 38, borderRadius: 10,
          background: '#1D4ED8', color: '#FFFFFF',
          fontSize: 13, fontWeight: 600,
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
          cursor: 'pointer',
        }}>
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 10v4l10 4V6z"/></svg>
          Send test broadcast
        </button>
      </TweakSection>

      <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
        <button onClick={onLock} style={{ flex: 1, height: 34, borderRadius: 10, background: 'var(--bg-sunken)', fontSize: 12, fontWeight: 700, color: 'var(--fg-2)' }}>Force re-pair</button>
        <button onClick={onReset} style={{ flex: 1, height: 34, borderRadius: 10, background: 'var(--bg-sunken)', fontSize: 12, fontWeight: 700, color: 'var(--fg-2)' }}>Reset demo</button>
      </div>
    </div>
  );
}

const TweakSection = ({ label, children }) => (
  <div style={{ marginBottom: 10 }}>
    <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', color: 'var(--fg-3)', textTransform: 'uppercase', marginBottom: 6 }}>{label}</div>
    {children}
  </div>
);

const SegRow = ({ options, value, onChange }) => (
  <div style={{ display: 'flex', gap: 4, padding: 3, background: 'var(--bg-sunken)', borderRadius: 10 }}>
    {options.map(([v, label]) => (
      <button key={String(v)} onClick={() => onChange(v)} style={{
        flex: 1, height: 30, borderRadius: 8,
        background: value === v ? '#fff' : 'transparent',
        color: value === v ? 'var(--fg-1)' : 'var(--fg-2)',
        fontSize: 12, fontWeight: 600,
        boxShadow: value === v ? 'var(--shadow-1)' : 'none',
      }}>{label}</button>
    ))}
  </div>
);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
