#!/usr/bin/env node
'use strict';

require('sjljs');

var fs = require('fs'),
    path = require('path'),
    exec = require('child_process').exec,
    dargs = require('dargs'),
    argv = require('yargs')
        // `git commit` flags with aliases (used to capture the alias
        // values for usage from within script)
        .alias('a', 'all')
        .alias('p', 'patch')
        .alias('C', 'reuse-message')
        .alias('c', 'reedit-message')
        .alias('z', 'null')
        .alias('F', 'file')
        .alias('m', 'message')
        .alias('t', 'template')
        .alias('s', 'signoff')
        .alias('n', 'no-verify')
        .alias('e', 'edit')
        .alias('i', 'include')
        .alias('o', 'only')
        .alias('u', 'untracked-files')
        .alias('v', 'verbose')
        .alias('q', 'quiet')
        .alias('S', 'gpg-sign')
        .argv,

    chalk = require('chalk'),
    moduleName = 'gitp',
    log = console.log;

module.exports = (function () {

    var ID_RULE_REGEX = /^[a-z](\-|_)?\d+/i,
        containedInPermissableError = [
            'nothing to commit'
        ];

    function getBranchName () {
        // Promise wrap for `exec`
        return (new Promise(function (resolve, reject) {

            // Execute command for getting only the current branch we are on // @see http://stackoverflow.com/questions/1417957/show-just-the-current-branch-in-git
            exec('git rev-parse --abbrev-ref HEAD', function (err, stdout, stderr) {
                // If err
                if (!sjl.empty(err)) {
                    handleSubProcessError(err, reject);
                }

                // If stderr
                if (!sjl.empty(stderr)) {
                    handleSubProcessError(stderr, reject);
                }

                // If output print/log it
                if (!sjl.empty(stdout)) {

                    // Print standard output
                    //log(chalk.cyan('\nCurrently on branch:'), stdout);

                    // Log a new line character
                    log();
                    resolve(stdout.trim());
                }

            }); // /exec

        }));
    }

    function extractStoryId (branchName) {
        // If branch doesn't adhere to id rule warn the user about this and exit
        if (!ID_RULE_REGEX.test(branchName)) {
            console.warn(chalk.cyan(branchName) + chalk.yellow('" doesn\'t contain a match for the specified story id rule: "' + ID_RULE_REGEX.toString() + '".'));
            process.exit(0);
        }

        // Return the story id
        return branchName.match(ID_RULE_REGEX)[0];
    }

    function getHumanReadableStoryId (storyId) {
        var output = '';
        output += storyId[0].toUpperCase();
        // If string doesn't already have a character separator (-|_)
        // insert one
        if (!/\-\_/.test(storyId[1])) {
            output += '-';
        }
        output += storyId.substring(1);
        return output;
    }

    function isPermissableError(err) {
        return containedInPermissableError.filter(function (permissable) {
            err.toString().indexOf(permissable) > -1;
        }).length > 0;
    }

    function handleSubProcessError(err, rejectCallback) {
        var classOfError = sjl.classOf(err),
            passThrough = true;
        if (classOfError === 'Error' && err.killed !== false) {
            passThrough = isPermissableError(err);
        }
        if (!passThrough) {
            rejectCallback();
            throw err;
        }
    }

    function executeCommand (cmdString) {
        // Promise wrap for `exec`
        return (new Promise(function (resolve, reject) {

            // Execute command and get a handle to it
            exec(cmdString, {maxbuffer: Number('1024e5')}, function (err, stdout, stderr) {
                // If err
                if (!sjl.empty(err)) {
                    handleSubProcessError(err, reject);
                }

                // If stderr
                if (!sjl.empty(stderr)) {
                    handleSubProcessError(stderr, reject);
                }

                // If output print/log it
                if (!sjl.empty(stdout)) {

                    // Print standard output
                    log(stdout);
                    resolve();

                } // /if stdout

            }); // /exec

        }));
    }

    function compileCommandString (program) {
        // Yargs injects process.argv[1] into itself and
        // we don't want that value to passed to git so we remove it here
        if (argv.hasOwnProperty('$0')) {
            delete argv['$0'];
        }

        // Delete alias keys (single letter keys since dargs will
        // always append '--' to the flags (use full name flags instead))
        Object.keys(argv).forEach(function (key) {
            if (key.length === 1 && key !== '_') {
                delete argv[key];
            }
        });

        // Compile and return command string
        return [program, argv._.shift()].concat(dargs(argv)).join(' ');
    }

    function plainOldGitCommit () {
        return executeCommand(compileCommandString('git'));
    }

    function gitpCommit() {
        return getBranchName().then(
            // Success call
            function (branchName) {
                var storyId = extractStoryId(branchName),
                    commandString;
                argv.message = '"' + getHumanReadableStoryId(storyId) + ' - ' + argv.message + '"';
                commandString = compileCommandString('git');
                return executeCommand(commandString).fail(function (failure) {
                        if (!sjl.empty(failure)) {
                            log(failure);
                        }
                    });
            },
            // Failure call
            function (failureMessage) {
                if (!sjl.empty(failureMessage)) {
                    log(failureMessage);
                }
                process.exit(0);
            });
    }

    // If not a valid call exit
    if (argv._.length === 0) {

        // Print help message(s)
        log(fs.readFileSync(path.join(__dirname, 'README.md'), {encoding: 'utf-8'}));

        // Exit process
         process.exit(0);
        return;
    }

    // If not a `commit` command send command to git directly
    if (argv._.indexOf('commit') === 0 && !sjl.empty(argv.message)) {
        gitpCommit();
    }
    else {
        plainOldGitCommit();
    }

}()); // /module.exports