/* Kitchen Display System — seatiq.ooniq.app/kokken
   Built for speed-of-glance in a busy kitchen, borrowing the patterns
   every dedicated KDS (Toast, Square, Fresh) converges on:
   - Tickets in a grid, OLDEST FIRST, one ticket per table/order.
   - Urgency escalates by elapsed time: normal → yellow (8 min) →
     red + pulse (15 min). Big mm:ss timer on every ticket.
   - Tap an item line = that line is made (strikethrough + ✓).
   - One big "ALT LAVET" button bumps the whole ticket off the rail.
   - Bumped tickets can be recalled (mistakes happen mid-rush).
   - New tickets flash + beep (toggleable sound).
   - Allergy warnings + notes pulled from the table's seated booking.
   Auth: same device-pairing + staff-PIN flow as /pos — kitchen writes
   run on the manager-paired session, so Firestore rules stay intact. */

const { useState: kS, useEffect: kE, useMemo: kM, useRef: kR } = React;

const KDS_STAFF_KEY = 'seatiq.pos.staff';
const KDS_WARN_MIN = 8;   // yellow after 8 minutes
const KDS_LATE_MIN = 15;  // red after 15 minutes

// Short double-beep via WebAudio — no asset, works offline.
let _kdsAudio = null;
function kdsBeep() {
  try {
    _kdsAudio = _kdsAudio || new (window.AudioContext || window.webkitAudioContext)();
    const ctx = _kdsAudio;
    [0, 0.22].forEach(offset => {
      const o = ctx.createOscillator();
      const g = ctx.createGain();
      o.connect(g); g.connect(ctx.destination);
      o.type = 'sine';
      o.frequency.value = 880;
      g.gain.setValueAtTime(0.0001, ctx.currentTime + offset);
      g.gain.exponentialRampToValueAtTime(0.18, ctx.currentTime + offset + 0.02);
      g.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + offset + 0.16);
      o.start(ctx.currentTime + offset);
      o.stop(ctx.currentTime + offset + 0.18);
    });
  } catch (e) { /* no audio permission yet — fine */ }
}

function kdsElapsedLabel(ms) {
  const s = Math.max(0, Math.floor(ms / 1000));
  const m = Math.floor(s / 60);
  return `${String(m).padStart(2, '0')}:${String(s % 60).padStart(2, '0')}`;
}

function KitchenApp() {
  const auth = window.useAuth();
  const [staffMember, setStaffMember] = kS(() => {
    try { return JSON.parse(localStorage.getItem(KDS_STAFF_KEY) || 'null'); } catch (e) { return null; }
  });

  kE(() => {
    document.documentElement.dataset.theme = 'light';
  }, []);

  if (!auth || !auth.tenantId) {
    return <div className="kds-shell"><div className="auth-spinner" style={{margin: '40vh auto'}}/></div>;
  }
  if (!staffMember) {
    const PosStaffLogin = window.PosStaffLogin;
    if (!PosStaffLogin) return <div className="kds-shell"/>;
    return <PosStaffLogin onLogin={(m) => {
      try { localStorage.setItem(KDS_STAFF_KEY, JSON.stringify(m)); } catch (e) {}
      setStaffMember(m);
    }}/>;
  }
  return <KitchenMain staffMember={staffMember} onLogoutStaff={() => {
    try { localStorage.removeItem(KDS_STAFF_KEY); } catch (e) {}
    setStaffMember(null);
  }}/>;
}

