const Minio = require('minio');

const client = new Minio.Client({
    endPoint: process.env.MINIO_ENDPOINT || 'localhost',
    port: parseInt(process.env.MINIO_PORT) || 9000,
    useSSL: process.env.MINIO_USE_SSL === 'true',
    accessKey: process.env.MINIO_ACCESS_KEY,
    secretKey: process.env.MINIO_SECRET_KEY
})


// Buckets

/**
 * 
 * @param {String} bucket - Il nome del bucket 
 * @returns {String} - restituisce il nome del bucket creato
 */
async function bucketExists(bucket) {
    const exists = await client.bucketExists(bucket);
    if(!exists) {
        await client.makeBucket(bucket);
    }
    return bucket
}

/**
 * Restituisce un array con tutti i bucket sul server
 * @returns {Array} - i diversi bucket presenti sul server
 */
async function getBuckets() {
    const buckets = await client.listBuckets();
    return buckets;
}

/**
 * Restituisce i metadata del bucket passato come parametro
 * @param {String} bucket - il nome del bucket
 * @returns {Object} - i metadata del bucket
 */
async function getBucket(bucket) {
    const buckets = await client.listBuckets();
    return buckets.filter(b => b.name === bucket);
}


/**
 * Restituisce un array con tutti gli oggetti presenti nel bucket passato come parametro
 * @param {String} bucket - il nome del bucket
 * @returns {Promise<Array>} - i file del bucket
 */
async function getObjects(bucket) {
    return new Promise((resolve, reject) => {
        const objects = [];
        const stream = client.listObjects(bucket, '', true);
        stream.on('data', obj => objects.push(obj));
        stream.on('error', err => reject(err));
        stream.on('end', () => resolve(objects));
    });
}

/**
 * Restituisce i metadata del file con id passato come parametro presente nel bucket
 * @param {String} bucket - il nome del bucket
 * @param {String} objectName - il nome dell'oggetto
 * @returns {Object} - i metadata del file
 */
async function getObject(bucket, objectName) {
    const item = await client.statObject(bucket, objectName);
    return item;
}

/**
 * Elimina il file con l'id passato a parametro dal bucket
 * @param {String} bucket - il nome del bucket
 * @param {String} objectName - il nome dell'oggetto
 */
async function removeObject(bucket, objectName) {
    await client.removeObject(bucket, objectName);
}


//Upload - Download
/**
 * Carica un file nel bucket come buffer
 * @param {String} bucket - il nome del bucket
 * @param {String} objectName - il nome che avrà il file in Minio
 * @param {Buffer} fileBuffer - il file caricato
 * @param {Number} size - dimensione del file
 * @param {String} contentType - mimetype (es. 'image/png')
 */
async function upload(bucket, objectName, fileBuffer, size, contentType) {
    await bucketExists(bucket);

    const metaData = {
        'Content-Type': contentType || 'application/octet-stream',
    }

    const result = await client.putObject(bucket, objectName, fileBuffer, size, metaData);
    return result;
}

/**
 * Genera un URL temporaneo di download
 * @param {String} bucket - il nome del bucket
 * @param {String} objectName - il nome del file
 * @param {Number} expiry - quanto dura il link in secondi (default: 24h = 86400)
 * 
 * @returns {String} url - url di download del file
 */
async function download(bucket, objectName, expiry = 86400) {
    const url = await client.presignedGetObject(bucket, objectName, expiry);
    return url;
}

/**
 * Recupera e ritorna lo stream dei dati dal server Minio (per leggere il contenuto via API)
 * @param {String} bucket - il nome del bucket
 * @param {String} objectName - il nome dell'oggetto
 */
async function getFileStream(bucket, objectName) {
    const dataStream = await client.getObject(bucket, objectName);
    return dataStream;
}

async function checkMinio() {
    try {
        await client.listBuckets();
        return true;
    } catch (error) {
        return false;
    }
}

module.exports = {
    bucketExists,
    getBuckets,
    getBucket,
    getObjects,
    getObject,
    removeObject,
    upload,
    download,
    getFileStream,
    checkMinio
}