export function createConnectionRecoveryCoordinator({
  verify,
  recover,
  onState,
  schedule = (callback, delay) => setTimeout(callback, delay),
  cancel = (handle) => clearTimeout(handle),
  offlineDelayMs = 2000
}) {
  let needsRecovery = false;
  let recoveryPromise = null;
  let offlineTimer = null;
  let networkConnected = true;
  let confirmedOffline = false;
  let disposed = false;

  function cancelOfflineCheck() {
    if (offlineTimer === null) return;
    cancel(offlineTimer);
    offlineTimer = null;
  }

  function scheduleOfflineCheck() {
    if (offlineTimer !== null || confirmedOffline) return;
    offlineTimer = schedule(async () => {
      offlineTimer = null;
      if (disposed || networkConnected) return;
      try {
        await verify();
      } catch {
        if (disposed || networkConnected) return;
        confirmedOffline = true;
        needsRecovery = true;
        onState("offline");
      }
    }, offlineDelayMs);
  }

  async function attemptRecovery() {
    if (!needsRecovery || !networkConnected) return;
    if (recoveryPromise) return recoveryPromise;
    const wasOffline = confirmedOffline;
    if (wasOffline) onState("recovering");
    recoveryPromise = Promise.resolve()
      .then(verify)
      .then(() => {
        if (disposed) return;
        needsRecovery = false;
        confirmedOffline = false;
        onState("online");
        recover();
      })
      .catch(() => {
        if (disposed || !wasOffline) return;
        confirmedOffline = false;
        onState("online");
      })
      .finally(() => { recoveryPromise = null; });
    return recoveryPromise;
  }

  return {
    networkChanged(connected) {
      if (!connected) {
        networkConnected = false;
        scheduleOfflineCheck();
        return undefined;
      }
      networkConnected = true;
      cancelOfflineCheck();
      return attemptRecovery();
    },
    appStateChanged(active) {
      if (!active) {
        needsRecovery = true;
        return undefined;
      }
      return attemptRecovery();
    },
    dispose() {
      disposed = true;
      cancelOfflineCheck();
    }
  };
}