function KitchenMain({ staffMember, onLogoutStaff }) {
  const fb = window.fb;
  const auth = window.useAuth();
  const tenantId = auth.tenantId;
  const [orders, setOrders] = kS([]);
  const [reservations, setReservations] = kS([]);
  const [now, setNow] = kS(Date.now());
  const [soundOn, setSoundOn] = window.usePersistedState('seatiq.kds.sound', true, v => typeof v === 'boolean');
  const [flashIds, setFlashIds] = kS({});
  const [lastBump, setLastBump] = kS(null); // { id, tableName, pendingKeys, ts }
  const knownRef = kR(new Set());

  // 1 s tick drives the elapsed timers.
  kE(() => {
    const i = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(i);
  }, []);

  // Live open orders.
  kE(() => {
    if (!tenantId) return;
    const unsub = fb.onSnapshot(
      fb.collection(fb.db, 'tenants', tenantId, 'orders'),
      (snap) => {
        const out = [];
        snap.forEach(d => out.push({ id: d.id, ...d.data() }));
        setOrders(out);
      },
      () => {}
    );
    return () => unsub();
  }, [tenantId]);

  // Today's bookings → allergy warnings + notes per table.
  kE(() => {
    if (!tenantId) return;
    const d = new Date();
    const todayKey = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
    const q = fb.query(
      fb.collection(fb.db, 'tenants', tenantId, 'reservations'),
      fb.where('bookedFor', '==', todayKey),
    );
    const unsub = fb.onSnapshot(q, (snap) => {
      const out = [];
      snap.forEach(doc => out.push({ id: doc.id, ...doc.data() }));
      setReservations(out);
    }, () => {});
    return () => unsub();
  }, [tenantId]);

  const itemKey = (it) => `${it.itemId || it.name}|${it.addedAt || ''}`;

  // Active tickets: open orders with at least one FIRED, un-made line.
  // Lines the waiter hasn't sent yet ("Send til køkken" on POS) are
  // invisible here — the kitchen only sees confirmed orders, and the
  // timer runs from the moment they were sent.
  const tickets = kM(() => {
    return orders
      .filter(o => o.status === 'open' && (o.items || []).some(it => it.sentAt && !it.kdone))
      .map(o => {
        const items = o.items || [];
        const sent = items.filter(it => it.sentAt);
        const pending = sent.filter(it => !it.kdone);
        const doneCount = sent.length - pending.length;
        const oldest = Math.min(...pending.map(it => Date.parse(it.sentAt) || Date.now()));
        const guest = reservations.find(r => r.table === o.tableId && ['seated', 'arriving', 'late'].includes(r.status));
        return { ...o, items, pending, doneCount, oldest, guest };
      })
      .sort((a, b) => a.oldest - b.oldest);
  }, [orders, reservations]);

  // Fully-made but still-open orders → the recall rail. Only fired
  // lines count; unsent lines neither show nor block "done".
  const doneTickets = kM(() => {
    return orders
      .filter(o => {
        if (o.status !== 'open') return false;
        const sent = (o.items || []).filter(it => it.sentAt);
        return sent.length > 0 && sent.every(it => it.kdone);
      })
      .sort((a, b) => (b.lastUpdatedAt || '').localeCompare(a.lastUpdatedAt || ''))
      .slice(0, 8);
  }, [orders]);

  // New-ticket detection → flash + beep.
  const ticketSig = tickets.map(t => t.id).sort().join(',');
  kE(() => {
    const ids = new Set(tickets.map(t => t.id));
    const fresh = [...ids].filter(id => !knownRef.current.has(id));
    if (knownRef.current.size > 0 && fresh.length > 0) {
      if (soundOn) kdsBeep();
      setFlashIds(prev => {
        const next = { ...prev };
        fresh.forEach(id => { next[id] = Date.now(); });
        return next;
      });
      setTimeout(() => {
        setFlashIds(prev => {
          const cutoff = Date.now() - 3500;
          const next = {};
          Object.entries(prev).forEach(([id, ts]) => { if (ts > cutoff) next[id] = ts; });
          return next;
        });
      }, 4000);
    }
    knownRef.current = ids;
  }, [ticketSig]);

  // Toggle one line (identified by itemId+addedAt — stable even if the
  // POS reorders the array concurrently). Runs in a transaction so a
  // simultaneous POS edit can't be clobbered.
  const toggleItem = async (ticket, it) => {
    const ref = fb.doc(fb.db, 'tenants', tenantId, 'orders', ticket.id);
    const key = itemKey(it);
    try {
      await fb.runTransaction(fb.db, async (tx) => {
        const snap = await tx.get(ref);
        if (!snap.exists()) return;
        const cur = snap.data();
        const items = (cur.items || []).map(x => itemKey(x) === key
          ? (x.kdone
              ? { ...x, kdone: false }
              : { ...x, kdone: true, kdoneAt: new Date().toISOString(), kdoneBy: staffMember.name })
          : x);
        tx.set(ref, { items, lastUpdatedAt: new Date().toISOString() }, { merge: true });
      });
    } catch (e) {
      alert(e?.message || 'Kunne ikke opdatere — prøv igen.');
    }
  };

  // Bump: everything on the ticket is made.
  const bumpTicket = async (ticket) => {
    const ref = fb.doc(fb.db, 'tenants', tenantId, 'orders', ticket.id);
    const pendingKeys = ticket.pending.map(itemKey);
    try {
      await fb.runTransaction(fb.db, async (tx) => {
        const snap = await tx.get(ref);
        if (!snap.exists()) return;
        const cur = snap.data();
        // Only fired lines — anything the waiter hasn't sent yet must
        // not be marked made by a bump.
        const items = (cur.items || []).map(x => (x.kdone || !x.sentAt) ? x : ({
          ...x, kdone: true, kdoneAt: new Date().toISOString(), kdoneBy: staffMember.name,
        }));
        tx.set(ref, { items, lastUpdatedAt: new Date().toISOString() }, { merge: true });
      });
      setLastBump({ id: ticket.id, tableName: ticket.tableName || ticket.id, pendingKeys, ts: Date.now() });
      setTimeout(() => setLastBump(lb => (lb && Date.now() - lb.ts >= 7000) ? null : lb), 7500);
    } catch (e) {
      alert(e?.message || 'Kunne ikke afslutte ticket — prøv igen.');
    }
  };

  // Recall: bring lines back. keys=null → the whole ticket.
  const recallTicket = async (orderId, keys) => {
    const ref = fb.doc(fb.db, 'tenants', tenantId, 'orders', orderId);
    try {
      await fb.runTransaction(fb.db, async (tx) => {
        const snap = await tx.get(ref);
        if (!snap.exists()) return;
        const cur = snap.data();
        const items = (cur.items || []).map(x =>
          (x.sentAt && (!keys || keys.includes(itemKey(x)))) ? { ...x, kdone: false } : x);
        tx.set(ref, { items, lastUpdatedAt: new Date().toISOString() }, { merge: true });
      });
      setLastBump(null);
    } catch (e) {
      alert(e?.message || 'Kunne ikke genkalde — prøv igen.');
    }
  };

  const clock = new Date(now);
  const clockLabel = `${String(clock.getHours()).padStart(2,'0')}:${String(clock.getMinutes()).padStart(2,'0')}`;

  return (
    <div className="kds-shell">
      <header className="kds-topbar">
        <div className="kds-brand">KØKKEN<span>.</span></div>
        <div className="kds-topbar-stats">
          <span className="kds-stat"><b>{tickets.length}</b> {tickets.length === 1 ? 'ticket' : 'tickets'}</span>
          <span className="kds-stat"><b>{tickets.reduce((a, t) => a + t.pending.reduce((x, i) => x + (i.qty || 1), 0), 0)}</b> retter i kø</span>
        </div>
        <div className="kds-clock">{clockLabel}</div>
        <div className="kds-topbar-actions">
          <button className={`kds-btn-ghost ${soundOn ? 'on' : ''}`}
            title={soundOn ? 'Lyd til — klik for at slå fra' : 'Lyd fra — klik for at slå til'}
            onClick={() => setSoundOn(!soundOn)}>
            {soundOn ? '🔔' : '🔕'}
          </button>
          <span className="kds-staff">{staffMember.name}</span>
          <button className="kds-btn-ghost" onClick={onLogoutStaff}>Skift</button>
        </div>
      </header>

      <main className="kds-main">
        {tickets.length === 0 && (
          <div className="kds-empty">
            <div className="kds-empty-icon">✓</div>
            <h2>Alt er lavet</h2>
            <p>Nye bestillinger fra POS dukker op her med det samme.</p>
          </div>
        )}
        <div className="kds-grid">
          {tickets.map(t => {
            const elapsed = now - t.oldest;
            const mins = elapsed / 60000;
            const urgency = mins >= KDS_LATE_MIN ? 'late' : mins >= KDS_WARN_MIN ? 'warn' : 'ok';
            const allergies = t.guest?.allergies || [];
            return (
              <div key={t.id}
                className={`kds-ticket ${urgency} ${flashIds[t.id] ? 'flash' : ''}`}>
                <div className="kds-ticket-head">
                  <div className="kds-ticket-table">{t.tableName || t.id}</div>
                  <div className={`kds-timer ${urgency}`}>{kdsElapsedLabel(elapsed)}</div>
                </div>
                {(allergies.length > 0 || t.guest?.notes) && (
                  <div className="kds-ticket-guest">
                    {allergies.length > 0 && (
                      <div className="kds-allergy">⚠ {allergies.join(' · ')}</div>
                    )}
                    {t.guest?.notes && <div className="kds-note">{t.guest.notes}</div>}
                  </div>
                )}
                <div className="kds-ticket-items">
                  {t.pending.map(it => (
                    <button key={itemKey(it)} className="kds-item"
                      onClick={() => toggleItem(t, it)}>
                      <span className="kds-item-qty">{it.qty || 1}×</span>
                      <span className="kds-item-name">{it.name}</span>
                      <span className="kds-item-check">○</span>
                    </button>
                  ))}
                  {t.doneCount > 0 && (
                    <div className="kds-done-summary">✓ {t.doneCount} {t.doneCount === 1 ? 'linje' : 'linjer'} allerede lavet</div>
                  )}
                </div>
                <button className="kds-bump" onClick={() => bumpTicket(t)}>
                  ALT LAVET ✓
                </button>
              </div>
            );
          })}
        </div>
      </main>

      {doneTickets.length > 0 && (
        <footer className="kds-recall-rail">
          <span className="kds-recall-label">Færdige — tryk for at genkalde:</span>
          {doneTickets.map(o => (
            <button key={o.id} className="kds-recall-chip"
              onClick={() => {
                if (window.confirm(`Genkald ${o.tableName || o.id}? Alle linjer kommer tilbage på skærmen.`)) {
                  recallTicket(o.id, null);
                }
              }}>
              {o.tableName || o.id}
            </button>
          ))}
        </footer>
      )}

      {lastBump && (
        <div className="kds-undo">
          <span><b>{lastBump.tableName}</b> markeret lavet</span>
          <button onClick={() => recallTicket(lastBump.id, lastBump.pendingKeys)}>FORTRYD</button>
        </div>
      )}
    </div>
  );
}

window.KitchenApp = KitchenApp;
