const STATE_KEY = "__codexRemoteBackLayer";
const STALE_STATE_KEY = "__codexRemoteStaleBackLayer";

export function createBackNavigation({ history, eventTarget }) {
  const layers = [];
  const pendingTraversals = [];
  const waitingContinuations = [];
  let disposed = false;

  if (history.state?.[STATE_KEY]) {
    const state = { ...history.state };
    delete state[STATE_KEY];
    history.replaceState(state, "");
  }

  function currentLayerId() {
    return history.state?.[STATE_KEY] ?? null;
  }

  function isStaleLayerState() {
    return history.state?.[STALE_STATE_KEY] === true;
  }

  function requestBack(afterNavigation, expectedLayerId = null) {
    pendingTraversals.push({
      afterNavigation: typeof afterNavigation === "function" ? afterNavigation : null,
      expectedLayerId
    });
    history.back();
  }

  function handlePopState() {
    if (disposed) return;
    const traversal = pendingTraversals.shift();
    const continuation = traversal?.afterNavigation;
    if (continuation) waitingContinuations.push(continuation);
    const currentId = currentLayerId();
    if (isStaleLayerState()) {
      if (pendingTraversals.length === 0) requestBack(undefined, traversal?.expectedLayerId);
      return;
    }
    if (traversal?.expectedLayerId && currentId === null) {
      if (pendingTraversals.length === 0) requestBack(undefined, traversal.expectedLayerId);
      return;
    }
    if (currentId && !layers.some((layer) => layer.id === currentId)) {
      if (pendingTraversals.length === 0) requestBack(undefined, traversal?.expectedLayerId);
      return;
    }
    while (layers.length > 0 && layers.at(-1).id !== currentId) {
      const layer = layers.pop();
      layer.onDismiss();
    }
    for (const afterNavigation of waitingContinuations.splice(0)) afterNavigation();
  }

  eventTarget.addEventListener("popstate", handlePopState);

  return {
    back() {
      if (layers.length === 0) return false;
      requestBack(undefined, layers.at(-2)?.id ?? null);
      return true;
    },
    register(id, onDismiss) {
      if (layers.some((layer) => layer.id === id)) return;
      layers.push({ id, onDismiss });
      const state = { ...(history.state ?? {}), [STATE_KEY]: id };
      delete state[STALE_STATE_KEY];
      history.pushState(state, "");
    },
    dismiss(id, afterNavigation, { preferTop = false } = {}) {
      const continuation = typeof afterNavigation === "function" ? afterNavigation : null;
      const targetId = preferTop ? layers.at(-1)?.id : id;
      const index = layers.findIndex((layer) => layer.id === targetId);
      if (index === -1) return false;
      const [layer] = layers.splice(index, 1);
      layer.onDismiss();
      if (currentLayerId() === targetId) {
        requestBack(targetId === id ? continuation : undefined, layers.at(-1)?.id ?? null);
      } else if (targetId === id) {
        continuation?.();
      }
      return true;
    },
    unregister(id) {
      const index = layers.findIndex((layer) => layer.id === id);
      if (index === -1) return;
      layers.splice(index, 1);
      if (currentLayerId() === id) {
        const state = { ...(history.state ?? {}) };
        delete state[STATE_KEY];
        state[STALE_STATE_KEY] = true;
        history.replaceState(state, "");
      }
    },
    dispose() {
      disposed = true;
      eventTarget.removeEventListener("popstate", handlePopState);
      layers.length = 0;
      pendingTraversals.length = 0;
      waitingContinuations.length = 0;
    }
  };
}
