#!/usr/bin/env node

const LINE = "*********************************";
const LINE_CR = LINE + "\n";

console.log(LINE);
console.log("***** Formbird installation *****");
console.log(LINE);

const isWin = process.platform === "win32";
var normalizeCmd = function (cmd) {
    return isWin ? cmd + '.cmd' : cmd;
};

const { spawnSync } = require('child_process');
const prompt = require('inquirer').prompt;
const fs = require('fs-extra');
const path = require('path');
const request = require('request');
const editJsonFile = require("edit-json-file");
const pjson = require('./package');

var formbirdAppName;
var formbirdDbName;
var srcCustomCompUrl;
var srcBaseMongoDBUrl;
var installModule;

var targetFbApp;
var hasYarn;
var hasPm2;

function hasPackage(pkgName) {
    if (isWin) {
        var install = spawnSync(normalizeCmd('npm'), ['list', '-g', '--depth', '0', '| findstr', pkgName]);
    } else {
        var install = spawnSync(normalizeCmd('npm'), ['list', '-g', '--depth', '0', '| grep', pkgName]);
    }

    var log = '${install.stderr.toString()}';
    return log.indexOf(pkgName);
}

function printVersion() {
    var banner = "| Platform: " + process.platform;
    banner += "\n| Architecture: " + process.arch;
    banner += "\n|\n| Application Versions: ";
    banner += "\n|   " + pjson.name + ": v" + pjson.version;
    banner += "\n|   Node: " + process.version;
    banner += "\n" + LINE_CR;

    console.log(banner);
}

var splitValue = function (params, defaultName) {
    var values = {
        name: defaultName,
        value: null
    };

    if (params) {
        var tmp = params.split("=");
        if (tmp.length > 1) {
            values.name = tmp[0];
            values.value = tmp[1];
        } else {
            values.value = tmp;
        }
    }

    return values;
}

function installNodeModules() {
    var install;
    if (hasYarn) {
        console.log("Installing node modules with yarn...");
        install = spawnSync(normalizeCmd('yarn'), ['install']);
    } else {
        console.log("Installing node modules with npm...");
        install = spawnSync(normalizeCmd('npm'), ['install']);
    }
    console.log(`stderr: ${install.stderr.toString()}`);
    console.log(`stdout: ${install.stdout.toString()}`);
}

function installPackage(pkgName, isGlobal) {
    var install;
    if (hasYarn) {
        install = spawnSync(normalizeCmd('yarn'), [isGlobal ? 'global' : '', 'add', pkgName]);
    } else {
        install = spawnSync(normalizeCmd('npm'), ['install', isGlobal ? '-g' : '', pkgName]);
    }

    console.log(`stderr: ${install.stderr.toString()}`);
    console.log(`stdout: ${install.stdout.toString()}`);
}

function showCompleteMessage(needModuleInstalled) {

    var doneMsg = "*** Please change directory to the application root directory, " + formbirdAppName +
        " (ie. `cd " + formbirdAppName + "`) and type:";

    if (needModuleInstalled) {
        if (hasYarn) {
            doneMsg += " `yarn install` or `npm install` to install node modules first";
        } else {
            doneMsg += " `npm install` to install node modules first";
        }
    }

    if (hasPm2) {
        doneMsg += " and then type: `pm2 start utils/pm2/processes.json`.";
    } else {
        doneMsg += " and then type: `node server/ServerApp.js`.";
    }

    doneMsg += " You can now open the formbird app in a browser at: http://localhost:3000." +
        " The reverse proxy (usually nginx) will need to be configured to point at localhost:3000.";

    console.log("********************************************");
    console.log("***** Formbird installation completed. *****");
    console.log("********************************************");
    console.log(doneMsg);
}

