// minio.js
// Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0

'use strict';

const Minio = require('minio');

const { SiteService } = require('../../lib/site-lib');

class MinioService extends SiteService {

  constructor (dtp) {
    super(dtp, module.exports);
  }

  async start ( ) {
    await super.start();

    const minioConfig = {
      endPoint: process.env.MINIO_ENDPOINT,
      port: parseInt(process.env.MINIO_PORT, 10),
      useSSL: (process.env.MINIO_USE_SSL === 'enabled'),
      accessKey: process.env.MINIO_ACCESS_KEY,
      secretKey: process.env.MINIO_SECRET_KEY,
    };
    this.log.debug('MinIO config', { minioConfig });
    this.minio = new Minio.Client(minioConfig);
  }

  async stop ( ) {
    this.log.info(`stopping ${module.exports.name} service`);
    await super.stop();
  }

  async makeBucket (name, region) {
    try {
      const result = await this.minio.makeBucket(name, region);
      return result;
    } catch (error) {
      this.log.error('failed to create bucket on MinIO', { name, region, error });
      throw error;
    }
  }

  async uploadFile (fileInfo) {
    try {
      const result = await this.minio.fPutObject(
        fileInfo.bucket,
        fileInfo.key,
        fileInfo.filePath,
        fileInfo.metadata
      );
      return result;
    } catch (error) {
      this.log.error('failed to upload file to MinIO', { fileInfo, error });
      throw error;
    }
  }

  async downloadFile (fileInfo) {
    await this.minio.fGetObject(fileInfo.bucket, fileInfo.key, fileInfo.filePath);
  }

  async openDownloadStream (fi) {
    if (fi.range) {
      const length = fi.range.end - fi.range.start + 1;
      const stream = await this.minio.getPartialObject(fi.bucket, fi.key, fi.range.start, length);
      return stream;
    }
    const stream = await this.minio.getObject(fi.bucket, fi.key);
    return stream;
  }

  async removeObject (bucket, key) {
    try {
      await this.minio.removeObject(bucket, key);
    } catch (error) {
      this.log.error('failed to remove object', { bucket, key, error });
      throw error;
    }
  }

  async statObject (bucket, key) {
    try {
      const stat = await this.minio.statObject(bucket, key);
      return stat;
    } catch (error) {
      this.log.error('failed to stat MinIO object', { bucket, key, error });
      throw error;
    }
  }

  async statFile (file) {
    file.stat = await this.statObject(file.bucket, file.key);
    return file.stat;
  }
}

module.exports = {
  slug: 'minio',
  name: 'minio',
  create: (dtp) => { return new MinioService(dtp); },
};
