const AWS = require('aws-sdk');
const s3 = new AWS.S3();

const conf = require('./config.js');
const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');
const crypto = require('crypto');

const notLocalHost = conf.notLocalHost;

const mozlog = require('./log.js');

const log = mozlog('portal.storage');

const redis = require('redis');
const redis_client = redis.createClient({
  host: conf.redis_host
});

redis_client.on('error', err => {
  log.info('Redis:', err);
});

if (notLocalHost) {
  module.exports = {
    filename: filename,
    exists: exists,
    length: awsLength,
    get: awsGet,
    set: awsSet,
    delete: awsDelete,
    forceDelete: awsForceDelete
  };
} else {
  module.exports = {
    filename: filename,
    exists: exists,
    length: localLength,
    get: localGet,
    set: localSet,
    delete: localDelete,
    forceDelete: localForceDelete
  };
}

function filename(id) {
  return new Promise((resolve, reject) => {
    redis_client.hget(id, 'filename', (err, reply) => {
      if (!err) {
        resolve(reply);
      } else {
        reject();
      }
    });
  });
}

function exists(id) {
  return new Promise((resolve, reject) => {
    redis_client.exists(id, (rediserr, reply) => {
      resolve(reply === 1);
    });
  });
}

function localLength(id) {
  return new Promise((resolve, reject) => {
    try {
      resolve(fs.statSync(path.join(__dirname, '../static', id)).size);
    } catch (err) {
      reject();
    }
  });
}

function localGet(id) {
  return fs.createReadStream(path.join(__dirname, '../static', id));
}

function localSet(id, file, filename, url) {
  return new Promise((resolve, reject) => {
    const fstream = fs.createWriteStream(path.join(__dirname, '../static', id));
    file.pipe(fstream);
    fstream.on('close', () => {
      const uuid = crypto.randomBytes(10).toString('hex');

      redis_client.hmset([id, 'filename', filename, 'delete', uuid]);
      redis_client.expire(id, 86400000);
      log.info('localSet:', 'Upload Finished of ' + id);
      resolve({
        uuid: uuid,
        url: url
      });
    });

    fstream.on('error', () => {
      log.error('localSet:', 'Failed upload of ' + id);
      reject();
    });
  });
}

function localDelete(id, delete_token) {
  return new Promise((resolve, reject) => {
    redis_client.hget(id, 'delete', (err, reply) => {
      if (!reply || delete_token !== reply) {
        reject();
      } else {
        redis_client.del(id);
        resolve(fs.unlinkSync(path.join(__dirname, '../static', id)));
      }
    });
  });
}

function localForceDelete(id) {
  return new Promise((resolve, reject) => {
    redis_client.del(id);
    resolve(fs.unlinkSync(path.join(__dirname, '../static', id)));
  });
}

function awsLength(id) {
  const params = {
    Bucket: conf.s3_bucket,
    Key: id
  };
  return new Promise((resolve, reject) => {
    s3.headObject(params, function(err, data) {
      if (!err) {
        resolve(data.ContentLength);
      } else {
        reject();
      }
    });
  });
}

function awsGet(id) {
  const params = {
    Bucket: conf.s3_bucket,
    Key: id
  };

  try {
    return s3.getObject(params).createReadStream();
  } catch(err) {
    log.info('GetFailed', 'Get Object from s3 failed.');
    return null;
  }
}

function awsSet(id, file, filename, url) {
  const params = {
    Bucket: conf.s3_bucket,
    Key: id,
    Body: file
  };

  return new Promise((resolve, reject) => {
    s3.upload(params, function(err, _data) {
      if (err) {
        log.info('awsUploadError:', err.stack); // an error occurred
        reject();
      } else {
        const uuid = crypto.randomBytes(10).toString('hex');

        redis_client.hmset([id, 'filename', filename, 'delete', uuid]);

        redis_client.expire(id, 86400000);
        log.info('awsUploadFinish', 'Upload Finished of ' + filename);
        if (conf.bitly_key) {
          fetch(
            'https://api-ssl.bitly.com/v3/shorten?access_token=' +
              conf.bitly_key +
              '&longUrl=' +
              encodeURIComponent(url) +
              '&format=txt'
          )
            .then(res => {
              return res.text();
            })
            .then(body => {
              resolve({
                uuid: uuid,
                url: body
              });
            });
        } else {
          resolve({
            uuid: uuid,
            url: url
          });
        }
      }
    });
  });
}

function awsDelete(id, delete_token) {
  return new Promise((resolve, reject) => {
    redis_client.hget(id, 'delete', (err, reply) => {
      if (!reply || delete_token !== reply) {
        reject();
      } else {
        redis_client.del(id);
        const params = {
          Bucket: conf.s3_bucket,
          Key: id
        };

        s3.deleteObject(params, function(err, _data) {
          err ? reject(err) : resolve(err);
        });
      }
    });
  });
}

function awsForceDelete(id) {
  return new Promise((resolve, reject) => {
    redis_client.del(id);
    const params = {
      Bucket: conf.s3_bucket,
      Key: id
    };

    s3.deleteObject(params, function(err, _data) {
      err ? reject(err) : resolve(err);
    });
  });
}