function determineAnswer() {
    if (!formbirdAppName) {
        console.log("*** App name is empty. Use default app name: formbird");
        formbirdAppName = 'formbird-web-app';
    }
    if (!formbirdDbName) {
        console.log("*** DB name is empty. Use default database name: formbird");
        formbirdDbName = 'formbird';
    }
    if (!srcCustomCompUrl) {
        console.log("*** Custom component path is empty. Use default path: http://download.formbird.com/custom-component-modules.7z");
        srcCustomCompUrl = "http://download.formbird.com/custom-component-modules.7z";
    }
    if (!srcBaseMongoDBUrl) {
        console.log("*** Base MongoDB package path is empty. Use default path: http://download.formbird.com/formbird-mongodb-base.7z");
        srcBaseMongoDBUrl = "http://download.formbird.com/formbird-mongodb-base.7z";
    }
}

function copySrc(srcFolder, targetFolder) {
    fs.emptyDirSync(targetFolder);
    fs.copySync(srcFolder, targetFolder);
}

function backup(folderName, folderPath) {
    var targetPath = targetFbApp + '/backup/' + folderName;
    if (fs.pathExistsSync(folderPath)) {
        fs.copySync(folderPath, targetPath);
    }
}

function restore(folderName, folderPath) {
    var srcPath = targetFbApp + '/backup/' + folderName;
    if (fs.pathExistsSync(srcPath)) {
        fs.copySync(srcPath, folderPath);
    }
}

function copyFiles() {

    targetFbApp = './' + formbirdAppName;
    targetFbApp = path.resolve(targetFbApp);

    fs.ensureDirSync(targetFbApp);

    var fbPrjPath = __dirname;

    // copy LICENSE
    fs.copySync(fbPrjPath + '/LICENSE', targetFbApp + '/LICENSE');

    // copy package.json, yarn.lock
    fs.copySync(fbPrjPath + '/package.json', targetFbApp + '/package.json');
    fs.copySync(fbPrjPath + '/yarn.lock', targetFbApp + '/yarn.lock');

    // copy shared folder
    copySrc(fbPrjPath + '/shared', targetFbApp + '/shared');

    // backup previous folders
    backup('vendor', targetFbApp + '/server/public/vendor');
    backup('config', targetFbApp + '/server/config');

    // copy server folder
    copySrc(fbPrjPath + '/server', targetFbApp + '/server');

    // create & copy utils folder
    copySrc(fbPrjPath + '/installer/utils', targetFbApp + '/utils');

    fs.ensureDirSync(targetFbApp + '/files');

    copySrc(fbPrjPath + '/installer/utils/formbird/app-config', targetFbApp + '/server/config');

    // restore previous config folder
    restore('config', targetFbApp + '/server/config');

    // setting up the dbName
    let file = editJsonFile(targetFbApp + '/server/config/web-app.json');
    file.set("dataServices.default.db", formbirdDbName);
    file.set("elasticsearch.indexName", formbirdDbName);
    file.save();

    // restore previous vendor folder
    restore('vendor', targetFbApp + '/server/public/vendor');

    // remove backup folder
    fs.removeSync(targetFbApp + '/backup');
}

function downloadCustomCom(cb) {
    let vendorPath = targetFbApp + '/server/public/vendor/';
    let downloadToPath = vendorPath + 'custom-component-modules.7z';

    if (!fs.existsSync(vendorPath)) {
        fs.mkdirSync(vendorPath);
    }

    let cur = 0, len = 0, total = 0;
    var out = fs.createWriteStream(downloadToPath);

    let customComponentReq = request({
        method: 'GET',
        uri: srcCustomCompUrl
    });

    customComponentReq.pipe(out);

    customComponentReq.on('response', function (data) {
        len = parseInt(data.headers['content-length'], 10);
        total = len / 1048576; //1048576 - bytes in  1Megabyte
        console.log("Downloading compressed file " + total + " mb to " + downloadToPath);
    });

    customComponentReq.on("data", function (chunk) {
        cur += chunk.length;
        console.log("Downloading " + (100.0 * cur / len).toFixed(2) + "% " + (cur / 1048576).toFixed(2) + " mb " + ". Total size: " + total.toFixed(2) + " mb");
    });

    out.on('finish', function () {
        out.close();
        console.log("*** Download Complete.\n");

        console.log("*** Extracting the package...");
        // background extraction
        const sevenBin = require('7zip-bin');
        const { extractFull } = require('node-7z');
        const componentExtract = extractFull(downloadToPath, vendorPath, {
            $bin: sevenBin.path7za
        });

        componentExtract.on('end', function () {
            setTimeout(function() {

                process.chdir(targetFbApp);

                fs.unlinkSync(downloadToPath);

                cb();
            });
        });
    });
}

