// admin-app/tables.jsx — Plan de table Kanban drag-and-drop
//
// Architecture :
// - useAdminData() fournit invites + tables (sync auto toutes les 30s)
// - On maintient un état local "optimistic" : les drags sont appliqués
//   immédiatement côté UI puis pushés au backend via adminAssign.
// - Édition des métadonnées (nom/note/capacité) via panel inline.
// - HTML5 DnD pour le drag-and-drop desktop ; sur tactile, dropdown
//   de secours (bouton "↪" sur chaque carte).

const UNASSIGNED_ID = "__unassigned__";
const TABLES_DIRTY_LS_KEY = "ea-admin:tables:dirty:v1";

function loadTablesDirty_() {
  try {
    const raw = localStorage.getItem(TABLES_DIRTY_LS_KEY);
    if (!raw) return {};
    const parsed = JSON.parse(raw);
    return (parsed && typeof parsed === "object") ? parsed : {};
  } catch { return {}; }
}
function saveTablesDirty_(dirty) {
  try {
    if (!dirty || Object.keys(dirty).length === 0) localStorage.removeItem(TABLES_DIRTY_LS_KEY);
    else localStorage.setItem(TABLES_DIRTY_LS_KEY, JSON.stringify(dirty));
  } catch {}
}

function TablesPage({ data, onReload }) {
  const invites = data.invites || [];
  const tables  = data.tables || [];

  // ─── Mode BATCH : dirty = { inviteId: table_id } (persisté en LS) ───
  const [dirty, setDirtyRaw] = React.useState(() => loadTablesDirty_());
  const [saving, setSaving] = React.useState(false);
  const [saveErr, setSaveErr] = React.useState("");

  const setDirty = (updater) => {
    setDirtyRaw(prev => {
      const next = typeof updater === "function" ? updater(prev) : updater;
      saveTablesDirty_(next);
      return next;
    });
  };

  const dirtyCount = Object.keys(dirty).length;

  // Freeze l'auto-refresh tant qu'il y a des modifs en attente
  React.useEffect(() => {
    window.__adminFreezeRefresh = dirtyCount > 0;
    return () => { window.__adminFreezeRefresh = false; };
  }, [dirtyCount]);

  // Nettoie les dirty qui sont déjà alignés avec le backend
  React.useEffect(() => {
    setDirty(prev => {
      const next = { ...prev };
      let changed = false;
      for (const id of Object.keys(prev)) {
        const inv = invites.find(i => i.id === id);
        if (inv && (inv.table_id || "") === (prev[id] || "")) {
          delete next[id]; changed = true;
        }
      }
      return changed ? next : prev;
    });
  }, [invites]);

  // Édition d'une table (modal-like inline)
  const [editingTableId, setEditingTableId] = React.useState(null);

  // Filtre côté
  const [coteFilter, setCoteFilter] = React.useState("all"); // all | E | A

  // Filtre les invités à placer : ceux qui ont dit oui (incluant +1 et Enfant)
  const placable = invites.filter(i => i.rsvp_statut === "oui");
  const visible = coteFilter === "all"
    ? placable
    : placable.filter(i => (i.cote || "").toLowerCase().startsWith(coteFilter.toLowerCase()));

  // table_id effectif (dirty si dispo, sinon backend)
  const effectiveTable = (inv) => dirty[inv.id] !== undefined ? dirty[inv.id] : (inv.table_id || "");

  // Groupage : { tableId: [invités], __unassigned__: [...] }
  const grouped = React.useMemo(() => {
    const g = { [UNASSIGNED_ID]: [] };
    tables.forEach(t => { g[t.id] = []; });
    visible.forEach(inv => {
      const tid = effectiveTable(inv);
      if (tid && g[tid]) g[tid].push(inv);
      else g[UNASSIGNED_ID].push(inv);
    });
    Object.keys(g).forEach(k => {
      g[k].sort((a, b) => {
        const ca = (a.cote || "").localeCompare(b.cote || "");
        if (ca !== 0) return ca;
        return (a.prenom || "").localeCompare(b.prenom || "");
      });
    });
    return g;
  }, [visible, tables, dirty]);

  // Stats
  const totalOui = placable.length;
  const placedCount = placable.filter(i => effectiveTable(i)).length;
  const unassignedCount = totalOui - placedCount;
  const overcapTables = tables.filter(t => (grouped[t.id]?.length || 0) > (Number(t.capacite) || 8));

  // ─── Drag & drop — mode BATCH : modifie juste dirty, pas de backend ici ──
  const moveInvite = (inviteId, targetTableId) => {
    const inv = invites.find(i => i.id === inviteId);
    if (!inv) return;
    const current = effectiveTable(inv);
    const next = targetTableId === UNASSIGNED_ID ? "" : targetTableId;
    if (current === next) return;
    setDirty(prev => ({ ...prev, [inviteId]: next }));
  };

  // Sauvegarder — 1 seul appel bulkAssign
  const saveAll = async () => {
    if (dirtyCount === 0) return;
    setSaving(true); setSaveErr("");
    const ops = Object.keys(dirty).map(id => ({ invite_id: id, table_id: dirty[id] || "" }));
    try {
      const res = await adminBulkAssign(ops);
      if (!res?.ok) {
        setSaveErr(res?.error || "Erreur inconnue");
        setSaving(false);
        return;
      }
      const failed = (res.results || []).filter(r => !r.ok);
      if (failed.length > 0) {
        const failedIds = new Set(failed.map(f => f.invite_id));
        setDirty(prev => {
          const next = {};
          for (const id of Object.keys(prev)) if (failedIds.has(id)) next[id] = prev[id];
          return next;
        });
        setSaveErr(`${failed.length} affectation(s) refusée(s)`);
      } else {
        setDirty({});
      }
      setSaving(false);
      onReload?.();
    } catch (e) {
      setSaveErr("Réseau : " + e.message);
      setSaving(false);
    }
  };

  const cancelAll = () => {
    if (dirtyCount === 0) return;
    if (!confirm(`Annuler les ${dirtyCount} modification(s) en attente ?`)) return;
    setDirty({});
  };

  return (
    <AdPage
      eyebrow={`${totalOui} invités confirmés`}
      title="Plan de table"
      actions={
        <>
          <button className="btn sm" onClick={() => {
            if (dirtyCount > 0 && !confirm(`${dirtyCount} modification(s) en attente seront écrasées. Recharger quand même ?`)) return;
            if (dirtyCount > 0) setDirty({});
            onReload?.();
          }}>↻ Recharger</button>
        </>
      }
    >
      {/* Bandeau de stats + filtres */}
      <div style={{ display: "flex", gap: 12, marginBottom: 18, flexWrap: "wrap", alignItems: "center" }}>
        <KpiPill value={placedCount} label="Placés" tone="ok"/>
        <KpiPill value={unassignedCount} label="Non assignés" tone={unassignedCount > 0 ? "warn" : "ok"}/>
        <KpiPill value={overcapTables.length} label="Tables saturées" tone={overcapTables.length > 0 ? "alert" : "ok"}/>
        <div style={{ marginLeft: "auto", display: "flex", gap: 6 }}>
          {[
            { id: "all", l: "Tout" },
            { id: "E",   l: "Côté Enora" },
            { id: "A",   l: "Côté Antoine" },
          ].map(f => (
            <button key={f.id} onClick={() => setCoteFilter(f.id)} className="btn sm"
              style={{
                background: coteFilter === f.id ? AD.ink : "transparent",
                color: coteFilter === f.id ? AD.white : AD.inkSoft,
                borderColor: coteFilter === f.id ? AD.ink : AD.ruleSoft,
              }}>
              {f.l}
            </button>
          ))}
        </div>
      </div>

      {/* Kanban — grille 2 rangées * 6 colonnes (11 colonnes au total) pour
          tout afficher sans scroll horizontal. Les colonnes s'adaptent en
          largeur selon l'écran disponible. */}
      <div style={{
        display: "grid",
        gridTemplateColumns: "repeat(6, minmax(0, 1fr))",
        gap: 10,
        padding: "4px 0",
      }}>
        {/* Colonne non assignés */}
        <KanbanColumn
          column={{ id: UNASSIGNED_ID, name: "Non assignés", note: unassignedCount > 0 ? "À glisser dans une table" : "Tout est placé ✓", capacite: null }}
          invites={grouped[UNASSIGNED_ID]}
          tables={tables}
          onMove={moveInvite}
          tone="unassigned"
        />
        {tables.map(t => {
          const list = grouped[t.id] || [];
          const cap = Number(t.capacite) || 8;
          const overcap = list.length > cap;
          return (
            <KanbanColumn
              key={t.id}
              column={t}
              invites={list}
              tables={tables}
              onMove={moveInvite}
              overcap={overcap}
              count={list.length}
              cap={cap}
              onEdit={() => setEditingTableId(t.id)}
            />
          );
        })}
      </div>

      {/* Modal d'édition */}
      {editingTableId && (
        <TableEditModal
          table={tables.find(t => t.id === editingTableId)}
          onClose={() => setEditingTableId(null)}
          onSaved={() => { setEditingTableId(null); onReload?.(); }}
        />
      )}

      {dirtyCount > 0 && (
        <AdSaveBar count={dirtyCount} saving={saving} err={saveErr}
          onSave={saveAll} onCancel={cancelAll} label="Affectation"/>
      )}
    </AdPage>
  );
}

