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 { concat as uint8ArrayConcat } from 'uint8arrays/concat';
import { getMimeFromUint8Array, toAsyncIterableWithMime } from './stream';
import ipfsCacheDb from './ipfsCacheDb';
import cyberCluster from './cluster';
import { contentToUint8Array, createTextPreview } from './content';
import { CYBER_GATEWAY_URL, FILE_SIZE_DOWNLOAD, } from '../config';
import { serializeError } from 'serialize-error';
const loadIPFSContentFromDb = (cid) => __awaiter(void 0, void 0, void 0, function* () {
    const data = yield ipfsCacheDb.get(cid);
    if (data && data.length) {
        const mime = yield getMimeFromUint8Array(data);
        const textPreview = createTextPreview(data, mime);
        const meta = {
            type: 'file',
            size: data.length,
            sizeLocal: data.length,
            mime,
        };
        return { result: data, cid, meta, source: 'db', textPreview };
    }
    return undefined;
});
const emptyMeta = {
    type: 'file',
    size: undefined,
    local: undefined,
    sizeLocal: undefined,
};
const fetchIPFSContentMeta = (cid, node, signal) => __awaiter(void 0, void 0, void 0, function* () {
    if (node) {
        const meta = yield node.stat(cid, { signal });
        return meta;
    }
    return emptyMeta;
});
const fetchIPFSContentFromNode = (cid, node, controller) => __awaiter(void 0, void 0, void 0, function* () {
    const controllerLegacy = controller || new AbortController();
    const { signal } = controllerLegacy;
    let timer;
    if (!node) {
        console.log('--------fetchIPFSContentFromNode NO NODE INTIALIZED--------');
        return undefined;
    }
    if (!controller) {
        timer = setTimeout(() => {
            controllerLegacy.abort();
        }, 1000 * 60 * 1);
    }
    try {
        const startTime = Date.now();
        const meta = yield fetchIPFSContentMeta(cid, node, signal);
        const statsDoneTime = Date.now();
        meta.statsTime = statsDoneTime - startTime;
        const allowedSize = meta.size ? meta.size < FILE_SIZE_DOWNLOAD : false;
        timer && clearTimeout(timer);
        switch (meta.type) {
            case 'directory': {
                return { cid, availableDownload: true, source: 'node', meta };
            }
            default: {
                const { value: firstChunk, done } = yield node
                    .cat(cid, { signal, length: 2048, offset: 0 })[Symbol.asyncIterator]()
                    .next();
                const mime = yield getMimeFromUint8Array(firstChunk);
                const fullyDownloaded = meta.size && meta.size > -1 && firstChunk.length >= meta.size;
                const textPreview = createTextPreview(firstChunk, mime);
                if (fullyDownloaded) {
                    yield ipfsCacheDb.add(cid, uint8ArrayConcat(firstChunk));
                }
                const stream = fullyDownloaded
                    ? firstChunk
                    : allowedSize
                        ? node.cat(cid, { signal })
                        : undefined;
                meta.catTime = Date.now() - statsDoneTime;
                if (!meta.local && allowedSize) {
                    node.pin(cid);
                    meta.pinTime = Date.now() - meta.catTime;
                }
                else {
                    meta.pinTime = -1;
                }
                return {
                    result: stream,
                    textPreview,
                    cid,
                    meta: Object.assign(Object.assign({}, meta), { mime }),
                    source: 'node',
                };
            }
        }
    }
    catch (error) {
        const serializedError = serializeError(error);
        console.log('error fetchIPFSContentFromNode', JSON.stringify(serializedError, null, 2));
        return { cid, availableDownload: true, source: 'node', meta: emptyMeta };
    }
});
const fetchIPFSContentFromGateway = (cid, node, controller) => __awaiter(void 0, void 0, void 0, function* () {
    const isExternalNode = (node === null || node === void 0 ? void 0 : node.nodeType) === 'external';
    const meta = isExternalNode
        ? yield fetchIPFSContentMeta(cid, node, controller === null || controller === void 0 ? void 0 : controller.signal)
        : emptyMeta;
    const contentUrl = `${CYBER_GATEWAY_URL}/ipfs/${cid}`;
    const response = yield fetch(contentUrl, {
        method: 'GET',
        signal: controller === null || controller === void 0 ? void 0 : controller.signal,
    });
    if (response && response.body) {
        const flushResults = (chunks) => !isExternalNode
            ? ipfsCacheDb.add(cid, uint8ArrayConcat(chunks))
            : Promise.resolve();
        const { mime, result, firstChunk } = yield toAsyncIterableWithMime(response.body, flushResults);
        const textPreview = createTextPreview(firstChunk, mime);
        return {
            cid,
            textPreview,
            meta: Object.assign(Object.assign({}, meta), { mime }),
            result,
            source: 'gateway',
            contentUrl,
        };
    }
    return undefined;
});
function fetchIpfsContent(cid, source, options) {
    return __awaiter(this, void 0, void 0, function* () {
        const { node, controller } = options;
        try {
            switch (source) {
                case 'db':
                    return loadIPFSContentFromDb(cid);
                case 'node':
                    return fetchIPFSContentFromNode(cid, node, controller);
                case 'gateway':
                    return fetchIPFSContentFromGateway(cid, node, controller);
                default:
                    return undefined;
            }
        }
        catch (e) {
            const serializedError = serializeError(e);
            console.log('----fetchIpfsContent error', JSON.stringify(serializedError, null, 2));
            return undefined;
        }
    });
}
const getIPFSContent = (cid, node, controller, callBackFuncStatus) => __awaiter(void 0, void 0, void 0, function* () {
    const dataRsponseDb = yield loadIPFSContentFromDb(cid);
    if (dataRsponseDb !== undefined) {
        return dataRsponseDb;
    }
    if (node) {
        callBackFuncStatus && callBackFuncStatus('trying to get with a node');
        const ipfsContent = yield fetchIPFSContentFromNode(cid, node, controller);
        return ipfsContent;
    }
    callBackFuncStatus && callBackFuncStatus('trying to get with a gatway');
    const respnseGateway = yield fetchIPFSContentFromGateway(cid, node, controller);
    return respnseGateway;
});
const catIPFSContentFromNode = (cid, node, offset, controller) => {
    if (!node) {
        console.log('--------fetchIPFSContentFromNode NO NODE INTIALIZED TODO: cover case--------');
        return undefined;
    }
    return node.cat(cid, { offset, signal: controller === null || controller === void 0 ? void 0 : controller.signal });
};
const addContenToIpfs = (node, content) => __awaiter(void 0, void 0, void 0, function* () {
    let cid;
    if (node) {
        cid = yield node.add(content);
    }
    cyberCluster.add(content);
    cid && (yield ipfsCacheDb.add(cid, yield contentToUint8Array(content)));
    return cid;
});
export { getIPFSContent, catIPFSContentFromNode, fetchIpfsContent, addContenToIpfs, };
//# sourceMappingURL=utils-ipfs.js.map