"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IpfsStorageFactory = void 0;
const stream_1 = require("stream");
const ipfs_1 = require("./ipfs");
const database_1 = require("../controllers/database");
const security_1 = require("../controllers/security");
const mime = __importStar(require("mime"));
const test_1 = require("../test");
function removeDot(name) {
    if (name === null || name === undefined) {
        return null;
    }
    return name.toString().replace('.', '');
}
class IpfsStorageFactory {
    constructor() {
        this.domain = '_Storage';
        this.canHandleFileStream = true;
        this.isS3 = false;
    }
    async createFile(name, size, data, contentType, pN, options) {
        name = this.validateFilename(name);
        return this._saveFile(name, size, data, contentType, pN, options);
    }
    async deleteFile(id, options) {
        const wOptions = { bypassDomainVerification: true };
        return await (0, database_1.removeDataInStore)(this.domain, { id: id }, {}, (0, test_1.databaseFactory)(), wOptions, options);
    }
    async fileInfo(id, options) {
        const wOptions = { bypassDomainVerification: true };
        const rule = { id: id, return: [] };
        const file = await (0, database_1.findDataByIdInStore)(this.domain, rule, (0, test_1.databaseFactory)(), wOptions, options);
        if (file && file.size && file.name)
            return file;
        else
            throw { message: 'File info can not be determined' };
    }
    async getFileLocation(id, configAdapter) {
        return '/storage/' + configAdapter.applicationId + '/file/' + encodeURIComponent(id);
    }
    async handleFileStream(id, req, res, contentType, options) {
        const wOptions = { bypassDomainVerification: true };
        const rule = { id: id, return: [] };
        const file = await (0, database_1.findDataByIdInStore)(this.domain, rule, (0, test_1.databaseFactory)(), wOptions, options);
        if (file && file.cid && file.type && file.size) {
            const size = file.size;
            const range = req.headers.range;
            const parts = range.replace(/bytes=/, "").split('-');
            let start = parts[0];
            let end = parts[1];
            start = parseInt(start, 10);
            end = end ? parseInt(end, 10) : size - 1;
            if (!isNaN(start) && isNaN(end)) {
                end = size - 1;
            }
            if (isNaN(start) && !isNaN(end)) {
                start = size - end;
                end = size - 1;
            }
            if (start >= size || end >= size) {
                res.writeHead(416, {
                    "Content-Range": `bytes */${size}`
                });
                return res.end();
            }
            res.writeHead(206, {
                "Content-Range": `bytes ${start}-${end}/${size}`,
                "Accept-Ranges": "bytes",
                "Content-Length": `${end - start + 1}`,
                "Content-Type": typeof file.type === 'string' ? file.type : 'application/octet-stream'
            });
            const ipfs = await ipfs_1.IpfsFactory.getInstance(options);
            const buffer = await ipfs.generateDataFromCid(file.cid, { json: false, stream: true, start: start, end: end }, options);
            (0, stream_1.pipeline)(buffer, res, err => {
                if (err) {
                    try {
                        res.end();
                    }
                    catch (_) {
                    }
                }
            });
        }
        else
            throw 'file not found, maybe its deleted';
    }
    async listFiles(query = { prefix: '', size: 20, skip: 0 }, options) {
        const q = {
            filter: {},
            return: [],
            size: query.size,
            skip: query.skip
        };
        const context = { useMasterKey: true };
        const wOptions = { bypassDomainVerification: true };
        let r = await (0, database_1.findDataByFilterInStore)(this.domain, q, context, (0, test_1.databaseFactory)(), wOptions, options);
        if (Array.isArray(r))
            return r.filter(x => x?.name?.toString()?.includes(query.prefix));
        else
            return [];
    }
    validateFilename(name) {
        const regx = /[^a-zA-Z0-9]/;
        return name.replace(new RegExp(regx), '');
    }
    async signedUrl(id, options) {
        return this.getFileLocation(id, options);
    }
    async _saveFile(name, size, data, contentType, pN, options) {
        const _obj = {
            id: pN ? removeDot(name) : (0, security_1.generateUUID)(),
            name: pN ? removeDot(name + (0, security_1.generateUUID)()) : removeDot(name),
            extension: mime.getExtension(contentType),
            type: contentType,
            cid: null,
            data: null,
            size: null,
        };
        const ipfs = await ipfs_1.IpfsFactory.getInstance(options);
        const dataRes = await ipfs.generateCidFromData(_obj, data, this.domain, options);
        _obj.cid = dataRes.cid;
        _obj.size = size;
        _obj.return = [];
        const wOptions = { bypassDomainVerification: true };
        return await (0, database_1.writeOneDataInStore)(this.domain, _obj, {}, (0, test_1.databaseFactory)(), wOptions, options);
    }
    async init(options) {
    }
    async getFileBuffer(file, options) {
        if (file && file.cid) {
            const ipfs = await ipfs_1.IpfsFactory.getInstance(options);
            return await ipfs.generateDataFromCid(file.cid, { json: false, stream: false }, options);
        }
        else {
            throw 'file not found, maybe its deleted';
        }
    }
    async getFileStream(file, options) {
        if (file && file.cid) {
            const ipfs = await ipfs_1.IpfsFactory.getInstance(options);
            return await ipfs.generateDataFromCid(file.cid, { json: false, stream: true }, options);
        }
        else {
            throw 'file not found, maybe its deleted';
        }
    }
}
exports.IpfsStorageFactory = IpfsStorageFactory;