function downloadBaseMongoDB(cb) {
    let baseDBPath = targetFbApp + '/utils/mongodb/';
    let downloadToPath = baseDBPath + 'formbird-mongodb-base.7z';

    if (!fs.existsSync(baseDBPath)) {
        fs.mkdirSync(baseDBPath);
    }

    let cur = 0, len = 0, total = 0;
    var out = fs.createWriteStream(downloadToPath);

    let customComponentReq = request({
        method: 'GET',
        uri: srcBaseMongoDBUrl
    });

    customComponentReq.pipe(out);

    customComponentReq.on('response', function (data) {
        len = parseInt(data.headers['content-length'], 10);
        total = len / 1048576; //1048576 - bytes in  1Megabyte
        console.log("Downloading compressed file " + total + " mb to " + downloadToPath);
    });

    customComponentReq.on("data", function (chunk) {
        cur += chunk.length;
        console.log("Downloading " + (100.0 * cur / len).toFixed(2) + "% " + (cur / 1048576).toFixed(2) + " mb " + ". Total size: " + total.toFixed(2) + " mb");
    });

    out.on('finish', function () {
        out.close();
        console.log("*** Download Base MongoDB Complete.\n");

        console.log("*** Extracting the package...");
        // background extraction
        const sevenBin = require('7zip-bin');
        const { extractFull } = require('node-7z');
        const componentExtract = extractFull(downloadToPath, baseDBPath, {
            $bin: sevenBin.path7za
        });

        componentExtract.on('end', function () {
            setTimeout(function() {

                process.chdir(targetFbApp);

                fs.unlinkSync(downloadToPath);

                cb();
            });
        });
    });
}

function doCreateProject() {

    copyFiles();

    downloadCustomCom(function() {

        downloadBaseMongoDB(function() {

            hasYarn = hasPackage('yarn');
            hasPm2 = hasPackage('pm2');

            if (!installModule || installModule === 'y') {

                installNodeModules();

                showCompleteMessage();

            } else {

                showCompleteMessage(true);
            }
        });
    });
}

var showVersion = false;
process.argv.forEach(function (param, position) {
    if (position === 2 && param === '--version') {
        showVersion = true;
    }
    if (position === 2) {
        formbirdAppName = splitValue(param, "formbirdAppName").value;
    }
    if (position === 3) {
        formbirdDbName = splitValue(param, "formbirdDbName").value;
    }
});

printVersion();

if (!showVersion) {

    if (!formbirdAppName) { //means there has no formbirdDbName also. Let prompt

        const questions = [{
            type: 'input',
            name: 'appName',
            message: 'What name would you like to use for the workspace? (default: formbird-web-app) '
        }, {
            type: 'input',
            name: 'dbName',
            message: 'What database name would you like to use? (default: formbird) '
        }, {
            type: 'input',
            name: 'customCompModule',
            message: 'Enter the URL where custom component modules downloaded? (default: http://download.formbird.com/custom-component-modules.7z)'
        }, {
            type: 'input',
            name: 'baseMongoDBDownload',
            message: 'Enter the URL where base MongoDB package downloaded? (default: http://download.formbird.com/formbird-mongodb-base.7z)'
        }, {
            type: 'input',
            name: 'installModule',
            message: 'Would you like to install node modules (y/n)?'
        }];

        prompt(questions).then(answers => {

            formbirdAppName = answers.appName;
            formbirdDbName = answers.dbName;
            srcCustomCompUrl = answers.customCompModule;
            srcBaseMongoDBUrl = answers.baseMongoDBDownload;
            installModule = answers.installModule;

            determineAnswer();

            doCreateProject();
        });

    } else {
        doCreateProject();
    }
}