import { createConnectedClient } from "./createConnectedClient";
import { subtractLsn } from "./parseLsn";

export interface SlotInfo {
  srcLsn: string;
  dstLsn: string;
  gap: number;
  gapTillLatch: number;
  lagSec: number | null;
  latched: {
    srcLsn: string;
    at: number;
  };
}

/**
 * Returns the difference between src LSN and dst LSN (gap), along with some
 * estimation on how much seconds they are apart from each other. To measure
 * that time, we "latch" an LSN on the src and then wait until dst crosses that
 * latched LSN. Once it does, we remember the actual astronomical time it took
 * to cross, and then latch the src LSN again (towards the next estimation
 * cycle). When the caller decides that the time is small enough, it stops
 * polling; and if the time is large (e.g. more than 20 seconds), it continues
 * to call getSlotInfo() over and over again until that time drops below the
 * needed 20 seconds threshold.
 */
export async function getSlotInfo(
  {
    dsn,
    slotName,
  }: {
    dsn: string;
    slotName: string;
  },
  prevSlotInfo: SlotInfo | undefined,
): Promise<SlotInfo> {
  const client = await createConnectedClient(dsn);
  try {
    const srcLsn = await client.currentWalInsertLsn();
    const dstLsn = await client.confirmedFlushLsn(slotName);
    const gap = subtractLsn(srcLsn, dstLsn)!;

    if (!prevSlotInfo) {
      prevSlotInfo = {
        srcLsn,
        dstLsn,
        gap,
        gapTillLatch: gap,
        lagSec: null, // means "unknown"
        latched: {
          srcLsn,
          at: performance.now(),
        },
      };
    }

    const gapTillLatch = subtractLsn(prevSlotInfo.latched.srcLsn, dstLsn)!;
    return gapTillLatch <= 0
      ? {
          srcLsn,
          dstLsn,
          gap,
          gapTillLatch,
          lagSec: Math.round(
            (performance.now() - prevSlotInfo.latched.at) / 1000,
          ),
          latched: {
            srcLsn,
            at: performance.now(),
          },
        }
      : {
          srcLsn,
          dstLsn,
          gap,
          gapTillLatch,
          lagSec: prevSlotInfo.lagSec,
          latched: prevSlotInfo.latched,
        };
  } finally {
    await client.end().catch(() => {});
  }
}
