var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { SyncPullAbortedError } from './sync-messages.js';
export function partitionRemoteEntries(entries) {
    const prefetched = [];
    const needsFetchCids = [];
    for (const entry of entries) {
        if (!entry.message) {
            needsFetchCids.push(entry.messageCid);
        }
        else if (entry.message.descriptor.interface === 'Records' &&
            entry.message.descriptor.method === 'Write' &&
            entry.message.descriptor.dataCid &&
            !entry.encodedData) {
            needsFetchCids.push(entry.messageCid);
        }
        else {
            prefetched.push(entry);
        }
    }
    return { prefetched, needsFetchCids };
}
export class SyncLinkReconciler {
    constructor(deps) {
        this._deps = deps;
    }
    reconcile(target, options) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const { did, dwnUrl, delegateDid, protocol, permissionGrantIds } = target;
            const direction = options === null || options === void 0 ? void 0 : options.direction;
            const verifyConvergence = (_a = options === null || options === void 0 ? void 0 : options.verifyConvergence) !== null && _a !== void 0 ? _a : false;
            const shouldContinue = this._deps.shouldContinue;
            const localRoot = yield this._deps.getLocalRoot(did, delegateDid, protocol, permissionGrantIds);
            if (shouldContinue && !shouldContinue()) {
                return { aborted: true, changed: false, didPull: false, didPush: false };
            }
            const remoteRoot = yield this._deps.getRemoteRoot(did, dwnUrl, delegateDid, protocol, permissionGrantIds);
            if (shouldContinue && !shouldContinue()) {
                return { aborted: true, changed: false, didPull: false, didPush: false };
            }
            let didPull = false;
            let didPush = false;
            let pushResult;
            if (localRoot !== remoteRoot) {
                const diff = yield this._deps.diffWithRemote(target);
                if (shouldContinue && !shouldContinue()) {
                    return { aborted: true, changed: true, didPull: false, didPush: false, localRoot, remoteRoot };
                }
                if ((!direction || direction === 'pull') && diff.onlyRemote.length > 0) {
                    const { prefetched, needsFetchCids } = partitionRemoteEntries(diff.onlyRemote);
                    try {
                        yield this._deps.pullMessages({
                            did,
                            dwnUrl,
                            delegateDid,
                            protocol,
                            permissionGrantIds,
                            messageCids: needsFetchCids,
                            prefetched,
                            shouldContinue,
                        });
                    }
                    catch (error) {
                        if (error instanceof SyncPullAbortedError) {
                            return { aborted: true, changed: true, didPull: false, didPush: false, localRoot, remoteRoot };
                        }
                        throw error;
                    }
                    if (shouldContinue && !shouldContinue()) {
                        return { aborted: true, changed: true, didPull: true, didPush: false, localRoot, remoteRoot };
                    }
                    didPull = true;
                }
                if ((!direction || direction === 'push') && diff.onlyLocal.length > 0) {
                    pushResult = yield this._deps.pushMessages({
                        did, dwnUrl, delegateDid, protocol, permissionGrantIds, messageCids: diff.onlyLocal,
                    });
                    if (shouldContinue && !shouldContinue()) {
                        return { aborted: true, changed: true, didPull, didPush: true, localRoot, remoteRoot, pushResult };
                    }
                    didPush = true;
                }
            }
            if (!verifyConvergence) {
                return {
                    changed: localRoot !== remoteRoot,
                    didPull,
                    didPush,
                    localRoot,
                    remoteRoot,
                    pushResult,
                };
            }
            const postLocalRoot = yield this._deps.getLocalRoot(did, delegateDid, protocol, permissionGrantIds);
            if (shouldContinue && !shouldContinue()) {
                return { aborted: true, changed: localRoot !== remoteRoot, didPull, didPush, localRoot, remoteRoot, pushResult };
            }
            const postRemoteRoot = yield this._deps.getRemoteRoot(did, dwnUrl, delegateDid, protocol, permissionGrantIds);
            if (shouldContinue && !shouldContinue()) {
                return { aborted: true, changed: localRoot !== remoteRoot, didPull, didPush, localRoot, remoteRoot, pushResult };
            }
            return {
                changed: localRoot !== remoteRoot,
                didPull,
                didPush,
                localRoot,
                remoteRoot,
                postLocalRoot,
                postRemoteRoot,
                converged: postLocalRoot === postRemoteRoot,
                pushResult,
            };
        });
    }
}
//# sourceMappingURL=sync-link-reconciler.js.map