// ─── KPI pill ─────────────────────────────────────────────────────────
function KpiPill({ value, label, tone }) {
  const colors = {
    ok:    { bg: AD.sagePale, fg: AD.sage },
    warn:  { bg: AD.orPale,   fg: AD.orDeep },
    alert: { bg: AD.rougePale, fg: AD.rougeDeep },
  };
  const c = colors[tone] || colors.ok;
  return (
    <div style={{ padding: "6px 12px", background: c.bg, borderLeft: `3px solid ${c.fg}`, display: "flex", alignItems: "baseline", gap: 8 }}>
      <span style={{ fontFamily: AD.display, fontWeight: 700, fontSize: 18, color: c.fg, lineHeight: 1 }}>{value}</span>
      <span style={{ fontFamily: AD.mono, fontSize: 9, letterSpacing: 1.5, color: c.fg, textTransform: "uppercase" }}>{label}</span>
    </div>
  );
}

// ─── Colonne Kanban ───────────────────────────────────────────────────
function KanbanColumn({ column, invites, tables, onMove, overcap, count, cap, onEdit, tone }) {
  const [dragOver, setDragOver] = React.useState(false);

  const onDragOver = (e) => { e.preventDefault(); setDragOver(true); };
  const onDragLeave = () => setDragOver(false);
  const onDrop = (e) => {
    e.preventDefault();
    setDragOver(false);
    const inviteId = e.dataTransfer.getData("text/invite_id");
    if (inviteId) onMove(inviteId, column.id);
  };

  const isUnassigned = tone === "unassigned";

  return (
    <div
      onDragOver={onDragOver}
      onDragLeave={onDragLeave}
      onDrop={onDrop}
      style={{
        minWidth: 0,
        background: dragOver ? AD.orPale : (isUnassigned ? AD.paperDeep : AD.paper),
        border: `1px solid ${dragOver ? AD.or : (overcap ? AD.rouge : AD.ruleSoft)}`,
        borderLeft: `4px solid ${overcap ? AD.rouge : (isUnassigned ? AD.inkMute : AD.or)}`,
        display: "flex", flexDirection: "column",
        transition: "background .12s, border-color .12s",
        minHeight: 280,
        maxHeight: "calc(50vh - 60px)",
      }}>
      {/* Header compact */}
      <div style={{ padding: "8px 10px", borderBottom: `1px solid ${AD.ruleSoft}`, flexShrink: 0 }}>
        <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 6 }}>
          <div className="display-bold" style={{ fontSize: 13, color: overcap ? AD.rougeDeep : AD.ink, lineHeight: 1.15, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
            {column.name || column.nom || column.id}
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 4, flexShrink: 0 }}>
            {cap != null && (
              <span style={{ fontFamily: AD.mono, fontSize: 9, letterSpacing: 0.5, color: overcap ? AD.rougeDeep : AD.inkMute }}>
                {count}/{cap}
              </span>
            )}
            {!isUnassigned && (
              <button onClick={onEdit} title="Éditer" style={{
                background: "transparent", border: "none", padding: 2, cursor: "pointer",
                fontFamily: AD.mono, fontSize: 11, color: AD.inkMute,
              }}>✎</button>
            )}
          </div>
        </div>
        {column.notes && cap != null && (
          <div style={{ marginTop: 2, fontFamily: AD.italic, fontStyle: "italic", fontSize: 10, color: AD.inkSoft, lineHeight: 1.3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }} title={column.notes}>{column.notes}</div>
        )}
      </div>

      {/* Liste */}
      <div style={{ flex: 1, overflowY: "auto", padding: 6, display: "flex", flexDirection: "column", gap: 4, minHeight: 0 }}>
        {invites.length === 0 ? (
          <div style={{ padding: "16px 8px", textAlign: "center", fontFamily: AD.italic, fontStyle: "italic", fontSize: 10, color: AD.inkMute }}>
            {isUnassigned ? "Aucun invité non assigné 🎉" : "Glisser ici"}
          </div>
        ) : (
          invites.map(inv => (
            <InviteCard key={inv.id} inv={inv} tables={tables} columnId={column.id} onMove={onMove}/>
          ))
        )}
      </div>
    </div>
  );
}

