/* ============ MAKI · MASACA — Consola de Operador ============ */
/* RF-1.1 round-robin · RF-1.2 validación mandatoria · RF-1.3 alergias
   RF-2.1 prioridad VIP · RF-2.2 bypass de voz · §6 Maxirest offline   */

const useCallback = React.useCallback;
const ROLL = "Round-robin activo";

const PAY_METHODS = {
  CASH:          { l: "Efectivo",      i: "cash" },
  BANK_TRANSFER: { l: "Transferencia", i: "card" },
  CARD:          { l: "Tarjeta",       i: "card" },
  MP:            { l: "Mercado Pago",   i: "card" },
};

function opName(id, overrideName) {
  if (overrideName) return overrideName;
  return (MAKI.OPERATORS.find(o => o.id === id) || {}).name || "—";
}
function chatTotal(c) { return c.items.reduce((s, i) => s + i.price * i.qty, 0); }
function waitTone(sec) {
  if (sec < 30) return { c: "var(--wasabi)", bg: "var(--wasabi-soft)" };
  if (sec < 60) return { c: "var(--st-preparacion)", bg: "var(--st-preparacion-bg)" };
  return { c: "#EF4444", bg: "#FEE2E2" };
}
function mmss(sec) { const m = Math.floor(sec / 60), s = sec % 60; return `${m}:${String(s).padStart(2, "0")}`; }
function mkItems(list) { return list.map(([name, qty]) => ({ name, qty, price: (MAKI.MENU[name] || { p: 0 }).p })); }

