/**
 * git-tag.js
 * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Contact:
 * Sungmin Kim <sm.art.kim@samsung.com>
 * Jonghwan Park <iwin100.park@samsung.com>
 * Kitae Kim <kt920.kim@samsung.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 * - S-Core Co., Ltd
**/

var async = require('async');
var fs = require('fs');
var optimist = require('optimist');
var path = require('path');
var _ = require('underscore');
var mysql = require('mysql');
var extfs = require('fs-extra');

var utils = require('../../lib/utils.js');

var ProjectModel = require('../dibs.model.common/project.js');
var SnapshotModel = require('../dibs.model.common/snapshot.js');
var ArtifactModel = require('../dibs.model.common/artifact.js');

var Git = require('../dibs.scm.git/git.js');

// print log messages using color code.
function stylize(str, style) {
    var styles = {
        grey: [90, 39],
        red: [31, 39],
        green: [32, 39],
        yellow: [33, 39]
    };
    return '\033[' + styles[style][0] + 'm' + str + '\033[' + styles[style][1] + 'm';
}

['grey', 'yellow', 'red', 'green'].forEach(function (style) {
    String.prototype.__defineGetter__(style, function () {
        return stylize(this, style);
    });
});

// custom setting
var customlog = {
    debug: function (str) { console.log(str.toString('utf-8').grey); },
    info: function (str) { console.log(str.toString('utf-8').green); },
    error: function (str) { console.log(str.toString('utf-8').red); },
    warn: function (str) { console.log(str.toString('utf-8').yellow); }
};

var usageMessage =
    'This tool is command line interface for migrating snapshots information into database\n' +
    'Usage: $0 <SUBCOMMAND> [OPTS]\n' +
    'Subcommands:\n' +
    '  extract      Extract build info such as git repo, commit, branch and version from database\n' +
    '  tag          Add git tagging upstream\n' +
    'Subcommand usage:\n' +
    '  extract      git-tag extract -D <distribution name> -S <snapshot name> -H <db host> -u <db user name> -p <db password> -d <db name>\n' +
    '  tag          git-tag tag -t <tag name> [-m <tag message>]\n';

var argv = optimist.usage(usageMessage)
    .describe('D', 'target distribution.        { develop }')
    .describe('S', 'target Snapshot.            { TizenStudio_1.1 }')
    .describe('H', 'database host.              { db-host }')
    .describe('u', 'database user name.         { db-user }')
    .describe('p', 'database password.          { db-password }')
    .describe('d', 'target database name.       { db-name } ')
    .describe('t', 'tag name.                   { TizenStudio_1.1 } ')
    .describe('m', 'tag message.                { tizen_studio_1.1 } ')
    .describe('h', 'show help')
    .string('u')
    .string('p')
    .string('d')
    .string('D')
    .string('S')
    .string('H')
    .string('t')
    .string('m')
    .alias({ h: 'help', D: 'dist', S: 'snapshot', H: 'host', u: 'user', p: 'password', d: 'database', t: 'tag', m: 'msg'})
    .argv;

if (argv.h) {
    optimist.showHelp();
    process.exit(0);
}

// arguments
var targetDist = argv.D;
var targetSnapshot = argv.S;

var dbHost = argv.H;
var dbUser = argv.u;
var dbPassword = argv.p;
var dbName = argv.d;

var gitTagName = argv.t;
var gitTagMsg = argv.m;

var subCmds = argv._;

// validate sub-commands
if (!subCmds || subCmds.length === 0) {
    console.log('Usage: git-tag-cli <SUBCOMMAND> [OPTS]');
    console.log('Try \'git-tag-cli --help\' for more information');
    process.exit(-1);
}

handleSubcommands(subCmds[0]);


function handleSubcommands(command) {
    switch (command) {
    case 'extract':
        extractBuildInfoFromDB();
        break;
    case 'tag':
        addTagUpstreamRepo();
        break;
    default:
        customlog.error('Invalid sub-command: ' + subCmds);
        process.exit(-1);
    }
}


