function createTodayCompletion({ todayWork, todaySync, workSession, refresh, warm, announce, advance = null,
  completeActivation = () => globalThis.stackchainFirstTaskCompleting?.(),
  now = Date.now, ttlMs = 10000, onOffer = null, onClear = null,
  setTimer = globalThis.setTimeout, clearTimer = globalThis.clearTimeout,
  undo = [globalThis.document?.getElementById('today-completion-undo'),
    globalThis.document?.getElementById('undo-today-completion')] }) {
  const [undoReceipt, undoControl] = undo;
  let pending = null;
  let expiryTimer = null;

  function clear() {
    if (expiryTimer !== null) clearTimer?.(expiryTimer);
    expiryTimer = null;
    pending = null;
    if (undoReceipt) undoReceipt.hidden = true;
    onClear?.();
  }

  function completeTodayItem(item, options = {}) {
    const identity = item && todayWork.identity(item);
    const snapshot = item && (todayWork.capture?.(item) ||
      (identity ? { id: identity, index: 0, ids: [identity], plan: { capacity_minutes: null, estimates: {} } } : null));
    if (!snapshot || !todayWork.remove(item)) {
      announce(options.failureMessage || 'Could not update Today on this device. Try again.');
      return false;
    }
    const operations = [
      {action:'remove', item_id:identity},
      ...(completeActivation() ? [{action:'activate', item_id:'first-task', activation_state:'complete'}] : []),
    ];
    const admitted = todaySync.enqueueBatch ? todaySync.enqueueBatch(operations) :
      todaySync.enqueue('remove', identity);
    if (!admitted) {
      todayWork.restore?.(snapshot);
      announce(options.admissionFailureMessage ||
        'Device storage is full. Free space and try again.');
      return false;
    }
    todaySync.flush();
    refresh();
    warm();
    (advance || (() => workSession.complete()))();
    announce(options.successMessage || 'Done for Today. The Gitea item is unchanged.');
    pending = { snapshot, expires_at: now() + ttlMs };
    if (undoReceipt) undoReceipt.hidden = false;
    if (undoControl) undoControl.disabled = false;
    onOffer?.({ identity: snapshot.id, expires_at: pending.expires_at });
    expiryTimer = setTimer?.(clear, ttlMs) ?? null;
    expiryTimer?.unref?.();
    return true;
  }

  completeTodayItem.undo = () => {
    if (!pending) return 'missing';
    if (now() >= pending.expires_at) {
      clear();
      announce('Undo expired. Add the item back to Today from My Work.');
      return 'expired';
    }
    const snapshot = pending.snapshot;
    const result = todayWork.restore?.(snapshot) || 'unavailable';
    if (result !== 'restored') {
      const messages = {
        full: 'Today is full. Remove another item before adding this work back.',
        changed: 'Today changed on this device. Add the item back from My Work.',
        unavailable: 'Could not restore Today on this device. Add the item back from My Work.',
      };
      clear();
      announce(messages[result] || messages.unavailable);
      return result;
    }
    todaySync.enqueue('add', snapshot.id);
    for (let index = snapshot.ids.length - 1; index > snapshot.index; index -= 1) {
      todaySync.enqueue('move', snapshot.id, 'up');
    }
    todaySync.flush();
    refresh();
    warm();
    clear();
    announce('Restored to Today. Your current work session is unchanged.');
    return 'restored';
  };
  completeTodayItem.clear = clear;
  undoControl?.addEventListener?.('click', () => {
    undoControl.disabled = true;
    if (completeTodayItem.undo() !== 'restored') undoControl.disabled = false;
  });
  return completeTodayItem;
}

if (typeof module !== 'undefined' && module.exports) module.exports = createTodayCompletion;