/* —— Ítem de la cola —— */
function QueueItem({ chat, active, onClick }) {
  const tone = waitTone(chat.waitSec);
  const unanswered = chat.firstResponseSec == null;
  const displayName = chat.assignedToName || (chat.assignedTo ? opName(chat.assignedTo) : null);
  return (
    <button onClick={onClick} style={{
      width: "100%", textAlign: "left", border: `1px solid ${active ? cv("--primary") : cv("--line")}`,
      background: active ? cv("--primary-tint") : cv("--surface"), borderRadius: 12, padding: "10px 11px",
      cursor: "pointer", display: "flex", flexDirection: "column", gap: 7, position: "relative",
      boxShadow: active ? "var(--sh-1)" : "none", transition: "border-color .12s, background .12s",
      borderLeft: chat.priority ? "3px solid var(--primary)" : undefined,
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <Avatar name={chat.customer} type={chat.segment === "B2B_CORPORATE" ? "empresa" : undefined} size={30} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13, fontWeight: 750, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{chat.customer}</div>
          <div style={{ fontSize: 11, color: cv("--ink-3"), fontWeight: 600 }}>
            {displayName
              ? <><span style={{ width: 5, height: 5, borderRadius: "50%", background: cv("--wasabi"), display: "inline-block", marginRight: 4 }} />{displayName}</>
              : "En cola general"}
          </div>
        </div>
        <ChannelTag channel={chat.channel} compact />
      </div>
      <div style={{ fontSize: 11.5, color: cv("--ink-2"), fontWeight: 550, lineHeight: 1.35, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", fontStyle: "italic" }}>"{chat.preview}"</div>
      <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
        {chat.segment !== "B2C_REGULAR" && <SegmentTag segment={chat.segment} size="sm" />}
        {chat.allergens.length > 0 && (
          <span style={{ display: "inline-flex", alignItems: "center", gap: 3, color: "#EF4444", fontSize: 10.5, fontWeight: 800 }}>
            <Icon name="alert" size={11} stroke={2.6} /> Alérgico
          </span>
        )}
        <div style={{ flex: 1 }} />
        <span style={{ display: "inline-flex", alignItems: "center", gap: 4, padding: "2px 7px", borderRadius: 99, background: tone.bg, color: tone.c, fontSize: 11, fontWeight: 800, fontVariantNumeric: "tabular-nums" }}>
          <Icon name="clock" size={11} stroke={2.5} /> {mmss(chat.waitSec)}{unanswered ? " · sin resp." : ""}
        </span>
      </div>
    </button>
  );
}

/* —— Ítem de la cola — procesado —— */
function ProcessedItem({ chat, confirmedState, onClick, active }) {
  return (
    <button onClick={onClick} style={{
      width: "100%", textAlign: "left", border: `1px solid ${active ? cv("--wasabi") : cv("--line")}`,
      borderRadius: 12, padding: "10px 11px", cursor: "pointer",
      background: confirmedState === "injected" ? cv("--wasabi-soft") : "#EFF6FF",
      display: "flex", flexDirection: "column", gap: 5,
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <Avatar name={chat.customer} size={28} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 12.5, fontWeight: 750, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{chat.customer}</div>
        </div>
        <span style={{ fontSize: 10.5, fontWeight: 800, padding: "2px 7px", borderRadius: 99, whiteSpace: "nowrap",
          background: confirmedState === "injected" ? cv("--wasabi") : "#3B82F6",
          color: "#fff" }}>
          {confirmedState === "injected" ? "✓ Inyectado" : "↻ Reintento"}
        </span>
      </div>
      <div style={{ fontSize: 11, color: cv("--ink-3"), whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
        {chat.items.map(it => `${it.qty}× ${it.name}`).join(" · ")}
      </div>
    </button>
  );
}

/* —— Columna de cola con tabs —— */
function QueueColumn({ chats, confirmedMap, selId, onSel, onVip, onLoadDemo, onClearQueue, queueTab, setQueueTab }) {
  const unprocessed = chats.filter(c => !confirmedMap[c.id]);
  const processed   = chats.filter(c =>  confirmedMap[c.id]);
  const unanswered  = unprocessed.filter(c => c.firstResponseSec == null).length;

  const tabChats =
    queueTab === "procesados" ? processed :
    queueTab === "vip"        ? unprocessed.filter(c => c.priority) :
    unprocessed;

  const TABS = [
    { id: "cola",       label: "Cola",       count: unprocessed.filter(c => !c.priority).length },
    { id: "vip",        label: "VIP",        count: unprocessed.filter(c =>  c.priority).length },
    { id: "procesados", label: "Procesados", count: processed.length },
  ];

  return (
    <div style={{ width: 268, flexShrink: 0, borderRight: `1px solid ${cv("--line")}`, background: cv("--surface"), display: "flex", flexDirection: "column", minHeight: 0 }}>
      {/* Header */}
      <div style={{ padding: "13px 14px 10px", borderBottom: `1px solid ${cv("--line")}` }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
          <span style={{ fontSize: 13.5, fontWeight: 800 }}>Cola de chats</span>
          {unanswered > 0 && (
            <span style={{ fontSize: 11, fontWeight: 800, color: "#EF4444", background: "#FEE2E2", padding: "2px 8px", borderRadius: 99 }}>{unanswered} sin atender</span>
          )}
        </div>
        <div style={{ fontSize: 11, color: cv("--ink-3"), fontWeight: 650, marginTop: 4, display: "flex", alignItems: "center", gap: 5 }}>
          <Icon name="repeat" size={12} stroke={2.2} color={cv("--wasabi")} /> {ROLL}
        </div>
        {/* Tab pills */}
        <div style={{ display: "flex", gap: 4, marginTop: 10 }}>
          {TABS.map(t => (
            <button key={t.id} onClick={() => setQueueTab(t.id)} style={{
              flex: 1, padding: "5px 2px", borderRadius: 8, border: "none", cursor: "pointer",
              background: queueTab === t.id ? cv("--primary") : cv("--bg"),
              color: queueTab === t.id ? "#fff" : cv("--ink-3"),
              fontSize: 10, fontWeight: 800, lineHeight: 1.2, transition: "background .12s",
            }}>
              {t.label}
              {t.count > 0 && <span style={{ display: "block", fontSize: 10, opacity: .75 }}>({t.count})</span>}
            </button>
          ))}
        </div>
      </div>
      {/* List */}
      <div style={{ flex: 1, overflowY: "auto", padding: "11px 12px", display: "flex", flexDirection: "column", gap: 9 }}>
        {tabChats.length === 0 && (
          <div style={{ textAlign: "center", color: cv("--ink-3"), fontSize: 12, fontWeight: 600, paddingTop: 24, lineHeight: 1.5 }}>
            {queueTab === "procesados" ? "Sin pedidos procesados aún" :
             queueTab === "vip"        ? "Sin clientes VIP en cola" : "Cola vacía"}
          </div>
        )}
        {queueTab === "procesados"
          ? tabChats.map(c => <ProcessedItem key={c.id} chat={c} confirmedState={confirmedMap[c.id]} active={c.id === selId} onClick={() => onSel(c.id)} />)
          : tabChats.map(c => <QueueItem key={c.id} chat={c} active={c.id === selId} onClick={() => onSel(c.id)} />)}
      </div>
      {/* Footer */}
      <div style={{ padding: 12, borderTop: `1px solid ${cv("--line")}`, display: "flex", flexDirection: "column", gap: 8 }}>
        <Btn variant="outline" size="sm" icon="star" style={{ width: "100%" }} onClick={onVip}>Simular chat VIP entrante</Btn>
        <div style={{ display: "flex", gap: 8 }}>
          <Btn variant="outline" size="sm" style={{ flex: 1 }} onClick={onLoadDemo}>Cargar demo</Btn>
          <Btn variant="outline" size="sm" style={{ flex: 1 }} onClick={onClearQueue}>Limpiar cola</Btn>
        </div>
      </div>
    </div>
  );
}

/* —— Dropdown de asignación manual —— */
function AssignDropdown({ chat, operators, onAssign }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  const displayName = chat.assignedToName || (chat.assignedTo ? opName(chat.assignedTo) : null);

  useEffect(() => {
    if (!open) return;
    const close = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", close);
    return () => document.removeEventListener("mousedown", close);
  }, [open]);

  return (
    <div ref={ref} style={{ position: "relative" }}>
      <button onClick={() => setOpen(o => !o)} style={{
        display: "flex", alignItems: "center", gap: 5, padding: "5px 9px", borderRadius: 8,
        border: `1px solid ${cv("--line-2")}`, background: cv("--bg"), cursor: "pointer",
        fontSize: 11.5, fontWeight: 700, color: displayName ? cv("--ink") : cv("--ink-3"), whiteSpace: "nowrap",
      }}>
        <Icon name="user" size={12} color={cv("--ink-3")} />
        {displayName || "Sin asignar"}
        <Icon name="chevD" size={11} color={cv("--ink-3")} />
      </button>
      {open && (
        <div style={{
          position: "absolute", top: "calc(100% + 5px)", left: 0, zIndex: 40,
          background: cv("--surface"), border: `1px solid ${cv("--line")}`, borderRadius: 12,
          boxShadow: "var(--sh-pop)", padding: 6, minWidth: 190,
        }}>
          <div style={{ fontSize: 10.5, fontWeight: 800, color: cv("--ink-3"), padding: "3px 8px 6px", textTransform: "uppercase", letterSpacing: ".04em" }}>Asignar a operador</div>
          {operators.filter(o => o.status !== "offline").map(op => (
            <button key={op.id} onClick={() => { onAssign(chat.id, op.id); setOpen(false); }} style={{
              display: "flex", alignItems: "center", gap: 8, width: "100%", padding: "7px 9px",
              border: "none", borderRadius: 8, cursor: "pointer", fontSize: 12.5, fontWeight: 650,
              background: op.id === chat.assignedTo ? cv("--primary-tint") : "transparent",
              color: op.id === chat.assignedTo ? cv("--primary-700") : cv("--ink"),
            }}>
              <span style={{ width: 7, height: 7, borderRadius: "50%", flexShrink: 0, background: op.status === "available" ? cv("--wasabi") : cv("--st-preparacion") }} />
              <span style={{ flex: 1 }}>{op.name}</span>
              <span style={{ fontSize: 10.5, color: cv("--ink-3") }}>{op.activeChats} chats</span>
            </button>
          ))}
          {operators.filter(o => o.status !== "offline").length === 0 && (
            <div style={{ padding: "8px 10px", fontSize: 12, color: cv("--ink-3") }}>Sin operadores disponibles</div>
          )}
        </div>
      )}
    </div>
  );
}

/* —— Panel de conversación —— */
function ConvPanel({ chat, operators, onAssign, onSend }) {
  const [reply, setReply] = useState("");
  const ch = MAKI.CHANNELS[chat.channel] || MAKI.CHANNELS.whatsapp;
  const submit = () => { const t = reply.trim(); if (!t) return; onSend(t); setReply(""); };
  return (
    <div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", background: cv("--bg") }}>
      {/* Header */}
      <div style={{ padding: "12px 18px", borderBottom: `1px solid ${cv("--line")}`, background: cv("--surface"), display: "flex", alignItems: "center", gap: 11 }}>
        <Avatar name={chat.customer} type={chat.segment === "B2B_CORPORATE" ? "empresa" : undefined} size={38} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
            <span style={{ fontSize: 14.5, fontWeight: 750 }}>{chat.customer}</span>
            {chat.segment !== "B2C_REGULAR" && <SegmentTag segment={chat.segment} size="sm" />}
          </div>
          <div style={{ fontSize: 12, color: cv("--ink-3"), fontWeight: 600 }}>{chat.phone} · {ch.label}</div>
        </div>
        <AssignDropdown chat={chat} operators={operators} onAssign={onAssign} />
        <ChannelTag channel={chat.channel} />
      </div>
      {/* Thread */}
      <div style={{ flex: 1, overflowY: "auto", padding: "18px", display: "flex", flexDirection: "column", gap: 10 }}>
        {chat.thread.map((m, i) => {
          const mine = m.from === "agent";
          return (
            <div key={i} style={{ display: "flex", justifyContent: mine ? "flex-end" : "flex-start" }}>
              <div style={{ maxWidth: "74%", padding: m.image ? "6px 6px 4px" : "9px 13px", borderRadius: 14, borderBottomLeftRadius: mine ? 14 : 4, borderBottomRightRadius: mine ? 4 : 14, background: mine ? cv("--primary") : cv("--surface"), color: mine ? "#fff" : cv("--ink"), fontSize: 13.5, fontWeight: 500, lineHeight: 1.45, boxShadow: mine ? "none" : "var(--sh-1)" }}>
                {m.image && (
                  <a href={m.image} target="_blank" rel="noopener noreferrer" style={{ display: "block" }}>
                    <img src={m.image} alt={m.text || "imagen"} loading="lazy" style={{ maxWidth: 240, maxHeight: 280, width: "auto", borderRadius: 9, display: "block", objectFit: "cover" }} />
                  </a>
                )}
                {m.text && <div style={{ marginTop: m.image ? 5 : 0, padding: m.image ? "0 6px" : 0 }}>{m.text}</div>}
                <div style={{ fontSize: 10.5, opacity: .6, marginTop: 3, textAlign: "right", padding: m.image ? "0 6px" : 0 }}>{m.at}</div>
              </div>
            </div>
          );
        })}
      </div>
      {/* Reply input */}
      <div style={{ display: "flex", alignItems: "center", gap: 8, margin: 14, padding: 6, border: `1px solid ${cv("--line-2")}`, borderRadius: 12, background: cv("--surface") }}>
        <input value={reply} onChange={e => setReply(e.target.value)} onKeyDown={e => { if (e.key === "Enter") submit(); }} placeholder={`Responder por ${ch.label}…`} style={{ flex: 1, border: "none", outline: "none", fontSize: 13.5, padding: "6px 8px", background: "transparent", fontFamily: "inherit" }} />
        <Btn variant="primary" size="sm" icon="send" onClick={submit} />
      </div>
    </div>
  );
}

/* —— Fila editable de validación (RF-1.2) —— */
function EditableValRow({ label, value, ok, optional, onSave, placeholder }) {
  const [editing, setEditing] = useState(false);
  const [draft, setDraft] = useState(value || "");

  // Sync draft when value changes externally
  useEffect(() => { setDraft(value || ""); }, [value]);

  if (ok) {
    // Validated — static display
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 11px", borderRadius: 10, background: cv("--surface"), border: `1px solid ${cv("--line")}` }}>
        <span style={{ width: 18, height: 18, borderRadius: "50%", flexShrink: 0, background: cv("--wasabi"), display: "flex", alignItems: "center", justifyContent: "center", color: "#fff" }}>
          <Icon name="check" size={11} stroke={3} />
        </span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 10.5, fontWeight: 700, color: cv("--ink-3"), textTransform: "uppercase", letterSpacing: ".03em" }}>{label}</div>
          <div style={{ fontSize: 13, fontWeight: 650, color: cv("--ink"), whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{value}</div>
        </div>
        {onSave && <button onClick={() => setEditing(true)} style={{ background: "none", border: "none", cursor: "pointer", color: cv("--ink-3"), padding: 3, borderRadius: 5 }}><Icon name="edit" size={13} /></button>}
      </div>
    );
  }

  if (editing) {
    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 6, padding: "9px 11px", borderRadius: 10, background: "#EFF6FF", border: "1px solid #93C5FD" }}>
        <div style={{ fontSize: 10.5, fontWeight: 700, color: "#3B82F6", textTransform: "uppercase", letterSpacing: ".03em" }}>{label}{optional && " (opcional)"}</div>
        <div style={{ display: "flex", gap: 6 }}>
          <input
            value={draft}
            onChange={e => setDraft(e.target.value)}
            placeholder={placeholder || `Ingresar ${label.toLowerCase()}…`}
            style={{ flex: 1, padding: "7px 10px", borderRadius: 8, border: "1px solid #93C5FD", fontSize: 13, fontFamily: "inherit", outline: "none", background: "#fff" }}
            autoFocus
            onKeyDown={e => { if (e.key === "Enter" && draft.trim()) { onSave(draft.trim()); setEditing(false); } if (e.key === "Escape") setEditing(false); }}
          />
          <button onClick={() => { if (draft.trim()) { onSave(draft.trim()); setEditing(false); } }} style={{ padding: "7px 12px", borderRadius: 8, background: cv("--wasabi"), color: "#fff", border: "none", cursor: "pointer", fontSize: 13, fontWeight: 800 }}>✓</button>
          <button onClick={() => setEditing(false)} style={{ padding: "7px 10px", borderRadius: 8, background: cv("--surface"), border: `1px solid ${cv("--line")}`, cursor: "pointer", fontSize: 13 }}>✕</button>
        </div>
      </div>
    );
  }

  // Missing — show edit prompt
  return (
    <button
      onClick={() => onSave ? setEditing(true) : undefined}
      style={{ width: "100%", textAlign: "left", display: "flex", alignItems: "center", gap: 10, padding: "9px 11px", borderRadius: 10, background: optional ? cv("--surface") : "#FEF2F2", border: `1px solid ${optional ? cv("--line") : "#FCA5A5"}`, cursor: onSave ? "pointer" : "default" }}
    >
      <span style={{ width: 18, height: 18, borderRadius: "50%", flexShrink: 0, background: optional ? cv("--ink-3") : "#EF4444", display: "flex", alignItems: "center", justifyContent: "center", color: "#fff" }}>
        <Icon name={onSave ? "edit" : "x"} size={11} stroke={3} />
      </span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 10.5, fontWeight: 700, color: cv("--ink-3"), textTransform: "uppercase", letterSpacing: ".03em" }}>{label}{optional && " (opcional)"}</div>
        <div style={{ fontSize: 13, fontWeight: 650, color: optional ? cv("--ink-3") : "#B91C1C" }}>
          {value || (onSave ? (optional ? "No informado — tocar para editar" : "Falta completar — tocar para editar") : (optional ? "No informado" : "Falta completar"))}
        </div>
      </div>
      {onSave && <Icon name="edit" size={13} color={cv("--ink-3")} />}
    </button>
  );
}

/* —— Log de actividad —— */
function ActivityLog({ logs, chatId }) {
  const entries = logs.filter(l => l.chatId === chatId).slice(0, 6);
  if (!entries.length) return null;
  return (
    <div style={{ marginTop: 18 }}>
      <div style={{ fontSize: 11.5, fontWeight: 800, letterSpacing: ".04em", textTransform: "uppercase", color: cv("--ink-3"), marginBottom: 9 }}>Log de actividad</div>
      <div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
        {entries.map(l => (
          <div key={l.id} style={{ display: "flex", gap: 8, padding: "6px 10px", borderRadius: 8, background: cv("--bg"), border: `1px solid ${cv("--line")}` }}>
            <span style={{ fontSize: 10.5, color: cv("--ink-3"), fontWeight: 600, whiteSpace: "nowrap", flexShrink: 0, paddingTop: 1 }}>{l.at}</span>
            <span style={{ fontSize: 11.5, fontWeight: 700, color: cv("--ink-2"), flex: 1 }}>{l.action}</span>
            {l.detail && <span style={{ fontSize: 11, color: cv("--ink-3"), fontWeight: 600, whiteSpace: "nowrap", maxWidth: 110, overflow: "hidden", textOverflow: "ellipsis" }}>{l.detail}</span>}
          </div>
        ))}
      </div>
    </div>
  );
}

/* —— Editor de ítems del pedido (RF-1.4) —— */
function OrderEditor({ items, onSave, onCancel }) {
  const [draft, setDraft] = useState(() => items.map(i => ({ ...i })));
  const [pick, setPick] = useState("");
  const menuNames = Object.keys(MAKI.MENU);
  const total = draft.reduce((s, i) => s + i.price * i.qty, 0);

  const addItem = (name) => {
    if (!name) return;
    setDraft(d => {
      const ex = d.find(i => i.name === name);
      if (ex) return d.map(i => i.name === name ? { ...i, qty: i.qty + 1 } : i);
      return [...d, { name, qty: 1, price: (MAKI.MENU[name] || { p: 0 }).p }];
    });
    setPick("");
  };
  const setQty = (name, q) => setDraft(d => q <= 0 ? d.filter(i => i.name !== name) : d.map(i => i.name === name ? { ...i, qty: q } : i));

  const stepBtn = { width: 24, height: 24, borderRadius: 6, border: `1px solid ${cv("--line-2")}`, background: cv("--surface"), cursor: "pointer", fontSize: 14, fontWeight: 800, color: cv("--ink-2"), display: "flex", alignItems: "center", justifyContent: "center", lineHeight: 1 };

  return (
    <div style={{ border: "1px solid #93C5FD", borderRadius: 12, overflow: "hidden", background: "#EFF6FF" }}>
      {draft.length === 0 && <div style={{ padding: 12, fontSize: 12, color: cv("--ink-3"), textAlign: "center" }}>Sin ítems — agregá del menú</div>}
      {draft.map((it, i) => (
        <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, padding: "8px 11px", borderBottom: "1px solid #DBEAFE" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 5 }}>
            <button onClick={() => setQty(it.name, it.qty - 1)} style={stepBtn}>−</button>
            <span style={{ minWidth: 20, textAlign: "center", fontSize: 12.5, fontWeight: 800, fontVariantNumeric: "tabular-nums" }}>{it.qty}</span>
            <button onClick={() => setQty(it.name, it.qty + 1)} style={stepBtn}>+</button>
          </div>
          <span style={{ flex: 1, fontSize: 12, fontWeight: 600, minWidth: 0 }}>{it.name}</span>
          <span style={{ fontSize: 12, color: cv("--ink-2"), fontWeight: 650, whiteSpace: "nowrap" }}>{MAKI.money(it.price * it.qty)}</span>
          <button onClick={() => setQty(it.name, 0)} style={{ background: "none", border: "none", cursor: "pointer", color: "#EF4444", padding: 2 }}><Icon name="trash" size={13} /></button>
        </div>
      ))}
      {/* Agregar ítem */}
      <div style={{ display: "flex", gap: 6, padding: "9px 11px", borderBottom: "1px solid #DBEAFE" }}>
        <select value={pick} onChange={e => addItem(e.target.value)} style={{ flex: 1, padding: "7px 9px", borderRadius: 8, border: "1px solid #93C5FD", fontSize: 12.5, fontFamily: "inherit", background: "#fff", color: cv("--ink"), cursor: "pointer" }}>
          <option value="">+ Agregar del menú…</option>
          {menuNames.map(n => <option key={n} value={n}>{n} · {MAKI.money(MAKI.MENU[n].p)}</option>)}
        </select>
      </div>
      {/* Total + acciones */}
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "9px 11px" }}>
        <span style={{ fontSize: 13, fontWeight: 800 }}>Total</span>
        <span style={{ fontSize: 15, fontWeight: 800, color: cv("--primary-700") }}>{MAKI.money(total)}</span>
      </div>
      <div style={{ display: "flex", gap: 7, padding: "0 11px 11px" }}>
        <Btn variant="primary" size="sm" icon="check" style={{ flex: 1 }} onClick={() => onSave(draft)}>Guardar pedido</Btn>
        <Btn variant="outline" size="sm" onClick={onCancel}>Cancelar</Btn>
      </div>
    </div>
  );
}