function extractBuildInfoFromDB() {
    var repos = {};
    var sourceList = path.join(process.cwd(), 'git_tag_list.txt');
    var projects = {};
    var snapshot = null;

    async.waterfall([
        function (cb) {
            searchSnapshotFromDB(function (err1, results) {
                snapshot = results;
                cb(err1);
            });
        },
        function (cb) {
            fs.access(sourceList, function (err) {
                if (!err) {
                    fs.unlink(sourceList, function (err1) {
                        cb(err1);
                    });
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            async.eachLimit(snapshot, 1, function (artifact, cb1) {
                var gitRepo = artifact.git_repo;
                var gitCommit = artifact.git_commit;

                if (!repos[gitRepo]) {
                    repos[gitRepo] = {};
                }

                var artifactInfo = {
                    name: artifact.package_name,
                    version: artifact.package_version,
                    gitRepo: artifact.git_repo,
                    gitCommit: artifact.git_commit,
                    gitBranch: artifact.git_branch
                };

                if (!repos[gitRepo][gitCommit]) {
                    repos[gitRepo][gitCommit] = artifactInfo;
                }
                cb1(null);
            },
            function (err) {
                cb(err);
            });
        },
        function (cb) {
            fs.writeFile(sourceList, JSON.stringify(repos, null, 4), { encoding: 'utf8', flag: 'a' }, function (err1) {
                cb(err1);
            });
        }
    ],
    function (err) {
        if (err) {
            console.error(err);
            process.exit(-1);
        } else {
            process.exit(0);
        }
    });
}


function addTagUpstreamRepo() {
    async.waterfall([
        function (cb) {
            customlog.info('Read build info from \'git_tag_list.txt\' file');
            fs.readFile(path.join(process.cwd(), 'git_tag_list.txt'), { encoding: 'utf8'}, function (err, data) {
                var repos = JSON.parse(data);
                cb(err, repos);
            });
        },
        function (repos, cb) {
            async.forEachOfLimit(repos, 1, function (commits, repo, cb1) {
                customlog.info('Start adding git tagging \'' + repo + '\'');
                addGitTag(commits, repo, function (err1) {
                    // ignore an error even if given tag-name does already exist.
                    cb1(null);
                });
            },
            function (err) {
                cb(err);
            });
        }
    ],
    function (err) {
        if (err) {
            console.error(err);
            process.exit(-1);
        } else {
            process.exit(0);
        }
    });
}


function addGitTag(gitCommits, gitRepo, callback) {
    var workDir = path.join(process.cwd(), path.basename(gitRepo).split('.')[0]);
    customlog.info(' ## - workdir: ' + workDir);

    if (!gitTagMsg) {
        gitTagMsg = '';
    }

    async.waterfall([
        function (cb) {
            fs.access(workDir, function (err) {
                if (!err) {
                    cb(null, true);
                } else {
                    cb(null, false);
                }
            });
        },
        function (isExist, cb) {
            customlog.info(' ## git-repo: ' + gitRepo);
            if (isExist) {
                customlog.info(' ## git pull ' + gitRepo);
                Git.pull(gitRepo, null, {
                    workPath: workDir,
                    onStdout: function (string) {
                        customlog.info(string);
                    },
                    onStderr: function (string) {
                        customlog.error(string);
                    }
                }, function (err1) {
                    if (err1) {
                        customlog.error(err1);
                    }
                    cb(err1);
                });
            } else {
                customlog.info(' ## - git clone ' + gitRepo);
                Git.clone(gitRepo, {
                    onStdout: function (string) {
                        customlog.info(string);
                    },
                    onStderr: function (string) {
                        customlog.error(string);
                    }
                }, function (err1) {
                    if (err1) {
                        customlog.error(err1);
                    }
                    cb(err1);
                });
            }
        },
        function (cb) {
            async.eachLimit(gitCommits, 1, function (artifact, cb1) {
                customlog.info(' ## - git repo ' + gitRepo);

                var branch = artifact.gitBranch.split('_');
                var platform = branch[branch.length - 1];
                // search pattern such as p2.3.1 or v3.6
                var regExp = /\w+(\d+(\.\d+)+)/;
                var tagName = _.clone(gitTagName);
                if (regExp.exec(platform)) {
                    tagName = tagName.concat('_', platform);
                }
                customlog.info(' ## - git tag: \'' + tagName + '\'');

                Git.tag(artifact.gitCommit, tagName, {
                    workPath: workDir,
                    tagOptions: ['-m', gitTagMsg]
                }, function (err1) {
                    cb1(err1);
                });
            },
            function (err1) {
                cb(err1);
            });
        }
    ],
    function (err) {
        callback(err);
    });
}


/*
 * utility APIs
 */
function createDBConnection(callback) {
    var config = {
        host: dbHost || '127.0.0.1',
        port: 3306,
        user: dbUser || 'root',
        password: dbPassword || 'password',
        database: dbName || 'test'
    };

    var client = mysql.createConnection(config);
    client.connect(function (err) {
        if (err) {
            customlog.error(err);
        }
        callback(err, client);
    });
}


function searchSnapshotFromDB(callback) {
    var conn = null;

    async.waterfall([
        function (cb) {
            createDBConnection(function (err, client) {
                conn = client;
                cb(err);
            });
        },
        function (cb) {

            var query = 'select a.distribution_name' +
                ', a.name snapshot_name' +
                ', d.id artifact_id' +
                ', d.name package_name' +
                ', d.type package_type' +
                ', d.version package_version' +
                ', d.environment os' +
                ', e.value project_name' +
                ', f.value git_repo' +
                ', g.value git_branch' +
                ', h.value git_commit' +
                ' from snapshots a' +
                    ', snapshot_artifact c' +
                    ', artifacts d' +
                    ', (select * from artifact_info where property = ' + utils.DBStr('__PROJECT_NAME') + ') e' +
                    ', (select * from artifact_info where property = ' + utils.DBStr('__GIT_REPO') + ') f' +
                    ', (select * from artifact_info where property = ' + utils.DBStr('__GIT_BRANCH') + ') g' +
                    ', (select * from artifact_info where property = ' + utils.DBStr('__GIT_COMMIT_ID') + ') h' +
                ' where a.distribution_name =' + utils.DBStr(targetDist) +
                    ' and a.name = ' + utils.DBStr(targetSnapshot) +
                    ' and a.id = c.snapshot_id' +
                    ' and c.artifact_id = d.id' +
                    ' and d.id = e.artifact_id' +
                    ' and d.id = f.artifact_id' +
                    ' and d.id = g.artifact_id' +
                    ' and d.id = h.artifact_id';

            conn.query(query, function (err1, results) {
                if (err1) {
                    cb(err1, null);
                } else {
                    if (results.length !== 0) {
                        cb(null, results);
                    } else {
                        cb(null, null);
                    }
                }
            });
        }
    ],
    function (err, snapshot) {
        conn.end(function (err1) {
            if (err1) {
                customlog.error(err1);
            }

            if (err) {
                customlog.error(err);
                return callback(err, null);
            } else {
                return callback(null, snapshot);
            }
        });
    });
}