// ─── Carte invité (draggable) ─────────────────────────────────────────
function InviteCard({ inv, tables, columnId, onMove }) {
  const [showMobileMenu, setShowMobileMenu] = React.useState(false);
  const coteColor = (inv.cote || "").toLowerCase().startsWith("e") ? AD.sage : AD.rouge;
  const isPlusOne = inv.categorie === "+1";
  const isEnfant  = inv.categorie === "Enfant";
  const isVege    = (inv.regime && inv.regime !== "omni") || (inv.allergies);

  const onDragStart = (e) => {
    e.dataTransfer.setData("text/invite_id", inv.id);
    e.dataTransfer.effectAllowed = "move";
  };

  return (
    <div
      draggable
      onDragStart={onDragStart}
      style={{
        background: AD.white,
        border: `1px solid ${AD.ruleSoft}`,
        borderLeft: `3px solid ${coteColor}`,
        padding: "5px 7px",
        cursor: "grab",
        position: "relative",
      }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 4 }}>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontSize: 11.5, fontFamily: AD.sans, fontWeight: 500, color: AD.ink, lineHeight: 1.2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
            {isPlusOne && <span style={{ color: AD.or, fontFamily: AD.mono, fontSize: 8, marginRight: 3 }}>+1</span>}
            {isEnfant && <span style={{ color: AD.or, fontFamily: AD.mono, fontSize: 8, marginRight: 3 }}>★</span>}
            {inv.prenom} {(inv.nom || "").charAt(0)}{inv.nom ? "." : ""}
          </div>
          <div style={{ marginTop: 1, fontFamily: AD.mono, fontSize: 8, letterSpacing: 0.5, color: coteColor, textTransform: "uppercase", lineHeight: 1 }}>
            {(inv.cote || "").charAt(0)}
            {isVege && <span style={{ marginLeft: 4, color: AD.or }}>●</span>}
            {inv.partner_id && <span style={{ marginLeft: 4, color: AD.inkMute }}>⚭</span>}
          </div>
        </div>
        <button
          onClick={() => setShowMobileMenu(v => !v)}
          title="Déplacer vers…"
          style={{ background: "transparent", border: "none", padding: 1, color: AD.inkMute, cursor: "pointer", fontFamily: AD.mono, fontSize: 10, flexShrink: 0 }}>↪</button>
      </div>

      {showMobileMenu && (
        <div style={{ position: "absolute", top: "100%", right: 0, zIndex: 10, background: AD.white, border: `1px solid ${AD.ruleSoft}`, padding: 4, marginTop: 2, minWidth: 160, maxHeight: 260, overflowY: "auto", boxShadow: "0 4px 14px rgba(0,0,0,0.1)" }}>
          <div style={{ padding: "4px 8px", fontFamily: AD.mono, fontSize: 9, letterSpacing: 1, color: AD.inkMute, textTransform: "uppercase" }}>Déplacer vers</div>
          <button onClick={() => { onMove(inv.id, UNASSIGNED_ID); setShowMobileMenu(false); }}
            style={mobileMenuItemStyle(columnId === UNASSIGNED_ID)}>Non assignés</button>
          {tables.map(t => (
            <button key={t.id} onClick={() => { onMove(inv.id, t.id); setShowMobileMenu(false); }}
              style={mobileMenuItemStyle(columnId === t.id)}>{t.nom || t.id}</button>
          ))}
        </div>
      )}
    </div>
  );
}