/* —— Selector de medio de pago (RF-1.5) —— */
function PayEditor({ method, onSet, onClose }) {
  return (
    <div style={{ position: "absolute", top: "calc(100% + 4px)", left: 0, right: 0, zIndex: 30, background: cv("--surface"), border: `1px solid ${cv("--line")}`, borderRadius: 10, boxShadow: "var(--sh-pop)", padding: 6 }}>
      {Object.entries(PAY_METHODS).map(([k, v]) => (
        <button key={k} onClick={() => { onSet(k); onClose(); }} style={{
          display: "flex", alignItems: "center", gap: 9, width: "100%", padding: "8px 10px", border: "none", borderRadius: 8, cursor: "pointer",
          fontSize: 12.5, fontWeight: 700, background: k === method ? cv("--primary-tint") : "transparent", color: k === method ? cv("--primary-700") : cv("--ink"),
        }}>
          <Icon name={v.i} size={14} color={cv("--ink-3")} /> {v.l}
          {k === method && <Icon name="check" size={13} stroke={3} color={cv("--primary-700")} style={{ marginLeft: "auto" }} />}
        </button>
      ))}
    </div>
  );
}

/* —— Panel de validación + pago + alergia + Maxirest —— */
function ValidationPanel({ chat, maxirestOnline, onToggleMaxirest, onVerifyPay, onConfirm, confirmedState, onSaveName, onSaveAddr, onSaveAltRef, onSaveAllergens, onSaveItems, onSetPay, onRequestPay, logs }) {
  const [copied, setCopied] = useState(false);
  const [editItems, setEditItems] = useState(false);
  const [payOpen, setPayOpen] = useState(false);
  const allergic = chat.allergens.length > 0;
  const allergenLabels = chat.allergens.map(a => (MAKI.ALLERGENS[a] || {}).label || a).join(", ");
  const needsAddr = chat.mode === "Envío";
  const canConfirm = chat.nameOk && (!needsAddr || chat.addrOk) && chat.paymentVerified;
  const total = chatTotal(chat);
  const pm = PAY_METHODS[chat.paymentMethod] || PAY_METHODS.CASH;

  const manualStr = `Cliente: ${chat.customer} | Tel: ${chat.phone} | ${chat.items.map(i => `${i.qty} ${i.name}`).join(", ")} | Total: ${MAKI.money(total)}${allergic ? ` | NOTA: ALÉRGICO A ${allergenLabels.toUpperCase()}` : ""}`;
  const copy = () => { try { navigator.clipboard.writeText(manualStr); } catch (e) {} setCopied(true); setTimeout(() => setCopied(false), 1800); };

  return (
    <div style={{ width: 372, flexShrink: 0, borderLeft: `1px solid ${cv("--line")}`, background: cv("--surface"), display: "flex", flexDirection: "column", minHeight: 0 }}>
      <div style={{ flex: 1, overflowY: "auto", padding: 16 }}>

        {/* Banner alergia (RF-1.3) */}
        {allergic && (
          <div style={{ borderRadius: 12, padding: "12px 14px", marginBottom: 14, color: "#fff", background: "#EF4444", animation: "maki-alert-pulse 1.4s infinite", display: "flex", alignItems: "flex-start", gap: 11 }}>
            <Icon name="alert" size={22} stroke={2.4} color="#fff" style={{ flexShrink: 0, marginTop: 1 }} />
            <div style={{ minWidth: 0 }}>
              <div style={{ fontSize: 10.5, fontWeight: 850, letterSpacing: ".06em", opacity: .9 }}>ALERGIA ALIMENTARIA</div>
              <div style={{ fontSize: 16, fontWeight: 850, lineHeight: 1.15, margin: "1px 0 2px" }}>{allergenLabels.toUpperCase()}</div>
              <div style={{ fontSize: 11.5, fontWeight: 600, opacity: .95, lineHeight: 1.25 }}>Evitar contaminación cruzada</div>
            </div>
          </div>
        )}

        {/* Maxirest offline (§6) */}
        {!maxirestOnline && (
          <div style={{ borderRadius: 12, padding: "13px 14px", marginBottom: 14, background: "#1E2823", color: "#fff" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 9 }}>
              <Icon name="wifiOff" size={18} stroke={2.3} color="#FF6B57" style={{ flexShrink: 0 }} />
              <span style={{ fontSize: 12, fontWeight: 800, color: "#FF6B57" }}>MAXIREST FUERA DE LÍNEA</span>
            </div>
            <div style={{ fontSize: 11.5, color: "rgba(255,255,255,.7)", fontWeight: 600, lineHeight: 1.45, marginBottom: 10 }}>El pedido quedará en cola de reintento. Cargalo manualmente en la terminal para no frenar la cocina.</div>
            <Btn variant="primary" size="sm" icon={copied ? "check" : "copy"} style={{ width: "100%", background: copied ? cv("--wasabi") : undefined }} onClick={copy}>{copied ? "¡Datos copiados!" : "Copiar datos del pedido"}</Btn>
          </div>
        )}

        {/* Datos del cliente (RF-1.2) */}
        <div style={{ fontSize: 11.5, fontWeight: 800, letterSpacing: ".04em", textTransform: "uppercase", color: cv("--ink-3"), marginBottom: 9 }}>Validación de datos</div>
        <div style={{ display: "flex", flexDirection: "column", gap: 7, marginBottom: 18 }}>
          <EditableValRow label="Nombre del cliente" value={chat.customer} ok={chat.nameOk} onSave={onSaveName} placeholder="Nombre y apellido" />
          {needsAddr
            ? <EditableValRow label="Dirección de entrega" value={chat.address} ok={chat.addrOk} onSave={onSaveAddr} placeholder="Ej: Av. Santa Fe 1234, CABA" />
            : <EditableValRow label="Modalidad" value="Retiro en local" ok={true} />
          }
          <EditableValRow label="Referencia / tel. alternativo" value={chat.altRef} ok={chat.altOk} optional onSave={onSaveAltRef} placeholder="Ej: Timbre 2B · tel. alternativo" />
        </div>

        {/* Alérgenos informados (RF-1.3) — control para cocina */}
        <div style={{ marginBottom: 18 }}>
          <div style={{ fontSize: 11.5, fontWeight: 800, letterSpacing: ".04em", textTransform: "uppercase", color: cv("--ink-3"), marginBottom: 9 }}>Alérgenos informados</div>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 7 }}>
            {Object.values(MAKI.ALLERGENS).map(a => {
              const on = chat.allergens.includes(a.key);
              return (
                <button
                  key={a.key}
                  onClick={() => onSaveAllergens(on ? chat.allergens.filter(x => x !== a.key) : [...chat.allergens, a.key])}
                  style={{
                    display: "flex", alignItems: "center", gap: 6, padding: "6px 11px", borderRadius: 99, cursor: "pointer",
                    border: `1.5px solid ${on ? "#EF4444" : cv("--line")}`,
                    background: on ? "#FEE2E2" : cv("--surface"),
                    color: on ? "#B91C1C" : cv("--ink-2"), fontSize: 12, fontWeight: 700, fontFamily: "inherit",
                  }}
                >
                  <span style={{ width: 14, height: 14, borderRadius: 4, border: `1.5px solid ${on ? "#EF4444" : cv("--ink-3")}`, background: on ? "#EF4444" : "transparent", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
                    {on && <Icon name="check" size={10} stroke={3} color="#fff" />}
                  </span>
                  {a.label}
                </button>
              );
            })}
          </div>
        </div>

        {/* Pago */}
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 9 }}>
          <span style={{ fontSize: 11.5, fontWeight: 800, letterSpacing: ".04em", textTransform: "uppercase", color: cv("--ink-3") }}>Pago</span>
          <button onClick={() => onRequestPay(chat.id)} style={{ background: "none", border: "none", cursor: "pointer", fontSize: 11, fontWeight: 800, color: cv("--primary"), display: "flex", alignItems: "center", gap: 4, padding: 0 }}>
            <Icon name="send" size={11} /> Solicitar al cliente
          </button>
        </div>
        <div style={{ border: `1px solid ${cv("--line")}`, borderRadius: 12, padding: 12, marginBottom: 18 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 10, position: "relative" }}>
            <Icon name={pm.i} size={16} color={cv("--ink-2")} />
            <button onClick={() => setPayOpen(o => !o)} style={{ display: "flex", alignItems: "center", gap: 5, flex: 1, background: "none", border: "none", cursor: "pointer", fontSize: 13, fontWeight: 700, color: cv("--ink"), textAlign: "left", padding: 0 }}>
              {pm.l} <Icon name="chevD" size={12} color={cv("--ink-3")} />
            </button>
            <span style={{ fontSize: 11.5, fontWeight: 800, padding: "2px 8px", borderRadius: 99, background: chat.paymentVerified ? cv("--wasabi-soft") : cv("--st-preparacion-bg"), color: chat.paymentVerified ? cv("--wasabi") : cv("--st-preparacion") }}>{chat.paymentVerified ? "Verificado" : "Pendiente"}</span>
            {payOpen && <PayEditor method={chat.paymentMethod} onSet={(m) => onSetPay(chat.id, m)} onClose={() => setPayOpen(false)} />}
          </div>
          {chat.paymentMethod === "BANK_TRANSFER" && (
            <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "8px 10px", borderRadius: 9, background: cv("--bg"), marginBottom: 10, fontSize: 12, fontWeight: 650, color: chat.paymentProof ? cv("--ink-2") : cv("--ink-3") }}>
              <Icon name={chat.paymentProof ? "check" : "upload"} size={14} color={chat.paymentProof ? cv("--wasabi") : cv("--ink-3")} />
              {chat.paymentProof ? `Comprobante: ${chat.paymentProof}` : "Sin comprobante adjunto"}
            </div>
          )}
          {!chat.paymentVerified && (
            <Btn variant="outline" size="sm" icon="check" style={{ width: "100%" }} onClick={() => onVerifyPay(chat.id)}>Verificar pago</Btn>
          )}
        </div>

        {/* Resumen pedido */}
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 9 }}>
          <span style={{ fontSize: 11.5, fontWeight: 800, letterSpacing: ".04em", textTransform: "uppercase", color: cv("--ink-3") }}>Pedido</span>
          {!editItems && (
            <button onClick={() => setEditItems(true)} style={{ background: "none", border: "none", cursor: "pointer", fontSize: 11, fontWeight: 800, color: cv("--primary"), display: "flex", alignItems: "center", gap: 4, padding: 0 }}>
              <Icon name="edit" size={11} /> Editar pedido
            </button>
          )}
        </div>
        {editItems ? (
          <OrderEditor items={chat.items} onSave={(items) => { onSaveItems(chat.id, items); setEditItems(false); }} onCancel={() => setEditItems(false)} />
        ) : (
          <div style={{ border: `1px solid ${cv("--line")}`, borderRadius: 12, overflow: "hidden" }}>
            {chat.items.length === 0 && (
              <div style={{ padding: "12px", fontSize: 12, color: cv("--ink-3"), textAlign: "center" }}>Sin ítems cargados — tocá "Editar pedido"</div>
            )}
            {chat.items.map((it, i) => (
              <div key={i} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 12px", borderBottom: `1px solid ${cv("--line")}` }}>
                <span style={{ minWidth: 28, height: 24, borderRadius: 6, background: cv("--primary-tint"), color: cv("--primary-700"), fontSize: 12.5, fontWeight: 800, display: "flex", alignItems: "center", justifyContent: "center", padding: "0 7px" }}>{it.qty}×</span>
                <span style={{ flex: 1, fontSize: 12.5, fontWeight: 600 }}>{it.name}</span>
                <span style={{ fontSize: 12.5, color: cv("--ink-2"), fontWeight: 650 }}>{MAKI.money(it.price * it.qty)}</span>
              </div>
            ))}
            {chat.items.length > 0 && (
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "10px 12px" }}>
                <span style={{ fontSize: 13, fontWeight: 800 }}>Total</span>
                <span style={{ fontSize: 15.5, fontWeight: 800, color: cv("--primary-700") }}>{MAKI.money(total)}</span>
              </div>
            )}
          </div>
        )}

        {/* Activity log */}
        <ActivityLog logs={logs} chatId={chat.id} />
      </div>

      {/* Footer confirmación */}
      <div style={{ padding: 14, borderTop: `1px solid ${cv("--line")}` }}>
        {!confirmedState && !canConfirm && (
          <div style={{ fontSize: 11.5, color: "#B91C1C", fontWeight: 700, marginBottom: 9, display: "flex", alignItems: "center", gap: 6 }}>
            <Icon name="alert" size={13} stroke={2.4} /> Completá datos y verificá el pago para confirmar
          </div>
        )}
        {confirmedState ? (
          <div style={{ display: "flex", alignItems: "center", gap: 9, padding: "12px 14px", borderRadius: 11, background: confirmedState === "injected" ? cv("--wasabi-soft") : "#1E2823", color: confirmedState === "injected" ? cv("--wasabi") : "#FF6B57", fontSize: 13, fontWeight: 800 }}>
            <Icon name={confirmedState === "injected" ? "checkCheck" : "wifiOff"} size={18} stroke={2.4} />
            {confirmedState === "injected" ? "Inyectado a Maxirest ✓" : "En cola de reintento (Maxirest offline)"}
          </div>
        ) : (
          <Btn variant="primary" size="md" icon="zap" style={{ width: "100%", opacity: canConfirm ? 1 : .45, pointerEvents: canConfirm ? "auto" : "none" }} onClick={() => onConfirm(chat)}>Confirmar e inyectar a Maxirest</Btn>
        )}
        <button onClick={onToggleMaxirest} style={{ width: "100%", marginTop: 8, background: "none", border: "none", cursor: "pointer", fontSize: 11, fontWeight: 700, color: cv("--ink-3"), display: "flex", alignItems: "center", justifyContent: "center", gap: 5, whiteSpace: "nowrap" }}>
          <span style={{ width: 7, height: 7, borderRadius: "50%", background: maxirestOnline ? cv("--wasabi") : "#EF4444" }} />
          Maxirest {maxirestOnline ? "en línea" : "offline"} · simular {maxirestOnline ? "caída" : "recuperación"}
        </button>
      </div>
    </div>
  );
}

