export interface LatestAsyncCommitter<T> {
  enqueue: (value: T) => void;
  hasPending: () => boolean;
  isBusy: () => boolean;
  waitForIdle: () => Promise<void>;
  dispose: () => void;
}

export interface LatestAsyncCommitResult<T> {
  value: T;
  applied: boolean;
  superseded: boolean;
}

/**
 * Serializes an expensive settings commit while retaining only the newest
 * value submitted during the active request. This keeps slider and color
 * interactions from creating an unbounded backend queue.
 */
export function createLatestAsyncCommitter<T>({
  commit,
  onSettled,
}: {
  commit: (value: T) => Promise<boolean>;
  onSettled?: (result: LatestAsyncCommitResult<T>) => void;
}): LatestAsyncCommitter<T> {
  let pending: T | undefined;
  let running = false;
  let disposed = false;
  const idleWaiters = new Set<() => void>();

  const settleIdleWaiters = () => {
    if (running || pending !== undefined) return;
    for (const resolve of idleWaiters) resolve();
    idleWaiters.clear();
  };

  const drain = async () => {
    if (running || disposed) return;
    running = true;

    try {
      while (!disposed && pending !== undefined) {
        const value = pending;
        pending = undefined;

        let applied = false;
        try {
          applied = await commit(value);
        } catch {
          applied = false;
        }

        if (disposed) return;
        onSettled?.({
          value,
          applied,
          superseded: pending !== undefined,
        });
      }
    } finally {
      running = false;
      if (!disposed && pending !== undefined) {
        void drain();
      } else {
        settleIdleWaiters();
      }
    }
  };

  return {
    enqueue(value) {
      if (disposed) return;
      pending = value;
      void drain();
    },
    hasPending: () => pending !== undefined,
    isBusy: () => running || pending !== undefined,
    waitForIdle() {
      if (!running && pending === undefined) return Promise.resolve();
      return new Promise<void>((resolve) => idleWaiters.add(resolve));
    },
    dispose() {
      disposed = true;
      pending = undefined;
      settleIdleWaiters();
    },
  };
}
