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 { defer, from, map, combineLatest } from 'rxjs';
import BroadcastChannelSender from '../../../../../backend/channels/BroadcastChannelSender';
import { broadcastStatus } from '../../../../../backend/channels/broadcastStatus';
import { mapPinToEntity } from '../../../../../CozoDb/mapping';
import { QueuePriority } from '../../../../../QueueManager/types';
import { createLoopObservable } from '../utils/rxjs';
import { IPFS_SYNC_INTERVAL } from '../consts';
import { fetchPins } from '../../../dataSource/ipfs/ipfsSource';
import { serializeError } from 'serialize-error';
class SyncIpfsLoop {
    get loop$() {
        return this._loop$;
    }
    constructor(deps, particlesResolver) {
        this.statusApi = broadcastStatus('pin', new BroadcastChannelSender());
        deps.dbInstance$.subscribe((db) => {
            this.db = db;
        });
        deps.ipfsInstance$.subscribe((ipfsInstance) => {
            this.ipfsNode = ipfsInstance;
        });
        this.particlesResolver = particlesResolver;
        this.isInitialized$ = combineLatest([
            deps.dbInstance$,
            deps.ipfsInstance$,
            particlesResolver.isInitialized$,
        ]).pipe(map(([dbInstance, ipfsInstance, syncQueueInitialized]) => !!ipfsInstance && !!dbInstance && !!syncQueueInitialized));
    }
    start() {
        this._loop$ = createLoopObservable(IPFS_SYNC_INTERVAL, this.isInitialized$, defer(() => from(this.syncPins())), () => this.statusApi.sendStatus('in-progress'));
        this._loop$.subscribe({
            next: (result) => this.statusApi.sendStatus('idle'),
            error: (err) => this.statusApi.sendStatus('error', err.toString()),
        });
        return this;
    }
    syncPins() {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const pinsResult = yield fetchPins(this.ipfsNode);
                const dbPins = (yield this.db.getPins()).rows.map((row) => row[0]);
                const pinsResultSet = new Set(pinsResult.map((pin) => pin.cid.toString()));
                const dbPinsSet = new Set(dbPins);
                const pinsToRemove = dbPins.filter((pin) => !pinsResultSet.has(pin));
                const pinsToAdd = pinsResult.filter((pin) => !dbPinsSet.has(pin.cid.toString()));
                if (pinsToRemove.length) {
                    yield this.db.deletePins(pinsToRemove);
                }
                const particlesExist = new Set((yield this.db.getParticles(['cid'])).rows.map((row) => row[0]));
                const cidsToAdd = pinsToAdd.map((pin) => pin.cid.toString());
                const particlesToAdd = cidsToAdd.filter((cid) => !particlesExist.has(cid));
                if (particlesToAdd.length > 0) {
                    yield this.particlesResolver.enqueue(particlesToAdd.map((cid) => ({
                        id: cid,
                        priority: QueuePriority.LOW,
                    })));
                }
                if (pinsToAdd.length > 0) {
                    yield this.db.putPins(pinsToAdd.map(mapPinToEntity));
                }
            }
            catch (e) {
                const serializedError = serializeError(e);
                console.log('---syncPins error', JSON.stringify(serializedError, null, 2));
                throw e;
            }
        });
    }
}
export default SyncIpfsLoop;
//# sourceMappingURL=SyncIpfsLoop.js.map