/* —— Modal bypass de voz (RF-2.2) —— */
function VoiceModal({ onClose }) {
  const [sec, setSec] = useState(0);
  useEffect(() => { const t = setInterval(() => setSec(s => s + 1), 1000); return () => clearInterval(t); }, []);
  return (
    <div style={{ position: "fixed", inset: 0, zIndex: 70, display: "flex", alignItems: "center", justifyContent: "center" }}>
      <div onClick={onClose} style={{ position: "absolute", inset: 0, background: "rgba(25,32,28,.4)", animation: "maki-fade .18s ease" }} />
      <div style={{ position: "relative", width: 440, maxWidth: "92vw", background: cv("--surface"), borderRadius: 18, boxShadow: "var(--sh-pop)", padding: 22, animation: "maki-rise .26s cubic-bezier(.22,1,.36,1)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 11, marginBottom: 18 }}>
          <span style={{ width: 42, height: 42, borderRadius: 11, background: cv("--ch-telefono-bg"), color: cv("--ch-telefono"), display: "flex", alignItems: "center", justifyContent: "center" }}><Icon name="headphones" size={22} stroke={2.2} /></span>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 15.5, fontWeight: 800 }}>Llamado de voz activo</div>
            <div style={{ fontSize: 12.5, color: cv("--ink-3"), fontWeight: 600 }}>Bypass manual · pedido telefónico</div>
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 6, padding: "5px 11px", borderRadius: 99, background: cv("--st-cancelado-bg"), color: cv("--st-cancelado"), fontSize: 14, fontWeight: 800, fontVariantNumeric: "tabular-nums" }}>
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: cv("--st-cancelado"), animation: "maki-pulse-dot 1s infinite" }} /> {mmss(sec)}
          </div>
        </div>
        <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
          {[["Teléfono entrante", "+54 11 ____-____", "phone"], ["Nombre del cliente", "Buscar o crear cliente…", "user"], ["Dirección / retiro", "Cargar datos de entrega…", "pin"]].map(([l, ph, ic]) => (
            <div key={l} style={{ display: "flex", alignItems: "center", gap: 9, padding: "10px 12px", border: `1px solid ${cv("--line-2")}`, borderRadius: 11 }}>
              <Icon name={ic} size={15} color={cv("--ink-3")} />
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 10.5, fontWeight: 700, color: cv("--ink-3"), textTransform: "uppercase", letterSpacing: ".03em" }}>{l}</div>
                <div style={{ fontSize: 13, color: cv("--ink-3"), fontWeight: 500 }}>{ph}</div>
              </div>
            </div>
          ))}
        </div>
        <div style={{ display: "flex", gap: 10, marginTop: 18 }}>
          <Btn variant="outline" size="md" style={{ flex: 1 }} onClick={onClose}>Cancelar</Btn>
          <Btn variant="primary" size="md" icon="check" style={{ flex: 1.4 }} onClick={onClose}>Cerrar llamado · {mmss(sec)}</Btn>
        </div>
      </div>
    </div>
  );
}

