import { assert } from "../../../shared/src/asserts.js";
import { baseSnapshotFromCommit, assertSnapshotCommitDD31, commitFromHead, localMutationsGreaterThan, compareCookiesForSnapshots, localMutationsDD31, commitFromHash, DEFAULT_HEAD_NAME } from "../db/commit.js";
import { rebaseMutationAndPutCommit } from "../db/rebase.js";
import { withRead, withWrite } from "../with-transactions.js";
import { setClientGroup, getClientGroup } from "./client-groups.js";
import { assertHasClientState, getClientGroupIDForClient, mustGetClient, assertClientV6, setClient } from "./clients.js";
import { GatherMemoryOnlyVisitor } from "./gather-mem-only-visitor.js";
async function persistDD31(lc, clientID, memdag, perdag, mutators, closed, formatVersion, getZeroData, onGatherMemOnlyChunksForTest = () => Promise.resolve()) {
  if (closed()) {
    return;
  }
  const [perdagLMID, perdagBaseSnapshot, mainClientGroupID] = await withRead(
    perdag,
    async (perdagRead) => {
      await assertHasClientState(clientID, perdagRead);
      const mainClientGroupID2 = await getClientGroupIDForClient(
        clientID,
        perdagRead
      );
      assert(
        mainClientGroupID2,
        `No main client group for clientID: ${clientID}`
      );
      const [, perdagMainClientGroupHeadCommit] = await getClientGroupInfo(
        perdagRead,
        mainClientGroupID2
      );
      const perdagLMID2 = await perdagMainClientGroupHeadCommit.getMutationID(
        clientID,
        perdagRead
      );
      const perdagBaseSnapshot2 = await baseSnapshotFromCommit(
        perdagMainClientGroupHeadCommit,
        perdagRead
      );
      assertSnapshotCommitDD31(perdagBaseSnapshot2);
      return [perdagLMID2, perdagBaseSnapshot2, mainClientGroupID2];
    }
  );
  if (closed()) {
    return;
  }
  const [newMemdagMutations, memdagBaseSnapshot, gatheredChunks] = await withRead(memdag, async (memdagRead) => {
    const memdagHeadCommit = await commitFromHead(
      DEFAULT_HEAD_NAME,
      memdagRead
    );
    const newMutations = await localMutationsGreaterThan(
      memdagHeadCommit,
      { [clientID]: perdagLMID || 0 },
      memdagRead
    );
    const memdagBaseSnapshot2 = await baseSnapshotFromCommit(
      memdagHeadCommit,
      memdagRead
    );
    assertSnapshotCommitDD31(memdagBaseSnapshot2);
    let gatheredChunks2;
    if (compareCookiesForSnapshots(memdagBaseSnapshot2, perdagBaseSnapshot) > 0) {
      await onGatherMemOnlyChunksForTest();
      const memdagBaseSnapshotHash = memdagBaseSnapshot2.chunk.hash;
      const visitor = new GatherMemoryOnlyVisitor(memdagRead);
      await visitor.visit(memdagBaseSnapshotHash);
      gatheredChunks2 = visitor.gatheredChunks;
    }
    return [newMutations, memdagBaseSnapshot2, gatheredChunks2];
  });
  if (closed()) {
    return;
  }
  let memdagBaseSnapshotPersisted = false;
  const zeroDataForMemdagBaseSnapshot = getZeroData && await getZeroData(memdagBaseSnapshot.chunk.hash);
  await withWrite(perdag, async (perdagWrite) => {
    const [mainClientGroup, latestPerdagMainClientGroupHeadCommit] = await getClientGroupInfo(perdagWrite, mainClientGroupID);
    let newMainClientGroupHeadHash = latestPerdagMainClientGroupHeadCommit.chunk.hash;
    let mutationIDs = {
      ...mainClientGroup.mutationIDs
    };
    let { lastServerAckdMutationIDs } = mainClientGroup;
    if (gatheredChunks) {
      const client = await mustGetClient(clientID, perdagWrite);
      assertClientV6(client);
      const latestPerdagBaseSnapshot = await baseSnapshotFromCommit(
        latestPerdagMainClientGroupHeadCommit,
        perdagWrite
      );
      assertSnapshotCommitDD31(latestPerdagBaseSnapshot);
      if (compareCookiesForSnapshots(
        memdagBaseSnapshot,
        latestPerdagBaseSnapshot
      ) > 0) {
        memdagBaseSnapshotPersisted = true;
        await Promise.all(
          Array.from(gatheredChunks.values(), (c) => perdagWrite.putChunk(c))
        );
        await setClient(
          clientID,
          {
            ...client,
            persistHash: memdagBaseSnapshot.chunk.hash
          },
          perdagWrite
        );
        newMainClientGroupHeadHash = memdagBaseSnapshot.chunk.hash;
        const mainClientGroupLocalMutations = await localMutationsDD31(
          mainClientGroup.headHash,
          perdagWrite
        );
        lastServerAckdMutationIDs = memdagBaseSnapshot.meta.lastMutationIDs;
        mutationIDs = { ...lastServerAckdMutationIDs };
        newMainClientGroupHeadHash = await rebase(
          mainClientGroupLocalMutations,
          newMainClientGroupHeadHash,
          perdagWrite,
          mutators,
          mutationIDs,
          lc,
          formatVersion,
          zeroDataForMemdagBaseSnapshot
        );
      }
    }
    let zeroDataForPerdagHeadCommit;
    if (!memdagBaseSnapshotPersisted) {
      zeroDataForPerdagHeadCommit = getZeroData && await getZeroData(newMainClientGroupHeadHash, {
        openLazySourceRead: perdagWrite
      });
    }
    newMainClientGroupHeadHash = await rebase(
      newMemdagMutations,
      newMainClientGroupHeadHash,
      perdagWrite,
      mutators,
      mutationIDs,
      lc,
      formatVersion,
      zeroDataForPerdagHeadCommit ?? zeroDataForMemdagBaseSnapshot
    );
    const newMainClientGroup = {
      ...mainClientGroup,
      headHash: newMainClientGroupHeadHash,
      mutationIDs,
      lastServerAckdMutationIDs
    };
    await setClientGroup(mainClientGroupID, newMainClientGroup, perdagWrite);
  });
  if (gatheredChunks && memdagBaseSnapshotPersisted) {
    await withWrite(
      memdag,
      (memdagWrite) => memdagWrite.chunksPersisted([...gatheredChunks.keys()])
    );
  }
}
async function getClientGroupInfo(perdagRead, clientGroupID) {
  const clientGroup = await getClientGroup(clientGroupID, perdagRead);
  assert(clientGroup, `No client group for clientGroupID: ${clientGroupID}`);
  return [clientGroup, await commitFromHash(clientGroup.headHash, perdagRead)];
}
async function rebase(mutations, basis, write, mutators, mutationIDs, lc, formatVersion, zeroData) {
  for (let i = mutations.length - 1; i >= 0; i--) {
    const mutationCommit = mutations[i];
    const { meta } = mutationCommit;
    const newMainHead = await commitFromHash(basis, write);
    if (await mutationCommit.getMutationID(meta.clientID, write) > await newMainHead.getMutationID(meta.clientID, write)) {
      mutationIDs[meta.clientID] = meta.mutationID;
      basis = (await rebaseMutationAndPutCommit(
        mutationCommit,
        write,
        basis,
        mutators,
        lc,
        meta.clientID,
        formatVersion,
        zeroData
      )).chunk.hash;
    }
  }
  return basis;
}
export {
  persistDD31
};
//# sourceMappingURL=persist.js.map
