function SnapshotPanel({ onClose, state, connected }) {
  const [snapshots, setSnapshots] = useState({});
  const [loading, setLoading] = useState(true);
  const [editingSlot, setEditingSlot] = useState(null);
  const [editName, setEditName] = useState('');
  const [saveFlash, setSaveFlash] = useState(null);
  const [discardEdits, setDiscardEdits] = useState(false);
  const holdTimer = useRef(null);
  const currentSlot = state?.current_snapshot ?? 0;

  const loadSnapshots = useCallback(async () => {
    if (!connected) { setLoading(false); return; }
    try {
      const data = await apiGet('/api/snapshots');
      setSnapshots(data.snapshots || {});
    } catch (e) { /* ignore */ }
    setLoading(false);
  }, [connected]);

  useEffect(() => { loadSnapshots(); }, [loadSnapshots]);

  // Handle press & hold vs tap
  const handlePointerDown = (slot) => {
    holdTimer.current = setTimeout(() => {
      // Long press → save
      holdTimer.current = null;
      handleSave(slot);
    }, 600);
  };

  const handlePointerUp = (slot) => {
    if (holdTimer.current) {
      clearTimeout(holdTimer.current);
      holdTimer.current = null;
      // Short tap → recall (or save if empty)
      if (snapshots[String(slot)]) {
        handleRecall(slot);
      } else {
        handleSave(slot);
      }
    }
  };

  const handlePointerLeave = () => {
    if (holdTimer.current) {
      clearTimeout(holdTimer.current);
      holdTimer.current = null;
    }
  };

  const handleSave = async (slot) => {
    try {
      await apiPost(`/api/snapshots/${slot}/save`);
      setSaveFlash(slot);
      setTimeout(() => setSaveFlash(null), 800);
      await loadSnapshots();
    } catch (e) { /* ignore */ }
  };

  const handleRecall = async (slot) => {
    try {
      await apiPost(`/api/snapshots/${slot}/recall`);
      await loadSnapshots();
    } catch (e) { /* ignore */ }
  };

  const handleRename = async (slot) => {
    if (!editName.trim()) return;
    try {
      await apiPut(`/api/snapshots/${slot}`, { name: editName.trim() });
      setEditingSlot(null);
      await loadSnapshots();
    } catch (e) { /* ignore */ }
  };

  const handleDelete = async (slot) => {
    try {
      await apiDelete(`/api/snapshots/${slot}`);
      await loadSnapshots();
    } catch (e) { /* ignore */ }
  };

  return (
    <SlidePanel onClose={onClose}>
      <div className="screen-header" style={{ justifyContent: "space-between" }}>
        <div className="screen-title">Snapshots</div>
        <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
          <label style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 10, color: T.textDim, cursor: "pointer" }}>
            <div className={`switch ${discardEdits ? 'on' : ''}`} style={{ width: 28, height: 16 }}
              onClick={() => setDiscardEdits(d => !d)}>
              <div style={{ position: "absolute", top: 1, left: discardEdits ? 11 : 1, width: 13, height: 13,
                borderRadius: "50%", background: "#fff", transition: "left .15s" }} />
            </div>
            Discard Edits
          </label>
        </div>
      </div>
      <div className="screen-body" style={{ gap: 8 }}>
        <div style={{ fontSize: 11, color: T.textDim, marginBottom: 4 }}>
          Tap to recall &middot; Hold to save
          {discardEdits && <span style={{ color: T.amber, marginLeft: 8 }}>Edits will be discarded on recall</span>}
        </div>
        <div className="snap-grid">
          {[1,2,3,4,5,6,7,8].map(slot => {
            const snap = snapshots[String(slot)];
            const isActive = currentSlot === slot;
            const isFilled = !!snap;
            const isEditing = editingSlot === slot;
            const isFlashing = saveFlash === slot;
            return (
              <div key={slot} className={`snap-slot ${isFilled ? "filled" : ""} ${isActive ? "active" : ""}`}
                onMouseDown={() => handlePointerDown(slot)}
                onTouchStart={() => handlePointerDown(slot)}
                onMouseUp={() => handlePointerUp(slot)}
                onTouchEnd={() => handlePointerUp(slot)}
                onMouseLeave={handlePointerLeave}
                onTouchCancel={handlePointerLeave}
                style={isFlashing ? { background: 'rgba(58,184,122,.15)' } : {}}>
                <div className="snap-num">{slot}</div>
                {isEditing ? (
                  <input className="snap-name-input" value={editName}
                    onChange={e => setEditName(e.target.value)}
                    onBlur={() => handleRename(slot)}
                    onKeyDown={e => { if (e.key === 'Enter') handleRename(slot); if (e.key === 'Escape') setEditingSlot(null); }}
                    autoFocus onClick={e => e.stopPropagation()} />
                ) : snap ? (
                  <div className="snap-name"
                    onClick={e => {
                      e.stopPropagation();
                      setEditingSlot(slot);
                      setEditName(snap.name);
                    }}>{snap.name}</div>
                ) : (
                  <div className="snap-empty">Empty</div>
                )}
                {isFlashing && <div className="snap-save-indicator" />}
                {isActive && <div style={{ position: "absolute", bottom: -2, left: "50%", transform: "translateX(-50%)",
                  width: 20, height: 2, borderRadius: 1, background: T.amber }} />}
              </div>
            );
          })}
        </div>

        {/* Delete button for active snapshot */}
        {currentSlot > 0 && snapshots[String(currentSlot)] && (
          <div style={{ marginTop: 8, display: "flex", gap: 8 }}>
            <button className="btn btn-danger btn-sm" style={{ flex: 1 }}
              onClick={() => handleDelete(currentSlot)}>
              Delete Snapshot {currentSlot}
            </button>
          </div>
        )}
      </div>
    </SlidePanel>
  );
}
