function createLaterPicker({
  now = () => new Date(),
  history = null,
  eventTarget = null,
  onState = () => {},
  onConfirm = () => false,
} = {}) {
  let active = false;
  let item = null;
  let trigger = null;
  let context = null;
  let afterClose = null;
  let committed = false;
  let started = false;

  function parse(input) {
    const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/.exec(String(input || ''));
    if (!match) return { ok:false, message:'Choose a valid local date and time.' };
    const parts = match.slice(1).map(Number);
    const value = new Date(parts[0], parts[1] - 1, parts[2], parts[3], parts[4], 0, 0);
    const sameLocalTime = value.getFullYear() === parts[0] &&
      value.getMonth() === parts[1] - 1 && value.getDate() === parts[2] &&
      value.getHours() === parts[3] && value.getMinutes() === parts[4];
    if (!sameLocalTime) return { ok:false, message:'Choose a valid local date and time.' };
    if (value <= now()) return { ok:false, message:'Choose a future date and time.' };
    return { ok:true, value };
  }

  function formatLocal(value) {
    const pad = part => String(part).padStart(2, '0');
    return value.getFullYear() + '-' + pad(value.getMonth() + 1) + '-' + pad(value.getDate()) +
      'T' + pad(value.getHours()) + ':' + pad(value.getMinutes());
  }

  function deactivate() {
    if (!active) return false;
    const restore = trigger;
    active = false;
    item = null;
    trigger = null;
    context = null;
    committed = false;
    onState({ open:false, message:'' });
    restore?.focus?.();
    const cleanup = afterClose;
    afterClose = null;
    cleanup?.();
    return true;
  }

  function start() {
    if (started || !eventTarget) return;
    started = true;
    eventTarget.addEventListener('popstate', event => {
      if (active && !event.state?.laterPicker) deactivate();
    });
    eventTarget.addEventListener('keydown', event => {
      if (active && event.key === 'Escape') {
        event.preventDefault?.();
        close();
      }
    });
  }

  function open(nextItem, nextTrigger, nextContext = 'card') {
    if (!nextItem || active) return false;
    active = true;
    item = nextItem;
    trigger = nextTrigger || null;
    context = nextContext;
    committed = false;
    const suggested = new Date(now().getTime() + 60 * 60 * 1000);
    suggested.setMinutes(Math.ceil(suggested.getMinutes() / 15) * 15, 0, 0);
    if (history) history.pushState({ ...(history.state || {}), laterPicker:true }, '');
    onState({ open:true, message:'', value:formatLocal(suggested) });
    return true;
  }

  function close() {
    if (!active) return false;
    if (history?.state?.laterPicker) history.back();
    else deactivate();
    return true;
  }

  function submit(input) {
    if (!active || committed) return false;
    const result = parse(input);
    if (!result.ok) {
      onState({ open:true, message:result.message, value:String(input || '') });
      return false;
    }
    const confirmed = typeof item?.confirm === 'function' ? item.confirm(result.value) :
      onConfirm(item, result.value, context);
    if (confirmed !== true && typeof confirmed !== 'function') return false;
    committed = true;
    afterClose = typeof confirmed === 'function' ? confirmed : null;
    close();
    return true;
  }

  return { parse, start, open, close, submit, current:() => active };
}

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