/* —— Overlay alerta VIP (RF-2.1) —— */
function VipAlert({ chat, onOpen, onClose }) {
  return (
    <div style={{ position: "fixed", top: 18, left: "50%", transform: "translateX(-50%)", zIndex: 90, width: 420, maxWidth: "92vw", background: cv("--ink"), color: "#fff", borderRadius: 14, boxShadow: "var(--sh-pop)", padding: "14px 16px", animation: "maki-vip-in .3s cubic-bezier(.22,1,.36,1)", display: "flex", alignItems: "center", gap: 13, border: `2px solid ${cv("--primary")}` }}>
      <span style={{ width: 44, height: 44, borderRadius: 12, background: cv("--primary"), display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, animation: "maki-pulse-dot 1s infinite" }}><Icon name="star" size={24} fill="#fff" stroke={0} /></span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: ".06em", color: cv("--primary") }}>CLIENTE VIP ENTRANTE</div>
        <div style={{ fontSize: 15, fontWeight: 800 }}>{chat.customer}</div>
        <div style={{ fontSize: 12, color: "rgba(255,255,255,.65)", fontWeight: 600 }}>Reenrutado al tope de la cola · push enviado al iPad</div>
      </div>
      <Btn variant="primary" size="sm" onClick={onOpen}>Atender</Btn>
      <button onClick={onClose} style={{ background: "rgba(255,255,255,.1)", border: "none", borderRadius: 8, width: 28, height: 28, color: "#fff", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}><Icon name="x" size={15} /></button>
    </div>
  );
}

