// tableplan.jsx — bordplan-editor: lærred, drag & drop, zoom/pan, regler
const { useState: useStateTP, useRef: useRefTP, useEffect: useEffectTP, useMemo: useMemoTP } = React;

const CANVAS_W = 2000, CANVAS_H = 1300;
const clamp = (v, a, b) => Math.max(a, Math.min(b, v));

function TablePlan({ project, mut, tweaks }) {
  const { layout, SR } = window.PlaniqGeo;
  const wrapRef = useRefTP(null);
  const drag = useRefTP(null);
  const pointers = useRefTP(new Map());
  const pinch = useRefTP(null);
  const hoverRef = useRefTP(null);

  const [view, _setView] = useStateTP({ x: 60, y: 40, scale: 0.78 });
  const viewRef = useRefTP(view);
  const setView = (v) => { const nv = typeof v === 'function' ? v(viewRef.current) : v; viewRef.current = nv; _setView(nv); };

  const [ghost, setGhost] = useStateTP(null);
  const [hoverSeat, _setHoverSeat] = useStateTP(null);
  const setHoverSeat = (s) => { hoverRef.current = s; _setHoverSeat(s); };
  const [selTable, setSelTable] = useStateTP(null);
  const [addMenu, setAddMenu] = useStateTP(false);
  const [showWarn, setShowWarn] = useStateTP(false);
  const [autoBusy, setAutoBusy] = useStateTP(false);
  const [confirmAuto, setConfirmAuto] = useStateTP(false);

  const guestById = (id) => project.guests.find(g => g.id === id);
  const seatGuest = (tid, idx) => project.guests.find(g => g.seat && g.seat.tableId === tid && g.seat.index === idx);

  // ---- layouts memo ----
  const layouts = useMemoTP(() => {
    const m = {}; project.tables.forEach(t => { m[t.id] = layout(t); }); return m;
  }, [project.tables]);

  // ---- rule violations ----
  const violations = useMemoTP(() => {
    const out = [];
    project.rules.forEach(r => {
      const a = guestById(r.a), b = guestById(r.b);
      if (!a || !b) return;
      if (r.type === 'apart' && a.seat && b.seat && a.seat.tableId === b.seat.tableId) {
        out.push({ ...r, a, b, msg: `${a.name} og ${b.name} sidder sammen` });
      }
      if (r.type === 'together' && a.seat && b.seat && a.seat.tableId !== b.seat.tableId) {
        out.push({ ...r, a, b, msg: `${a.name} og ${b.name} sidder ved hver sit bord` });
      }
    });
    return out;
  }, [project.rules, project.guests]);
  const badTables = useMemoTP(() => {
    const s = new Set();
    violations.forEach(v => { if (v.a.seat) s.add(v.a.seat.tableId); if (v.b.seat) s.add(v.b.seat.tableId); });
    return s;
  }, [violations]);

  const placed = project.guests.filter(g => g.seat).length;
  const totalSeats = project.tables.reduce((s, t) => s + t.seats, 0);

  // ---- global pointer handlers (bound once) ----
  useEffectTP(() => {
    const move = (e) => {
      const d = drag.current;
      // update tracked pointer
      if (pointers.current.has(e.pointerId)) pointers.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
      if (!d) return;
      if (d.kind === 'guest') {
        setGhost({ x: e.clientX, y: e.clientY, guest: d.guest });
        const el = document.elementFromPoint(e.clientX, e.clientY);
        const seatEl = el && el.closest('[data-seat-table]');
        if (seatEl) setHoverSeat({ tableId: seatEl.dataset.seatTable, index: +seatEl.dataset.seatIndex });
        else setHoverSeat(null);
      } else if (d.kind === 'table') {
        // Først når fingeren/musen er flyttet mere end ~5px regnes det som et træk.
        if (!d.moved && Math.hypot(e.clientX - d.sx, e.clientY - d.sy) < 5) return;
        d.moved = true;
        const dx = (e.clientX - d.sx) / d.scale, dy = (e.clientY - d.sy) / d.scale;
        mut.updateTable(d.id, { x: clamp(d.ox + dx, 40, CANVAS_W - 40), y: clamp(d.oy + dy, 40, CANVAS_H - 40) });
      } else if (d.kind === 'pan') {
        if (pointers.current.size >= 2) return; // pinch takes over
        setView({ ...viewRef.current, x: d.ox + (e.clientX - d.sx), y: d.oy + (e.clientY - d.sy) });
      } else if (d.kind === 'pinch') {
        const pts = [...pointers.current.values()];
        if (pts.length < 2) return;
        const dist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
        const mid = { x: (pts[0].x + pts[1].x) / 2, y: (pts[0].y + pts[1].y) / 2 };
        const rect = wrapRef.current.getBoundingClientRect();
        const ns = clamp(pinch.current.scale * (dist / pinch.current.dist), 0.3, 2.2);
        const k = ns / pinch.current.scale;
        const mx = pinch.current.mid.x - rect.left, my = pinch.current.mid.y - rect.top;
        setView({ scale: ns, x: mx - (mx - pinch.current.x) * k, y: my - (my - pinch.current.y) * k });
      }
    };
    const up = (e) => {
      pointers.current.delete(e.pointerId);
      const d = drag.current;
      if (d && d.kind === 'guest') {
        const hov = hoverRef.current;
        if (hov) mut.assignSeat(d.guest.id, hov.tableId, hov.index, d.from);
        else if (d.from) {
          const el = document.elementFromPoint(e.clientX, e.clientY);
          if (el && el.closest('[data-unseat-zone]')) mut.unseat(d.guest.id);
        }
      }
      // Bord-tap (ingen bevægelse) → åbn popup; bord-træk → ingenting (flytningen er sket).
      if (d && d.kind === 'table' && !d.moved) setSelTable(d.id);
      if (pointers.current.size === 0) { drag.current = null; pinch.current = null; }
      else if (d && (d.kind === 'pan' || d.kind === 'pinch') && pointers.current.size === 1) {
        const p = [...pointers.current.values()][0];
        drag.current = { kind: 'pan', sx: p.x, sy: p.y, ox: viewRef.current.x, oy: viewRef.current.y, scale: viewRef.current.scale };
      }
      setGhost(null); setHoverSeat(null);
    };
    window.addEventListener('pointermove', move, { passive: false });
    window.addEventListener('pointerup', up);
    window.addEventListener('pointercancel', up);
    return () => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up); window.removeEventListener('pointercancel', up); };
  }, []);

  // wheel zoom (native, non-passive)
  useEffectTP(() => {
    const el = wrapRef.current; if (!el) return;
    const onWheel = (e) => {
      e.preventDefault();
      const rect = el.getBoundingClientRect();
      const mx = e.clientX - rect.left, my = e.clientY - rect.top;
      const v = viewRef.current;
      const factor = Math.exp(-e.deltaY * 0.0014);
      const ns = clamp(v.scale * factor, 0.3, 2.2);
      const k = ns / v.scale;
      setView({ scale: ns, x: mx - (mx - v.x) * k, y: my - (my - v.y) * k });
    };
    el.addEventListener('wheel', onWheel, { passive: false });
    return () => el.removeEventListener('wheel', onWheel);
  }, []);

  // ---- interactions ----
  const startGuestDrag = (e, guest, from) => {
    e.preventDefault(); e.stopPropagation();
    drag.current = { kind: 'guest', guest, from };
    setGhost({ x: e.clientX, y: e.clientY, guest });
    setSelTable(null);
  };
  const startTableDrag = (e, t) => {
    if (e.button === 2) return;
    e.stopPropagation();
    // Vælg ikke bordet endnu — vent og se om det bliver et tap (åbn popup) eller et træk (flyt).
    drag.current = { kind: 'table', id: t.id, sx: e.clientX, sy: e.clientY, ox: t.x, oy: t.y, scale: viewRef.current.scale, moved: false };
  };
  const onBgDown = (e) => {
    setSelTable(null); setAddMenu(false);
    pointers.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
    if (pointers.current.size === 1) {
      drag.current = { kind: 'pan', sx: e.clientX, sy: e.clientY, ox: viewRef.current.x, oy: viewRef.current.y, scale: viewRef.current.scale };
    } else if (pointers.current.size === 2) {
      const pts = [...pointers.current.values()];
      pinch.current = { dist: Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y), mid: { x: (pts[0].x + pts[1].x) / 2, y: (pts[0].y + pts[1].y) / 2 }, x: viewRef.current.x, y: viewRef.current.y, scale: viewRef.current.scale };
      drag.current = { kind: 'pinch' };
    }
  };

  const zoomBtn = (f) => {
    const rect = wrapRef.current.getBoundingClientRect();
    const v = viewRef.current, ns = clamp(v.scale * f, 0.3, 2.2), k = ns / v.scale;
    const mx = rect.width / 2, my = rect.height / 2;
    setView({ scale: ns, x: mx - (mx - v.x) * k, y: my - (my - v.y) * k });
  };
  const fit = () => {
    const rect = wrapRef.current.getBoundingClientRect();
    if (!project.tables.length) { setView({ x: 60, y: 40, scale: 0.78 }); return; }
    let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
    project.tables.forEach(t => { const L = layouts[t.id]; minX = Math.min(minX, t.x - L.w / 2); maxX = Math.max(maxX, t.x + L.w / 2); minY = Math.min(minY, t.y - L.h / 2); maxY = Math.max(maxY, t.y + L.h / 2); });
    const pad = 80, bw = maxX - minX + pad * 2, bh = maxY - minY + pad * 2;
    const scale = clamp(Math.min(rect.width / bw, rect.height / bh), 0.3, 1.4);
    setView({ scale, x: rect.width / 2 - (minX - pad + bw / 2) * scale, y: rect.height / 2 - (minY - pad + bh / 2) * scale });
  };
  const centerTable = (tid) => {
    const t = project.tables.find(x => x.id === tid); if (!t) return;
    const rect = wrapRef.current.getBoundingClientRect();
    const s = clamp(viewRef.current.scale, 0.6, 1.2);
    setView({ scale: s, x: rect.width / 2 - t.x * s, y: rect.height / 2 - t.y * s });
    setSelTable(tid);
  };

  // Åbn bekræftelse først — auto-placering kan ikke fortrydes.
  const doAutoPlace = () => setConfirmAuto(true);
  const runAutoPlace = () => {
    setConfirmAuto(false);
    setAutoBusy(true);
    setTimeout(() => { mut.autoPlace(); setAutoBusy(false); }, 480);
  };

  const sel = project.tables.find(t => t.id === selTable);

  return (
    <div className="row tp-root" style={{ height: '100%', alignItems: 'stretch' }}>
      <GuestSource project={project} startGuestDrag={startGuestDrag} dragging={!!ghost} onAutoPlace={doAutoPlace} autoBusy={autoBusy} />

      <div className="col" style={{ flex: 1, minWidth: 0, position: 'relative' }}>
        {/* toolbar */}
        <div className="row gap-3 mobile-wrap" style={{ padding: '12px 18px', borderBottom: '1px solid var(--line)', background: 'var(--surface)', zIndex: 6 }}>
          <div style={{ position: 'relative' }}>
            <button className="btn btn-primary" onClick={() => setAddMenu(m => !m)}><Icon name="plus" size={19} /> Tilføj bord <Icon name="chevD" size={17} /></button>
            {addMenu && <AddTableMenu defaultType={tweaks.defaultTable} onPick={(type) => { mut.addTable(type); setAddMenu(false); }} onClose={() => setAddMenu(false)} />}
          </div>
          <div className="spring"></div>
          {violations.length > 0 && (
            <div style={{ position: 'relative' }}>
              <button className="btn btn-sm" style={{ background: 'var(--warn-soft)', color: 'var(--warn)', border: 'none' }} onClick={() => setShowWarn(s => !s)}>
                <Icon name="warn" size={18} /> {violations.length} {violations.length === 1 ? 'advarsel' : 'advarsler'}
              </button>
              {showWarn && <WarningsPopover violations={violations} onJump={(tid) => { centerTable(tid); setShowWarn(false); }} onClose={() => setShowWarn(false)} />}
            </div>
          )}
          <span className="chip" style={{ background: 'var(--surface-2)', height: 40 }}>
            <span style={{ width: 9, height: 9, borderRadius: '50%', background: placed === project.guests.length && placed > 0 ? 'var(--sage)' : 'var(--honey)' }}></span>
            {placed}/{project.guests.length} placeret · {totalSeats} pladser
          </span>
          <div className="row gap-2">
            <button className="btn btn-icon" onClick={() => zoomBtn(1 / 1.2)}><Icon name="zoomOut" size={20} /></button>
            <button className="btn btn-icon" onClick={() => zoomBtn(1.2)}><Icon name="zoomIn" size={20} /></button>
            <button className="btn btn-icon" onClick={fit}><Icon name="fit" size={20} /></button>
          </div>
        </div>

        {/* canvas */}
        <div ref={wrapRef} onPointerDown={onBgDown} onContextMenu={e => e.preventDefault()} style={{
          flex: 1, position: 'relative', overflow: 'hidden', touchAction: 'none',
          background: 'var(--bg-2)', cursor: ghost ? 'grabbing' : 'default',
        }}>
          {project.tables.length === 0 && <EmptyCanvas onAdd={() => setAddMenu(true)} />}
          <div style={{
            position: 'absolute', top: 0, left: 0, width: CANVAS_W, height: CANVAS_H,
            transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})`, transformOrigin: '0 0',
            backgroundImage: 'radial-gradient(var(--line-2) 1.5px, transparent 1.5px)', backgroundSize: '40px 40px',
          }}>
            {project.tables.map(t => (
              <TableNode key={t.id} t={t} L={layouts[t.id]} selected={selTable === t.id} bad={badTables.has(t.id)}
                seatGuest={seatGuest} groups={project.groups} hoverSeat={hoverSeat} dragging={!!ghost}
                onTableDown={(e) => startTableDrag(e, t)} onSeatDown={startGuestDrag} />
            ))}
          </div>

          {/* contextual table bar */}
          {sel && <TableContextBar t={sel} L={layouts[sel.id]} usedSeats={project.guests.filter(g => g.seat && g.seat.tableId === sel.id).length}
            seatedDiet={dietTally(project, sel.id)} mut={mut} onClose={() => setSelTable(null)} />}
        </div>
      </div>

      {/* drag ghost */}
      {ghost && <DragGhost ghost={ghost} groups={project.groups} />}

      {confirmAuto && <AutoPlaceConfirm
        count={project.guests.filter(g => !g.seat).length}
        onConfirm={runAutoPlace}
        onClose={() => setConfirmAuto(false)} />}
    </div>
  );
}

/* ---- Bekræftelse før auto-placering (kan ikke fortrydes) ----
   Fortsæt-knappen er låst med en 5-sekunders nedtælling. */
function AutoPlaceConfirm({ count, onConfirm, onClose }) {
  const COUNTDOWN = 5;
  const [left, setLeft] = useStateTP(COUNTDOWN);
  useEffectTP(() => {
    if (left <= 0) return;
    const t = setTimeout(() => setLeft(l => l - 1), 1000);
    return () => clearTimeout(t);
  }, [left]);
  const ready = left <= 0;

  return (
    <Modal onClose={onClose} maxWidth={420}>
      <h2 style={{ marginBottom: 8 }}>Auto-placér alle gæster?</h2>
      <p className="muted" style={{ fontSize: 16, lineHeight: 1.5, marginBottom: 22 }}>
        Planiq fordeler {count} {count === 1 ? 'gæst' : 'gæster'} på de ledige pladser ud fra grupper og regler.
        <strong style={{ color: 'var(--ink)' }}> Det overskriver den nuværende placering og kan ikke fortrydes.</strong>
      </p>
      <div className="row gap-3" style={{ justifyContent: 'flex-end' }}>
        <button className="btn btn-ghost" onClick={onClose}>Annullér</button>
        <button className="btn btn-sage" disabled={!ready} onClick={onConfirm}
          style={!ready ? { opacity: 0.6, cursor: 'not-allowed' } : {}}>
          {ready ? 'Fortsæt' : `Fortsæt (${left})`}
        </button>
      </div>
    </Modal>
  );
}

function dietTally(project, tid) {
  const t = {};
  project.guests.filter(g => g.seat && g.seat.tableId === tid).forEach(g => g.diet.forEach(d => t[d] = (t[d] || 0) + 1));
  return t;
}

function DragGhost({ ghost, groups }) {
  const g = ghost.guest;
  const grp = groups.find(x => x.id === g.group);
  return (
    <div style={{ position: 'fixed', left: ghost.x, top: ghost.y, transform: 'translate(-50%, -50%) rotate(-4deg) scale(1.08)', zIndex: 200, pointerEvents: 'none', filter: 'drop-shadow(0 14px 22px rgba(78,60,40,.35))' }}>
      <div className="row gap-2" style={{ background: 'var(--surface)', borderRadius: 999, padding: '6px 14px 6px 6px', border: '2px solid var(--accent)' }}>
        <Avatar name={g.name} group={grp?.color} size={36} child={g.child} />
        <span style={{ fontWeight: 800, fontSize: 15 }}>{g.name.split(' ')[0]}</span>
      </div>
    </div>
  );
}

function EmptyCanvas({ onAdd }) {
  return (
    <div className="col center" style={{ position: 'absolute', inset: 0, zIndex: 4, textAlign: 'center', gap: 14, pointerEvents: 'none' }}>
      <div style={{ width: 92, height: 92, borderRadius: 30, background: 'var(--surface)', display: 'grid', placeItems: 'center', boxShadow: 'var(--shadow)' }}>
        <Icon name="table" size={44} color="var(--accent)" sw={1.5} />
      </div>
      <h3 style={{ fontSize: 25 }}>Tomt lærred</h3>
      <p className="muted" style={{ fontSize: 17, maxWidth: 360, lineHeight: 1.5 }}>Tilføj dit første bord, så kan du begynde at trække gæster i stolene.</p>
      <button className="btn btn-primary btn-lg" style={{ pointerEvents: 'auto', marginTop: 4 }} onClick={onAdd}><Icon name="plus" size={20} /> Tilføj bord</button>
    </div>
  );
}

Object.assign(window, { TablePlan });
