/**
 * test-automation-project.js
 * Copyright (c) 2000 - 2015 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 url = require('url');
var async = require('async');
var jenkins = require('jenkins');
var _ = require('underscore');
var shouldProxy = require('should-proxy');
var http = require('http');

var Job = require('../dibs.model.common/job.js');
var DError = require('../../core/exception');
var TizenCommon = require('../org.tizen.common/tizen_common.js');
var dibs = require('../../core/dibs.js');

/**
 * Tizen test-automation project plugin
 * @module models/tizen-test-automation-project/project
 */

/**
 * Tizen test automation job
 * @constructor
 * @augments module:models/job
 * @param {module:models/job~Job} baseJob - base job object
 */

function TizenTestAutomationJob(baseJob) {
    Job.copy(this, baseJob);

    this.compatJob = null;
    this.callback = null;
    if (!this.options.packages) {
        this.options.packages = [];
    }
    this.initEnvironments = dibs.projectTypes['Tizen-Test-Automation'].environments
        .filter(function (e) {
            return (e.indexOf('ubuntu') >= 0);
        });
    this.execEnvironments = dibs.projectTypes['Tizen-Test-Automation'].environments
        .filter(function (e) {
            return (e.indexOf('ubuntu') >= 0);
        });
}

/**
 * Create tizen git job instance
 * @function createJob
 * @param {string} prjName - project name
 * @param {string} prjType - project type
 * @param {string} distName - distribution name
 * @param {module:models/job.options} options - job options
 * @param {module:lib/utils.callback_error} callback - callback(error)
 * @memberOf module:models/tizen-project/project
 */

function createJob(userEmail, distName, prjName, prjType, environmentName, parentId, distType, options, callback) {
    Job.create(userEmail, distName, distType, prjName, prjType, environmentName, parentId, null, options,
        function (err, newJob) {
            callback(err, new TizenTestAutomationJob(newJob));
        });
}
module.exports.createJob = createJob;


/**
 * Initialize Tizen test-automation job
 * @function initializeJob
 * @param {module:models/job~Job} job - job
 * @param {string} workDir - workDir
 * @param {module:core/base-server.BaseServer} server - server
 * @param {module:lib/utils.callback_error} callback - callback(error)
 * @memberOf module:models/tizen-project/project
 */
function initializeJob(job, options, callback) {
    if (job.status === 'INITIALIZED') {
        return callback(null);
    }
    callback(null, job);
}
module.exports.initializeJob = initializeJob;


/**
 * Execute Tizen test-automation job
 * @function executeJob
 * @param {module:models/job~Job} job - job
 * @param {string} workDir - workDir
 * @param {module:core/base-server.BaseServer} server - server
 * @param {module:lib/utils.callback_error} callback - callback(error)
 * @memberOf module:models/tizen-project/project
 */
function executeJob(job, options, callback) {
    var monitor = options.monitor;
    var informations = {};
    informations.jenkinsURL = job.options.JENKINS_URL || null;
    informations.jenkinsProject = job.options.PROJECT_NAME || null;
    informations.jenkinsUserId = job.options.USER_ID || null;
    informations.jenkinsAPIKey = job.options.API_KEY || null;
    informations.jenkinsToken = job.options.TOKEN || null;
    informations.jenkinsArg = job.options.JENKINS_ARG || {};

    async.waterfall([
        function (cb) {
            monitor.updateProgress('Validate input value...');

            if (!informations.jenkinsURL && !informations.jenkinsProject) {
                cb(new DError('TIZENTESTAUTOJOB001'));
            } else {
                cb(null);
            }
        }, function (cb) {
            monitor.updateProgress('Trigger jenkins build...');

            var retryCount = 0;
            async.retry({
                times: 5,
                interval: 10 * 1000 // 10sec
            }, function (cb1) {
                retryCount++;
                if (process.env.http_proxy || process.env.https_proxy) {
                    // proxy environment
                    triggerWithProxy(informations, monitor, cb1);
                } else {
                    trigger(informations, monitor, cb1);
                }
            }, function (err1) {
                if (err1) {
                    monitor.updateProgress(' - failed to trigger jenkins build... retrying... ' + retryCount);
                    cb(err1);
                } else {
                    monitor.updateProgress(' - success');
                    cb(null);
                }
            });
        }, function (cb) {
            monitor.updateProgress('Terminating job...', cb);
        }
    ], function (err) {
        if (err) {
            monitor.updateProgress(' - remote build failed:' + err.message);
        }

        // NOTE. MUST strip unnecessary information for reducing object size
        //       if not, DNODE RPC cannnot receive callback
        TizenCommon.stripJob(job);

        // This job must be finished with SUCCESS state
        callback(null, job);
    });
}
module.exports.executeJob = executeJob;


