import { assertSnapshotCommitDD31, baseSnapshotFromHead, commitFromHash, baseSnapshotFromHash, commitFromHead, DEFAULT_HEAD_NAME, baseSnapshotFromCommit, localMutationsGreaterThan, compareCookiesForSnapshots } from "../db/commit.js";
import { rebaseMutationAndPutCommit } from "../db/rebase.js";
import { diffCommits } from "../sync/diff.js";
import { withRead, withWrite } from "../with-transactions.js";
import { getClientGroupForClient, ClientStateNotFoundError, mustGetClient, assertClientV6, setClient } from "./clients.js";
import { GatherNotCachedVisitor } from "./gather-not-cached-visitor.js";
const GATHER_SIZE_LIMIT = 5 * 2 ** 20;
async function refresh(lc, memdag, perdag, clientID, mutators, diffConfig, closed, formatVersion, zero) {
  if (closed()) {
    return;
  }
  const memdagBaseSnapshot = await withRead(
    memdag,
    (memdagRead) => baseSnapshotFromHead(DEFAULT_HEAD_NAME, memdagRead)
  );
  assertSnapshotCommitDD31(memdagBaseSnapshot);
  const result = await memdag.withSuspendedSourceCacheEvictsAndDeletes(async () => {
    const perdagWriteResult = await withWrite(
      perdag,
      async (perdagWrite) => {
        const clientGroup = await getClientGroupForClient(
          clientID,
          perdagWrite
        );
        if (!clientGroup) {
          throw new ClientStateNotFoundError(clientID);
        }
        const perdagClientGroupHeadHash2 = clientGroup.headHash;
        const perdagClientGroupHeadCommit = await commitFromHash(
          perdagClientGroupHeadHash2,
          perdagWrite
        );
        const perdagLmid2 = await perdagClientGroupHeadCommit.getMutationID(
          clientID,
          perdagWrite
        );
        const client = await mustGetClient(clientID, perdagWrite);
        assertClientV6(client);
        const perdagClientGroupBaseSnapshot2 = await baseSnapshotFromHash(
          perdagClientGroupHeadHash2,
          perdagWrite
        );
        assertSnapshotCommitDD31(perdagClientGroupBaseSnapshot2);
        if (shouldAbortRefresh(
          memdagBaseSnapshot,
          perdagClientGroupBaseSnapshot2,
          perdagClientGroupHeadHash2
        )) {
          return void 0;
        }
        const visitor = new GatherNotCachedVisitor(
          perdagWrite,
          memdag,
          GATHER_SIZE_LIMIT
        );
        await visitor.visit(perdagClientGroupHeadHash2);
        const { gatheredChunks: gatheredChunks2 } = visitor;
        const refreshHashesSet = new Set(client.refreshHashes);
        refreshHashesSet.add(perdagClientGroupHeadHash2);
        const newClient = {
          ...client,
          refreshHashes: [...refreshHashesSet]
        };
        await setClient(clientID, newClient, perdagWrite);
        return [
          perdagClientGroupHeadHash2,
          perdagClientGroupBaseSnapshot2,
          perdagLmid2,
          gatheredChunks2,
          client.refreshHashes
        ];
      }
    );
    if (closed() || !perdagWriteResult) {
      return {
        type: "aborted"
      };
    }
    const [
      perdagClientGroupHeadHash,
      perdagClientGroupBaseSnapshot,
      perdagLmid,
      gatheredChunks,
      refreshHashesForRevert
    ] = perdagWriteResult;
    return withWrite(memdag, async (memdagWrite) => {
      const memdagHeadCommit = await commitFromHead(
        DEFAULT_HEAD_NAME,
        memdagWrite
      );
      const memdagBaseSnapshot2 = await baseSnapshotFromCommit(
        memdagHeadCommit,
        memdagWrite
      );
      assertSnapshotCommitDD31(memdagBaseSnapshot2);
      if (shouldAbortRefresh(
        memdagBaseSnapshot2,
        perdagClientGroupBaseSnapshot,
        perdagClientGroupHeadHash
      )) {
        return {
          type: "aborted",
          refreshHashesForRevert
        };
      }
      const newMemdagMutations = await localMutationsGreaterThan(
        memdagHeadCommit,
        { [clientID]: perdagLmid },
        memdagWrite
      );
      const ps = [];
      for (const { chunk, size } of gatheredChunks.values()) {
        ps.push(memdagWrite.putChunk(chunk, size));
      }
      await Promise.all(ps);
      let newMemdagHeadHash = perdagClientGroupHeadHash;
      if (newMemdagMutations.length > 0) {
        const zeroData = await zero?.getTxData?.(newMemdagHeadHash, {
          openLazyRead: memdagWrite
        });
        for (let i = newMemdagMutations.length - 1; i >= 0; i--) {
          newMemdagHeadHash = (await rebaseMutationAndPutCommit(
            newMemdagMutations[i],
            memdagWrite,
            newMemdagHeadHash,
            mutators,
            lc,
            newMemdagMutations[i].meta.clientID,
            formatVersion,
            zeroData
          )).chunk.hash;
        }
      }
      const newMemdagHeadCommit = await commitFromHash(
        newMemdagHeadHash,
        memdagWrite
      );
      const diffs = await diffCommits(
        memdagHeadCommit,
        newMemdagHeadCommit,
        memdagWrite,
        diffConfig,
        formatVersion
      );
      await memdagWrite.setHead(DEFAULT_HEAD_NAME, newMemdagHeadHash);
      return {
        type: "complete",
        diffs,
        oldHead: memdagHeadCommit.chunk.hash,
        newHead: newMemdagHeadHash,
        newPerdagClientHeadHash: perdagClientGroupHeadHash
      };
    });
  });
  if (closed()) {
    return;
  }
  const setRefreshHashes = (refreshHashes) => withWrite(perdag, async (perdagWrite) => {
    const client = await mustGetClient(clientID, perdagWrite);
    const newClient = {
      ...client,
      refreshHashes
    };
    await setClient(clientID, newClient, perdagWrite);
  });
  if (result.type === "aborted") {
    if (result.refreshHashesForRevert) {
      await setRefreshHashes(result.refreshHashesForRevert);
    }
    return void 0;
  }
  zero?.advance(result.oldHead, result.newHead, result.diffs.get("") ?? []);
  await setRefreshHashes([result.newPerdagClientHeadHash]);
  return {
    oldHead: result.oldHead,
    newHead: result.newHead,
    diffs: result.diffs
  };
}
function shouldAbortRefresh(memdagBaseSnapshot, perdagClientGroupBaseSnapshot, perdagClientGroupHeadHash) {
  const baseSnapshotCookieCompareResult = compareCookiesForSnapshots(
    memdagBaseSnapshot,
    perdagClientGroupBaseSnapshot
  );
  return baseSnapshotCookieCompareResult > 0 || baseSnapshotCookieCompareResult === 0 && perdagClientGroupHeadHash === perdagClientGroupBaseSnapshot.chunk.hash;
}
export {
  refresh
};
//# sourceMappingURL=refresh.js.map
