const { app } = require('electron');
const path = require('path');
const fs = require('fs');
const request = require('request');
const updater = require('electron-simple-updater');
const log = require('electron-log');

const reboot = require('../../modules/reboot');
const clearAppDir = require('../../modules/clear-app-dir');

const sleep = delay => new Promise(res => setTimeout(res, delay));

let _downloading = false;

module.exports.isDownloading = () => _downloading;
module.exports.downloadUpdate = downloadUpdate;
module.exports.quitAndInstall = quitAndInstall;

function downloadUpdate(meta) {
  const downloadUrl = meta.install;
  const appImagePath = process.env.APPIMAGE;

  _downloading = true;
  let downloadedImage;

  return getTemporaryFilePath(appImagePath, downloadUrl)
    .then((tempFile) => {
      downloadedImage = tempFile;
      try { return downloadFile(downloadUrl, tempFile); } catch (e) {
        console.error(e);
      }
    })
    .then(() => {
      return checkFileSize(downloadUrl, downloadedImage)
    })
    .then(() => {
      _downloading = false;
      return updater.emit('update-downloaded', meta);
    })
    .catch((error) => {
      _downloading = false;
      return updater.emit('error', error);
    });
}

function quitAndInstall(meta) {
  const saveDir = path.dirname(process.env.APPIMAGE);
  const filename = meta.install.substring(meta.install.lastIndexOf('/') + 1).replace('AppImage', 'temp')
  const filepath = path.resolve(saveDir, filename);

  console.log('quitAndInstall');

  return sleep(10 * 1000)
    .then(() => renameNewFile(filepath))
    .then(() => deleteOldFile())
    .then(() => clearAppDir())
    .then(() => relaunchFile());
}

function getTemporaryFilePath(appImagePath, downloadUrl) {
  const saveDir = path.dirname(appImagePath);
  return new Promise((resolve, reject) => {
    fs.access(saveDir, fs.W_OK, (err) => {
      if (err) {
        return reject(new Error(`Cannot write to the directory ${saveDir}`));
      }

      const filename = downloadUrl.substring(downloadUrl.lastIndexOf('/') + 1).replace('AppImage', 'temp')
      const filepath = path.resolve(saveDir, filename);

      resolve(filepath);
    });
  });
}

function downloadFile(url, tempPath) {
  return new Promise((resolve, reject) => {
    const file = fs.createWriteStream(tempPath);
    const req = request.get({
      url,
      timeout: 1000 * 60 * 3 // 3 minutes
    }).on('response', function (response) {
      if (response.statusCode !== 200) {
        log.warn('Download file: status wrong', response.statusCode);
        return reject(response.statusCode, 'Download file: status wrong');
      }

      log.debug('Download file: status ok');
    }).on('end', function () {
      log.debug('Download file: success');
      return resolve();
    }).on('error', function (err) {
      log.debug('Download file: error', err);
      return reject(err, 'Download file: error');
    }).pipe(file);

    //req.on('finish', () => file.close());
  });
}

function checkFileSize(serverFile, localFile) {
  return new Promise((resolve, reject) => {
    request({
      method: 'HEAD',
      url: serverFile,
      timeout: 30 * 1000 // 30 seconds
    }, (err, res) => {
      if (err) {
        log.warn('Check file size: err', err);
        return reject(err, 'Check file size: err');
      }

      const code = res.statusCode
      if (code >= 400) {
        log.warn('Check file size: wrong status code', code);
        return reject(new Error('Received invalid status code: ' + code))
      }

      let len = res.headers['content-length']
      if (!len) {
        log.warn('Check file size: unable to determine remote file size');
        return reject(new Error('Unable to determine file size'))
      }
      len = +len
      if (len !== len) {
        log.warn('Check file size: invalid content-length');
        return reject(new Error('Invalid Content-Length received'))
      }

      try {
        const stat = fs.statSync(localFile);
        const localSize = stat.size;

        if (localSize !== len) {
          log.warn('Check file size: size misnatch', len, 'vs', localSize);
          throw new Error('File size mismatch');
        }

        log.warn('Check file size: success');
        return resolve();
      } catch (err) {
        log.warn('Check file size: error', err);
        return reject(err);
      }
    })
  });
}

function renameNewFile(file) {
  const newFile = file.replace('temp', 'AppImage');

  return new Promise((resolve, reject) => {
    fs.rename(file, newFile, (err) => {
      if (err) {
        return reject(`Could not rename new file ${process.env.APPIMAGE}: ${err.message}`);
      }

      resolve();
    });
  });
}

function deleteOldFile() {
  return new Promise((resolve, reject) => {
    fs.unlink(process.env.APPIMAGE, (err) => {
      if (err) {
        return reject(`Could not delete old file ${process.env.APPIMAGE}: ${err.message}`);
      }


      resolve();
    });
  });
}

function relaunchFile(filePath) {
  if (process.platform !== 'linux') {
    return app.exit(0);
  }

  reboot.reboot();
}