function trigger(informations, monitor, callback) {
    var targetJenkins;

    monitor.updateProgress(' - Connect to jenkins');
    monitor.updateProgress(' -- url: ' + informations.jenkinsURL);

    if (informations.jenkinsUserId && informations.jenkinsAPIKey) {
        var urlObj = url.parse(informations.jenkinsURL);
        var protocol = urlObj.protocol;
        var host = informations.jenkinsURL.split(protocol + '//')[1];
        var auth = informations.jenkinsUserId + ':' + informations.jenkinsAPIKey + '@';

        targetJenkins = jenkins(protocol + '//' + auth + host);
    } else {
        targetJenkins = jenkins(informations.jenkinsURL);
    }

    monitor.updateProgress(' - Trigger remote build');
    monitor.updateProgress(' -- project name: ' + informations.jenkinsProject);
    monitor.updateProgress(' -- parameters: ' + JSON.stringify(informations.jenkinsArg));
    targetJenkins.job.build({
        name: informations.jenkinsProject,
        parameters: informations.jenkinsArg
    }, callback);
}

function triggerWithProxy(informations, monitor, callback) {
    var options = makeHttpRequest(informations);
    if (options) {
        monitor.updateProgress(' - Trigger remote build');
        monitor.updateProgress(' -- project name: ' + informations.jenkinsProject);
        monitor.updateProgress(' -- parameters: ' + JSON.stringify(informations.jenkinsArg));

        http.get(options, function (res) {
            monitor.updateProgress(' -- got response: CODE ' + res.statusCode);
            callback(null);
        }).on('error', function (err) {
            callback(new Error('NETERR : ' + err.message));
        });
    } else {
        callback(new Error('Can not create request'));
    }
}

// make a request to a tunneling proxy
function makeHttpRequest(informations) {
    // remove last slash
    var jenkinsURL = informations.jenkinsURL.replace(/\/$/, '');
    var proxy = null;
    var doProxy = shouldProxy(jenkinsURL, {
        no_proxy: process.env.no_proxy
    });

    if (doProxy) {
        proxy = getProxy(jenkinsURL);
    }

    if (!proxy) {
        return null;
    }

    var proxyUrl = url.parse(proxy);
    var auth = '';
    if (informations.jenkinsUserId && informations.jenkinsAPIKey) {
        auth = informations.jenkinsUserId + ':' + informations.jenkinsAPIKey;
    }

    var path = jenkinsURL + '/job/' + informations.jenkinsProject + '/';
    if (informations.jenkinsArg === {}) {
        path += 'build?token=' + informations.jenkinsToken;
    } else {
        path += 'buildWithParameters?token=' + informations.jenkinsToken + '&';
        path += _.map(informations.Arg, function (value, key) {
            return key + '=' + value;
        }).join('&');
    }

    var options = {};
    options.host = proxyUrl.hostname;
    options.port = proxyUrl.port * 1;
    options.path = path;
    options.agent = false;
    options.auth = auth;

    return options;
}

function getProxy(inputURL) {
    var urlObj = url.parse(inputURL);

    if (urlObj.protocol === 'http:') {
        return process.env.http_proxy;
    } else if (urlObj.protocol === 'https:') {
        return process.env.https_proxy;
    } else {
        return null;
    }
}