function mobileMenuItemStyle(active) {
  return {
    display: "block", width: "100%", textAlign: "left",
    padding: "6px 10px", background: active ? AD.paper : "transparent", border: "none",
    fontFamily: AD.sans, fontSize: 12, color: active ? AD.inkMute : AD.ink,
    cursor: active ? "default" : "pointer",
  };
}

// ─── Modal d'édition d'une table ──────────────────────────────────────
function TableEditModal({ table, onClose, onSaved }) {
  const [nom, setNom] = React.useState(table?.nom || "");
  const [capacite, setCapacite] = React.useState(Number(table?.capacite) || 8);
  const [notes, setNotes] = React.useState(table?.notes || "");
  const [saving, setSaving] = React.useState(false);
  const [err, setErr] = React.useState("");

  const save = async () => {
    setSaving(true); setErr("");
    const res = await adminUpdateTable(table.id, { nom: nom.trim(), capacite: Number(capacite) || 8, notes: notes.trim() });
    setSaving(false);
    if (res?.ok) onSaved?.();
    else setErr(res?.error || "Erreur inconnue");
  };

  return (
    <div onClick={onClose} style={{
      position: "fixed", inset: 0, background: "rgba(26,22,18,0.55)", zIndex: 1000,
      display: "flex", alignItems: "center", justifyContent: "center", padding: 20,
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        background: AD.white, maxWidth: 460, width: "100%",
        border: `1px solid ${AD.ruleSoft}`, padding: 28,
      }}>
        <div className="eyebrow">Éditer la table</div>
        <div className="display-bold" style={{ fontSize: 22, marginTop: 4, marginBottom: 18 }}>
          {table.nom || table.id}
        </div>

        <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
          <label>
            <div style={{ fontFamily: AD.mono, fontSize: 10, letterSpacing: 1.5, color: AD.inkSoft, marginBottom: 4, textTransform: "uppercase" }}>Nom</div>
            <input type="text" value={nom} onChange={(e) => setNom(e.target.value)} style={editInput()} placeholder="ex: Table d'honneur"/>
          </label>
          <label>
            <div style={{ fontFamily: AD.mono, fontSize: 10, letterSpacing: 1.5, color: AD.inkSoft, marginBottom: 4, textTransform: "uppercase" }}>Capacité</div>
            <input type="number" min="2" max="20" value={capacite} onChange={(e) => setCapacite(e.target.value)} style={editInput()} />
          </label>
          <label>
            <div style={{ fontFamily: AD.mono, fontSize: 10, letterSpacing: 1.5, color: AD.inkSoft, marginBottom: 4, textTransform: "uppercase" }}>Note d'ambiance</div>
            <textarea value={notes} onChange={(e) => setNotes(e.target.value)} rows="2" style={{ ...editInput(), resize: "vertical", minHeight: 60 }} placeholder="ex: Témoins & équipe rapprochée"/>
          </label>
        </div>

        {err && <div style={{ marginTop: 12, padding: "8px 12px", background: AD.rougePale, color: AD.rougeDeep, fontFamily: AD.mono, fontSize: 11 }}>⚠ {err}</div>}

        <div style={{ marginTop: 22, display: "flex", gap: 8, justifyContent: "flex-end" }}>
          <button className="btn" onClick={onClose} disabled={saving}>Annuler</button>
          <button className="btn gold" onClick={save} disabled={saving}>{saving ? "…" : "✓ Enregistrer"}</button>
        </div>
      </div>
    </div>
  );
}

function editInput() {
  return {
    width: "100%", padding: "10px 12px",
    background: AD.paper, border: `1px solid ${AD.ruleSoft}`,
    fontFamily: AD.sans, fontSize: 14, color: AD.ink, outline: "none",
  };
}

Object.assign(window, { TablesPage });