/* —— Pantalla Consola —— */
function ConsolaScreen({ chain }) {
  const order = (list) => [...list].sort((a, b) => (b.priority - a.priority) || (b.firstResponseSec == null) - (a.firstResponseSec == null) || (b.waitSec - a.waitSec));
  const [chats, setChats] = useState([]);
  const [selId, setSelId] = useState(null);
  const [maxirestOnline, setMaxirestOnline] = useState(true);
  const [voice, setVoice] = useState(false);
  const [vip, setVip] = useState(null);
  const [confirmed, setConfirmed] = useState({});
  const [queueTab, setQueueTab] = useState("cola");
  const [logs, setLogs] = useState([]);
  const [operators, setOperators] = useState(() => MAKI.OPERATORS);
  const vipRef = useRef(0);

  const addLog = useCallback((action, detail, chatId) => {
    const at = new Date().toLocaleTimeString("es-AR", { hour: "2-digit", minute: "2-digit" });
    setLogs(prev => [{ id: Date.now() + Math.random(), at, action, detail: detail || "", chatId }, ...prev].slice(0, 300));
    window.dispatchEvent(new CustomEvent("maki:notif", { detail: { type: "log", text: `${action}${detail ? " · " + detail : ""}`, at } }));
  }, []);

  // tick timers
  useEffect(() => {
    const t = setInterval(() => setChats(cs => cs.map(c => confirmed[c.id] ? c : { ...c, waitSec: c.waitSec + 1 })), 1000);
    return () => clearInterval(t);
  }, [confirmed]);

  // Load queue + operators from API
  useEffect(() => {
    if (!window.API || !API.isAuthenticated()) return;

    API.getQueue().then(queue => {
      const mapped = (queue || []).map(a => API.mapQueueItem(a));
      setChats(mapped);
      if (mapped.length > 0) setSelId(prev => prev || order(mapped)[0].id);
    }).catch(() => {});

    API.getOperators().then(ops => {
      if (ops && ops.length > 0) {
        setOperators(ops.map(op => ({
          id: op.userId,
          name: op.user?.displayName || op.displayName || "Operador",
          status: op.status,
          activeChats: op.activeChatsCount || 0,
          artSec: 0,
        })));
      }
    }).catch(() => {});

    const sock = API.connectOps((event, data) => {
      if (event === "chat:vip_incoming") {
        const newChat = { ...API.mapQueueItem(data), waitSec: 0, priority: true };
        setChats(cs => [newChat, ...cs]);
        setVip(newChat);
        addLog("⭐ VIP entrante", newChat.customer, newChat.id);
        window.dispatchEvent(new CustomEvent("maki:notif", { detail: { type: "vip", text: `Cliente VIP: ${newChat.customer}`, at: new Date().toLocaleTimeString("es-AR", { hour: "2-digit", minute: "2-digit" }) } }));
        if (typeof makiBeep !== "undefined") makiBeep(2);
        setTimeout(() => setVip(v => v && v.id === newChat.id ? null : v), 6000);
      }
      if (event === "queue:update") {
        API.getQueue().then(queue => {
          const mapped = (queue || []).map(a => API.mapQueueItem(a));
          setChats(mapped);
          setSelId(prev => (mapped.some(c => c.id === prev) ? prev : (mapped[0] ? order(mapped)[0].id : null)));
        }).catch(() => {});
      }
      if (event === "chat:message") {
        const cid = data && data.conversationId;
        if (!cid) return;
        const at = new Date().toLocaleTimeString("es-AR", { hour: "2-digit", minute: "2-digit" });
        setChats(cs => {
          const exists = cs.some(c => c.id === cid);
          if (!exists) {
            // Chat aún no cargado → recargar cola (trae el texto real vía lastMessage)
            API.getQueue().then(queue => {
              if (queue && queue.length > 0) setChats(queue.map(a => API.mapQueueItem(a)));
            }).catch(() => {});
            return cs;
          }
          const img = data.mediaId ? API.mediaUrl(data.mediaId) : null;
          return cs.map(c => {
            if (c.id !== cid) return c;
            const last = c.thread[c.thread.length - 1];
            if (img) {
              // Dedupe imagen por url
              if (last && last.from === "client" && last.image === img) return c;
              return { ...c, preview: data.body || "📷 Imagen", thread: [...c.thread, { from: "client", at, text: (data.body || ""), image: img }] };
            }
            // Dedupe texto: evita duplicar si queue:update ya cargó este mismo texto
            if (last && last.from === "client" && last.text === data.body) return c;
            return { ...c, preview: data.body, thread: [...c.thread, { from: "client", at, text: data.body }] };
          });
        });
        addLog(data.mediaId ? "🖼️ Imagen entrante" : "💬 Mensaje entrante", (data.body || (data.mediaId ? "imagen" : "")).slice(0, 32), cid);
        window.dispatchEvent(new CustomEvent("maki:notif", { detail: { type: "ok", text: `Nuevo mensaje de ${data.customerName || cid}`, at } }));
        if (typeof makiBeep !== "undefined") makiBeep(1);
      }
      if (event === "maxirest:offline") {
        setMaxirestOnline(false);
        window.dispatchEvent(new CustomEvent("maki:notif", { detail: { type: "error", text: "Maxirest offline — pedidos en cola de reintento", at: new Date().toLocaleTimeString("es-AR", { hour: "2-digit", minute: "2-digit" }) } }));
      }
      if (event === "maxirest:recovered") {
        setMaxirestOnline(true);
        window.dispatchEvent(new CustomEvent("maki:notif", { detail: { type: "ok", text: "Maxirest volvió a estar en línea", at: new Date().toLocaleTimeString("es-AR", { hour: "2-digit", minute: "2-digit" }) } }));
      }
    });

    return () => API.disconnectOps();
  }, []);

  // Filter by chain (from App header selector)
  const sorted = order(chats);
  const chainFiltered = !chain || chain === "todas" ? sorted : sorted.filter(c => c.chain === chain);
  const unconfirmed = chainFiltered.filter(c => !confirmed[c.id]);
  const sel = chainFiltered.find(c => c.id === selId) || unconfirmed[0] || chainFiltered[0] || sorted[0];

  // Handlers
  const verifyPay = (id) => {
    const chat = chats.find(c => c.id === id);
    setChats(cs => cs.map(c => c.id === id ? { ...c, paymentVerified: true, paymentProof: c.paymentProof || "comprobante_cargado.jpg" } : c));
    addLog("✓ Pago verificado", chat?.customer, id);
    if (window.API && chat && chat._orderId) API.verifyPayment(chat._orderId).catch(() => {});
  };

  const confirm = (chat) => {
    const state = maxirestOnline ? "injected" : "queued";
    setConfirmed(s => ({ ...s, [chat.id]: state }));
    addLog(state === "injected" ? "⚡ Inyectado a Maxirest" : "⏳ En cola de reintento", chat.customer, chat.id);
    setQueueTab("procesados");
    if (window.API && chat._orderId) API.confirmOrder(chat._orderId).catch(() => {});
  };

  const saveAddr = (chatId, addr) => {
    setChats(cs => cs.map(c => c.id === chatId ? { ...c, address: addr, addrOk: !!addr } : c));
    addLog("📍 Dirección actualizada", addr, chatId);
    if (window.API && API.isAuthenticated()) API.updateContactData(chatId, addr, undefined).catch(() => {});
  };

  const saveAltRef = (chatId, altRef) => {
    setChats(cs => cs.map(c => c.id === chatId ? { ...c, altRef, altOk: !!altRef } : c));
    addLog("📞 Referencia actualizada", altRef, chatId);
    if (window.API && API.isAuthenticated()) API.updateContactData(chatId, undefined, altRef).catch(() => {});
  };

  const saveAllergens = (chatId, flags) => {
    const chat = chats.find(c => c.id === chatId);
    setChats(cs => cs.map(c => c.id === chatId ? { ...c, allergens: flags } : c));
    const labels = flags.map(f => (MAKI.ALLERGENS[f] || {}).label || f).join(", ") || "ninguno";
    addLog("⚠️ Alérgenos actualizados", labels, chatId);
    if (window.API && API.isAuthenticated() && chat && chat.customerId) API.updateAllergens(chat.customerId, flags).catch(() => {});
  };

  const saveName = (chatId, name) => {
    setChats(cs => cs.map(c => c.id === chatId ? { ...c, customer: name, nameOk: !!name } : c));
    addLog("✏️ Nombre corregido", name, chatId);
    if (window.API && API.isAuthenticated()) API.updateContactData(chatId, undefined, undefined, name).catch(() => {});
  };

  const saveItems = (chatId, items) => {
    setChats(cs => cs.map(c => c.id === chatId ? { ...c, items } : c));
    const qty = items.reduce((s, i) => s + i.qty, 0);
    addLog("🍱 Pedido editado", `${items.length} líneas · ${qty} u.`, chatId);
  };

  const setPay = (chatId, method) => {
    setChats(cs => cs.map(c => c.id === chatId ? { ...c, paymentMethod: method } : c));
    addLog("💳 Medio de pago", (PAY_METHODS[method] || {}).l || method, chatId);
  };

  const notifyErr = (msg) => window.dispatchEvent(new CustomEvent("maki:notif", { detail: { type: "error", text: msg, at: new Date().toLocaleTimeString("es-AR", { hour: "2-digit", minute: "2-digit" }) } }));

  // Envía mensaje al cliente (WhatsApp real vía Meta si está configurado; si no, queda en el hilo)
  const sendToCustomer = (chatId, text) => {
    const chat = chats.find(c => c.id === chatId);
    const at = new Date().toLocaleTimeString("es-AR", { hour: "2-digit", minute: "2-digit" });
    setChats(cs => cs.map(c => c.id === chatId ? { ...c, thread: [...c.thread, { from: "agent", at, text }] } : c));
    if (window.API && API.isAuthenticated() && chat && chat.phone) {
      API.sendWhatsapp(chat.phone, text)
        .then(r => {
          if (r && r.simulated) addLog("💬 Mensaje (simulado)", text.slice(0, 32), chatId);
          else { addLog("✅ WhatsApp enviado", text.slice(0, 32), chatId); window.dispatchEvent(new CustomEvent("maki:notif", { detail: { type: "ok", text: `WhatsApp enviado a ${chat.customer}`, at } })); }
        })
        .catch(e => notifyErr("WhatsApp: " + (e.message || "error al enviar")));
    } else {
      addLog("💬 Mensaje enviado", text.slice(0, 32), chatId);
    }
  };

  const sendReply = (text) => sendToCustomer(sel.id, text);

  const requestPay = (chatId) => {
    sendToCustomer(chatId, "¡Hola! ¿Con qué medio vas a abonar? Efectivo, transferencia, tarjeta o Mercado Pago 🙏");
    addLog("💬 Solicitó medio de pago", "", chatId);
  };

  const assignChat = (chatId, operatorId) => {
    const op = operators.find(o => o.id === operatorId);
    setChats(cs => cs.map(c => c.id === chatId ? { ...c, assignedTo: operatorId, assignedToName: op?.name || operatorId } : c));
    setOperators(ops => ops.map(o => o.id === operatorId ? { ...o, activeChats: (o.activeChats || 0) + 1 } : o));
    addLog("👤 Asignado a", op?.name || operatorId, chatId);
    if (window.API && API.isAuthenticated()) API.assignChat(chatId, operatorId).catch(() => {});
  };

  const simulateVip = () => {
    if (window.API && API.isAuthenticated()) { API.simulateVip().catch(() => {}); return; }
    vipRef.current++;
    const names = [["Gastón Pereyra", ["shellfish"]], ["Embajada de Japón", []], ["Dra. Rivas", ["peanut"]]];
    const [nm, al] = names[vipRef.current % names.length];
    const id = "ch-vip-" + vipRef.current;
    const newChat = {
      id, customer: nm, phone: "+54 11 4" + (1000 + vipRef.current) + "-9921", channel: "whatsapp",
      chain: "sakura", segment: "VIP", allergens: al, altRef: "Encargado de seguridad en portería",
      address: "Pilará, Lote 12", mode: "Envío", priority: true,
      waitSec: 0, firstResponseSec: null, assignedTo: null, assignedToName: null, status: "queued",
      paymentMethod: "BANK_TRANSFER", paymentVerified: false, paymentProof: null,
      nameOk: true, addrOk: true, altOk: true,
      preview: "Hola, necesito un pedido urgente para hoy",
      items: mkItems([["Roll Salmón Premium x10", 3], ["Combinado Masaca x40", 1]]),
      thread: [{ from: "client", at: "ahora", text: "Hola, necesito un pedido urgente para hoy 🙏" }],
    };
    setChats(cs => [newChat, ...cs]);
    setVip(newChat);
    addLog("⭐ VIP simulado", nm, id);
    window.dispatchEvent(new CustomEvent("maki:notif", { detail: { type: "vip", text: `VIP simulado: ${nm}`, at: new Date().toLocaleTimeString("es-AR", { hour: "2-digit", minute: "2-digit" }) } }));
    if (typeof makiBeep !== "undefined") makiBeep(2);
    setTimeout(() => setVip(v => v && v.id === id ? null : v), 6000);
  };

  const loadDemo = () => {
    if (!(window.API && API.isAuthenticated())) return;
    API.seedQueue().then(() => API.getQueue()).then(queue => {
      const mapped = (queue || []).map(a => API.mapQueueItem(a));
      setChats(mapped);
      if (mapped.length > 0) setSelId(order(mapped)[0].id);
      addLog("📋 Demo cargada", `${mapped.length} chats`);
    }).catch(() => {});
  };

  const clearQueue = () => {
    if (!(window.API && API.isAuthenticated())) return;
    API.clearQueue().then(() => { setChats([]); setSelId(null); addLog("🧹 Cola limpiada"); }).catch(() => {});
  };

  if (!sel) return (
    <div style={{ height: "100%", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 16, padding: 40, color: cv("--ink-3") }}>
      <div style={{ fontSize: 14, fontWeight: 600, textAlign: "center" }}>Cola vacía · esperando chats entrantes de WhatsApp…</div>
      <div style={{ display: "flex", gap: 10 }}>
        <Btn variant="outline" size="sm" onClick={loadDemo}>Cargar demo</Btn>
        <Btn variant="outline" size="sm" icon="star" onClick={simulateVip}>Simular VIP</Btn>
      </div>
    </div>
  );

  return (
    <div style={{ height: "100%", display: "flex", minHeight: 0, position: "relative" }}>
      <QueueColumn
        chats={chainFiltered}
        confirmedMap={confirmed}
        selId={sel.id}
        onSel={setSelId}
        onVip={simulateVip}
        onLoadDemo={loadDemo}
        onClearQueue={clearQueue}
        queueTab={queueTab}
        setQueueTab={setQueueTab}
      />

      <div style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", minHeight: 0 }}>
        {/* Toolbar */}
        <div style={{ height: 48, flexShrink: 0, borderBottom: `1px solid ${cv("--line")}`, background: cv("--surface"), display: "flex", alignItems: "center", padding: "0 16px", gap: 10 }}>
          <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12, fontWeight: 700, whiteSpace: "nowrap", color: maxirestOnline ? cv("--wasabi") : "#EF4444" }}>
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: maxirestOnline ? cv("--wasabi") : "#EF4444" }} />
            Puente Maxirest {maxirestOnline ? "en línea" : "offline"}
          </span>
          <div style={{ flex: 1 }} />
          {/* Registro de llamada de voz — oculto (feature futura) */}
          {false && <Btn variant="outline" size="sm" icon="headphones" onClick={() => setVoice(true)}>Registrar llamado de voz</Btn>}
        </div>

        <div style={{ flex: 1, display: "flex", minHeight: 0 }}>
          {confirmed[sel.id] ? (
            /* Vista de chat ya procesado */
            <div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", flexDirection: "column", gap: 14, background: cv("--bg"), padding: 32 }}>
              <div style={{ width: 64, height: 64, borderRadius: 20, background: cv("--wasabi-soft"), display: "flex", alignItems: "center", justifyContent: "center" }}>
                <Icon name="checkCheck" size={32} stroke={2} color={cv("--wasabi")} />
              </div>
              <div style={{ textAlign: "center" }}>
                <div style={{ fontSize: 18, fontWeight: 800, color: cv("--ink") }}>{confirmed[sel.id] === "injected" ? "Pedido inyectado a Maxirest" : "En cola de reintento"}</div>
                <div style={{ fontSize: 13, color: cv("--ink-3"), marginTop: 4 }}>{sel.customer}</div>
              </div>
              <Btn variant="outline" size="sm" onClick={() => { setQueueTab("procesados"); }}>Ver todos los procesados</Btn>
            </div>
          ) : (
            <>
              <ConvPanel chat={sel} key={sel.id} operators={operators} onAssign={assignChat} onSend={sendReply} />
              <ValidationPanel
                chat={sel}
                maxirestOnline={maxirestOnline}
                onToggleMaxirest={() => {
                  const next = !maxirestOnline;
                  setMaxirestOnline(next);
                  if (window.API && API.isAuthenticated()) (next ? API.maxirestUp() : API.maxirestDown()).catch(() => {});
                }}
                onVerifyPay={verifyPay}
                onConfirm={confirm}
                confirmedState={confirmed[sel.id]}
                onSaveName={(name) => saveName(sel.id, name)}
                onSaveAddr={(addr) => saveAddr(sel.id, addr)}
                onSaveAltRef={(altRef) => saveAltRef(sel.id, altRef)}
                onSaveAllergens={(flags) => saveAllergens(sel.id, flags)}
                onSaveItems={saveItems}
                onSetPay={setPay}
                onRequestPay={requestPay}
                logs={logs}
              />
            </>
          )}
        </div>
      </div>

      {voice && <VoiceModal onClose={() => setVoice(false)} />}
      {vip && <VipAlert chat={vip} onOpen={() => { setSelId(vip.id); setVip(null); setQueueTab("cola"); }} onClose={() => setVip(null)} />}
    </div>
  );
}

Object.assign(window, { ConsolaScreen });
