Source: libs/utils/git.js

const _ = require('lodash');
const path = require('path');
const jira = require(path.resolve(__dirname,'jira.js'));
const semver = require('semver');
const exec = require('child_process').exec;
const semverMax = require('semver-max');

/** @class git */


/**
 * Given array, will review each value and verify value is valid semantically versioned tag.
 * @param tags
 * @returns {Array}
 */
function filterAllTags(tags){
    let results = _.map(tags,(tag)=>{
        if (semver.valid(tag)){
            return tag;
        }
    })
    return _.compact(results);
}

/**
 * Windows doesn't have awk, grep, etc - So we have to perform version collections into array through other means.
 * @param rawTagStr
 * @returns {Array}
 */
function windowsGetNextVersion(rawTagStr) {
    let cursor = rawTagStr.split("\n");
    let versions = [];
    _.each(cursor,(ver)=>{
       let staged = ver.slice(51);
        // Annontated tags as a stringhave a ^{} that we need to remove..
       if (staged.indexOf("^{}")>0) {
           versions.push(staged.replace("^{}",""));
       } else {
           versions.push(staged);
       }
    });
   return versions;
}

/**
 * Without cloning repository, get ALL tags through ls-remote, awk and grep command line executions.  Tag is compared to
 * target environment [version_seed] to ensure found tag is greater.  Returns evaluated highest version.
 * @param instance
 * @param callback
 */
function evaluateHighestRepoTag (instance,callback){

    console.log('Retrieving all remote tags from', instance.build_settings.repo_url);

    // If jobrunner not executing on winblows.
    if (process.platform !== 'win32'){
        exec(`git ls-remote --tags "${instance.build_settings.repo_url}" | awk '{print $2}' | grep -v '{}' | awk -F"/" '{print $3}' | sort -n -t. -k1,1 -k2,2 -k3,3`, (err,stdout,stderr)=>{
            if (err) callback(err);
            if (stderr !== "") callback(stderr);
            let tags = stdout.split("\n");
            tags = filterAllTags(tags);
            let highestInRepository;
            if (tags.length == 0 || _.isNil(tags)){
                console.log('No tags found in repository, setting default version of build to version_seed found in target environment settings.');
                highestInRepository = instance.build_settings.version_seed;
            } else {
                highestInRepository = tags.reduce(semverMax);
            }

            console.log('Found',tags.length, 'tags in repository - Evaluated highest version in repository as:',highestInRepository);
            // If tag version found in repository is greater than version_seed in settings.json then return value
            // Else return higher value found in version_seed.
            if (semver.gt(highestInRepository,instance.build_settings.version_seed)){
                callback(null,highestInRepository);
            } else {
                callback(null,instance.build_settings.version_seed);
            }
        })
    } else {
        exec(`git ls-remote --tags "${instance.build_settings.repo_url}"`, (err,stdout,stderr)=>{
            if (err) callback(err);
            if (stderr !== "") callback(stderr);
            let tags = windowsGetNextVersion(stdout);
            // Strip off 52 characters;
            //let tags = stdout.split("\n");
            tags = filterAllTags(tags);
            let highestInRepository;
            if (tags.length == 0 || _.isNil(tags)){
                console.log('No tags found in repository, setting default version of build to version_seed found in target environment settings.');
                highestInRepository = instance.build_settings.version_seed;
            } else {
                highestInRepository = tags.reduce(semverMax);
            }

            console.log('Found',tags.length, 'tags in repository - Evaluated highest version in repository as:',highestInRepository);
            // If tag version found in repository is greater than version_seed in settings.json then return value
            // Else return higher value found in version_seed.
            if (semver.gt(highestInRepository,instance.build_settings.version_seed)){
                callback(null,highestInRepository);
            } else {
                callback(null,instance.build_settings.version_seed);
            }
        })
    }
}

/**
 * Get commit sha of [instance.latest_tag] found in repository, and if not possible assign [instance.latest_tag_sha] to
 * current commit sha, which simply means no jira ids will be found for some given unknown, and don't really care reason
 * right now....
 * @param instance
 * @return {Promise}
 */
function getLatestTagCommitSha(instance){
    return new Promise((resolve)=>{
        console.log('Retrieving commit sha for tag:', instance.latest_tag);
        exec(`git ls-remote ${instance.build_settings.repo_url} | grep "${instance.latest_tag}" | cut -b 1-40`,(err,stdout,stderr)=>{
            if (err) instance.latest_tag_sha = instance.current_sha;
            if (stderr) instance.latest_tag_sha = instance.current_sha;
            let results = stdout.toString().replace(/(\r\n|\n|\r)/gm,"##LF##");
            let foundShas = _.compact(results.split("##LF##")); //Compact to remove any possible empty array items.
            instance.latest_tag_sha = foundShas[foundShas.length-1];
            resolve(null);
        })
    })
}


/**
 * Gets commit sha of respository without clone repository locally.
 * @param instance
 * @return {Promise}
 */
function getCurrentHeadSha(instance){
    return new Promise((resolve,reject)=>{
        exec(`git ls-remote ${instance.build_settings.repo_url} ${instance.arguments.branch} | cut -c1-40`,(err,stdout,stderr)=>{
            let sha = stdout.toString().replace(/(\r\n|\n|\r)/gm,"")
            console.log('Commit sha of', instance.arguments.branch, ':',sha);
            if (err) reject (err);
            if (stderr) reject(stderr);
            resolve(sha); //Replace all line endings, (Windows, mac, linux)
        })
    })
}


/**
 * Evaulates current highest version tag, and commit sha of branch provided by cli argument -b
 * @param instance
 * @return {Promise}
 */
exports.getRepoInformation = function (instance) {
    return new Promise ((resolve,reject)=>{
        evaluateHighestRepoTag(instance,(err,version)=>{
            if (err) reject(err);
            instance.latest_tag = version;
            getCurrentHeadSha(instance)
                .then((sha)=>{
                    instance.current_sha = sha;
                    getLatestTagCommitSha(instance)
                        .then(()=>{
                            resolve(null);
                        })

                })
        })
    })
}

/** Increments {instance.latest_tag} by corresponding versioning type [prepatch] [patch] [minor]
 * @memberOf git
 * @param instance
 * @return {Promise}
 */
exports.nextVersionTag = function(instance) {
    return new Promise((resolve)=>{
        if (!semver.valid(instance.latest_tag)) {
            console.log('Version tag invalid');
            process.exit(1);
        } else {
            if (instance.arguments.release === false || instance.arguments.release  === "false" || _.isNil(instance.arguments.release)){
                let next = semver.inc(instance.latest_tag,`${instance.build_settings.versioning}`);
                console.log('Next version:', next);
                instance.next_tag = next;
                resolve(null);
            } else {
                let next = semver.inc(instance.latest_tag,`${instance.build_settings.versioning}`);
                console.log('Next version:', next);
                instance.next_tag = next;
                resolve(null);
            }
        }
    })
}