"use strict";

const fs = require('fs');
const UtilFileSystem = require('../server/utils/UtilFileSystem');
const spawn = require('cross-spawn').spawn;

const gulpConfig =  {
    debugPort       : 5858,
    buildConfig     : {
        serverDir : "./server",
        publicDir   : "./server/public",
        privateDir  : "./server/private",
        clientDir : "./client",
        clientAppDir : "./client/app",
        docsDir   : "./docs",
        hashFile : "./.hashfile",
        schematicDir: "./fieldtec-schematic/src/ng-new/files",
        sharedDir : "./shared",
        distDir: "./dist"
    }
};

const err = function() {
    const notify = require("gulp-notify");
    var args = Array.prototype.slice.call(arguments);

    // Send error to notification center with gulp-notify
    notify.onError({
        title: "Compile Error",
        message: "<%= error.message %>"
    }).apply(this, args);

    // Keep gulp from hanging on this task
    this.emit('end');
};

const getPackageJson = function () {
    // `fs` is used instead of require to prevent caching in watch (require caches)
    return JSON.parse(fs.readFileSync('./package.json', 'utf8'));
};

const getReleaseDetails = function () {
    // `fs` is used instead of require to prevent caching in watch (require caches)
    return JSON.parse(fs.readFileSync('./ReleaseDetails.json', 'utf8'));
};

// build a client library using the Angular CLI
async function lernaBuild() {
    logger.info('Building libraries using lerna. This might take a moment...');
    return new Promise( (resolve, reject) => {
        const lernaBootstrap = spawn('npx', ['lerna', 'bootstrap'], { stdio: 'inherit' });
        lernaBootstrap.on('exit', code => {
            let exitCode = code.toString(); // call toString because the raw data is a buffer
            if (exitCode !== '0') {
                reject(new Error('lerna bootstrap exited with a non error code: ' + exitCode));
            } else {
                const lernaBuild = spawn('npx', ['lerna', 'run', 'build'], { stdio: 'inherit' });
                lernaBuild.on('exit', code => {
                    exitCode = code.toString();
                    if (exitCode !== '0') {
                        reject(new Error('lerna build exited with a non error code: ' + exitCode));
                    } else {
                        resolve();
                    }
                });
            }
        });
    });
}

async function updateDependencies()  {
  return new Promise( (resolve, reject) => {
    let buildArgs = [''];
    const ng = spawn('yarn', buildArgs, { stdio: 'inherit' });
    ng.on('exit', function (code) {
      const exitCode = code.toString(); // call toString because the raw data is a buffer
      if (code.toString() !== '0') {
        reject(new Error('yarn exited with a non error code: ' + exitCode));
      } else {
        resolve();
      }
    });
  });
}

async function buildClient(opt) {
    logger.info('Building Angular App into public folder. This might take a moment...');
    // Default ng build settings can be located at: angular.json > projects.fieldtec-web-ng-cli.architect.build
    // We'll override with custom settings to deploy it at server/public:
    // ng build --prod --output-path=dist --delete-output-path=false
    // Note that {{urlBasePath}}/ is meant for handlebars
    return new Promise( (resolve, reject) => {
        const publicFolder = gulpConfig.buildConfig.publicDir;
        let buildArgs = [
          'build',
          `--output-path=${publicFolder}`,
          '--delete-output-path=false',
          '--base-href={{urlBasePath}}/',
          '--stats-json',
          '--source-map'
        ];
        // production arguments:
        if (opt.prodMode !== false) {
          buildArgs = buildArgs.concat(['--configuration', 'production']);
        }

        const ng = spawn('ng', buildArgs, { stdio: 'inherit' });
        ng.on('exit', function (code) {
            const exitCode = code.toString(); // call toString because the raw data is a buffer
            if (code.toString() !== '0') {
                reject(new Error('ng build exited with a non error code: ' + exitCode));
            } else {
                resolve();
            }
        });
    });
}

async function preparePrivateFolder() {
    const privateFolder = gulpConfig.buildConfig.privateDir;
    if (!await UtilFileSystem.fileExists(privateFolder)) {
        logger.info('Creating private folder...');
        await UtilFileSystem.mkdir(privateFolder);
    }
    // public/index.html will always be replaced by StaticServer.js via handlebars to set the base path. See StaticServer.js
    // Base path value is determined at the configuration document (serverConfiguration.urlBase.path)
    // Handlebars expects that the original index.html generated from the build is copied to the private/ folder.
    const privateIndexHtml = `${privateFolder}/index.html`;
    if (await UtilFileSystem.fileExists(privateIndexHtml)) {
        await UtilFileSystem.removeFile(privateIndexHtml);
    }
    const publicFolder = gulpConfig.buildConfig.publicDir;
    const publicIndexHtml = `${publicFolder}/index.html`;
    if (await UtilFileSystem.fileExists(publicIndexHtml)) {
        await UtilFileSystem.copyFile(publicIndexHtml, privateIndexHtml);
    }
}

async function serveClient() {
    const ngPort = process.argv.indexOf('--ng-port');
    let portNo;
    if (ngPort > -1) {
        portNo = Number(process.argv[ngPort + 1]);
        if (isNaN(portNo)) {
            logger.error ('Invalid port number for ng-serve: ' + portNo + '. Using default port...');
        }
    }
    const ngArgs = ['serve'];
    if (portNo) {
        ngArgs.push(`--port`);
        ngArgs.push(portNo);
    }
    const ng = spawn('ng', ngArgs, { stdio: 'inherit' });
    ng.on('exit', function (code) {
        const exitCode = code.toString(); // call toString because the raw data is a buffer
        if (code.toString() !== '0') {
            logger.error('ng build exited with a non error code: ' + exitCode);
        }
    });
}

function flushRedis() {
    const { redis } = getAppConfig();
    const clientOptions = {
        url: `redis://${redis.host}:${redis.port}/${redis.databaseNumber}`
    };
    const redisClient = require("redis").createClient(clientOptions);
    return redisClient.flushdb();
}

let serverProcessConf;

function getProcessConfig() {
    if (!serverProcessConf) {
        serverProcessConf = require("../system/pm2/test-e2e");
        if (process.env.NODE_ENV) {
            serverProcessConf.env.NODE_ENV = process.env.NODE_ENV;
        }
        if (process.env.WEB_APP_SERVER) {
            serverProcessConf.env.WEB_APP_SERVER = process.env.WEB_APP_SERVER;
        }
    }
    return serverProcessConf;
}

function getAppConfig() {
    const { WEB_APP_SERVER } = getProcessConfig().env;
    return require(`../server/config/${WEB_APP_SERVER}`);
}


module.exports = {

    config: gulpConfig,

    err: err,

    getPackageJson: getPackageJson,

    getReleaseDetails: getReleaseDetails,

    buildClient,

    lernaBuild,

    serveClient,

    preparePrivateFolder,

    updateDependencies,

    flushRedis,

    getProcessConfig,

    getAppConfig
};
