var log4js = require('log4js');
var http = require('http');
var async = require('async');
var properties = require(process.env.SDNC_CONFIG_DIR + '/netdb-updater.json');
var admProperties = require(process.env.SDNC_CONFIG_DIR + '/admportal.json');
var csvtojson = require('csvtojson');
var mysql = require('mysql');
var moment = require('moment');
var os = require('os');
var fs = require('fs.extra');

// Check to make sure SDNC_CONFIG_DIR is set
var sdnc_config_dir = process.env.SDNC_CONFIG_DIR;
if ( typeof sdnc_config_dir == 'undefined' )
{
    console.log('ERROR the SDNC_CONFIG_DIR environmental variable is not set.');
    return;
}

// SETUP LOGGER
log4js.configure(process.env.SDNC_CONFIG_DIR + '/netdb.log4js.json');
var logger = log4js.getLogger('netdb');
logger.setLevel(properties.netdbLogLevel);

var yargs = require('yargs')
  .usage("\nUsage: node netdb_updater -t link_master|router_master")
  .demand('t')
  .alias('t', 'table')
  .example("Example: node netdb_updater -t link_master","Update SDNC LINK_MASTER table from NetDB.")
  .argv;

var dbTable = yargs.table;
var debug = properties.netdbDebug;
var env = properties.netdbEnv; 
var retSuccess = false;

// DB Setup
var currentDB = '';
var dbConnection = '';
var db01 = '';
var db02 = '';
var count = 0;
var errorMsg = [];

var dbtasks = [];
dbtasks.push( function(callback) { checkParams(callback); } );
dbtasks.push( function(callback) { dbConnect(callback); } );
//dbtasks.push( function(callback) { netdb(callback); } );

logger.debug('\n\n********** START PROCESSING - Env=' + env + ' Debug=' + debug + ' **********');

async.series(dbtasks, function(err,result){
    if(err) {
		logger.error(err + ' COUNT: ' + count);
    }
    else {
		if ( errorMsg.length > 0 ){
			logger.error(errorMsg);
		}
	}
});


function checkParams(scb){
	if ( dbTable != 'link_master' && dbTable != 'router_master' ){
		scb("Invalid parameter passed in '" + dbTable + " ' exiting.'");
	}
	else{
		scb(null);
	}
}


async.whilst(
    	function () { return count < properties.netdbRetryInterval },
    	function (callback) {
			if ( dbTable == 'link_master' ){
				getLinkMaster(callback);
			}
			else if (dbTable == 'router_master'){
				getRouterMaster(callback);
			}
			else{ // should never hit this condition
				logger.debug("Invalid parameter passed in '" + dbTable + " ' exiting.'");
			}
    	},
    	function (err) {
logger.debug('whilst err function errorMsg = ' + errorMsg);
			// report error
			if ( errorMsg.length > 0 ){
				logger.debug(errorMsg + ' COUNT: ' + count);
				process.exit(1);
			}
			else{
				logger.debug('success');
				process.exit(0);
			}
    	}
);


function returnError(emsg, cb){
	retSuccess=false;
	errorMsg.push(emsg);
	if ( count == properties.netdbRetryInterval ) { logger.error(errorMsg); }
    setTimeout( function(){
    		cb(null);
		}, properties.netdbWaitTime);
}

function returnSuccess(cb){
logger.debug('inside returnSuccess');
	errorMsg = '';
	//var cnt = properties.netdbRetryInterval;
	//logger.debug('b4 inc returnSuccess count=' + count);
	//count = ++cnt;
	//logger.debug('after inc returnSuccess count=' + count);
	//cb(null);
	retSuccess=true;
process.exit(0);
} 

function getRouterMaster(cb){

	logger.info('getRouterMaster debug=' + debug + ' count=' + count);

	// setup connection
    var netdbEnv = properties.netdbEnv;
    var auth_param = '';
    if ( netdbEnv == 'e2e' || netdbEnv == 'prod' ){
        // conexus network
        auth_param = '?auth=' + admProperties['ip-addresses']['eth2'] + ';'
    }else{
        // app network
        auth_param = '?auth=' + admProperties['ip-addresses']['eth1:0'] + ';'
    }
    var username = properties.netdbUser;;
    var password = properties.netdbPassword;
	var date = moment().format('YYYYMMDD');
    var auth = 'Basic ' + new Buffer(username + ':' + password).toString('base64');
    var host = properties.netdbHost;
    var port = properties.netdbPort;
    var path = '/' + properties.netdbPath
                + '/' + properties.netdbNetwork
                + '/' + properties.netdbApiName
                + auth_param
                + 'client=' + properties.netdbClientName + ';'
                + 'date=' + date + ';'
                + 'reportName=' + dbTable + ';'
                + 'type=' + properties.netdbType;

    var header = { 'Content-Type': 'text/csv' };
    //var header = {'Host': host, 'Authorization': auth, 'Content-Type': 'text/csv' };
    var options = {
        method            : "GET",
        path              : path,
        host              : host,
        port              : port,
        headers           : header
    };

	logger.debug('options:\n' + JSON.stringify(options,null,2));

    var request = http.request(options, function(response) {

    	var response_str = '';
		if ( retSuccess == true ){
			var cnt = properties.netdbRetryInterval;
			count = ++cnt;
		}
		else{
			count++;
		}

        logger.debug('STATUS: ' + response.statusCode + ' content-type=' + response.headers['content-type']);

        // Read the response from ODL side
        response.on('data', function(chunk) {
            response_str += chunk;
        });

        response.on('end', function() {

			logger.debug('HEADERS:' + JSON.stringify(response.headers));

            if(response.statusCode == 200){

                if(response_str.length > 0){

					// save the upload
					try{
						fs.writeFileSync('/sdncvar/sdnc/files/netdb-updater/' + moment().unix() + ".netdb." + dbTable + '.csv', response_str);
					}
					catch(e){
						// this is not in reqs, if it fails keep on going.
						logger.error('Error writing NetDB file:' + e);
					}

					if (response.headers['content-type'].indexOf('html') > 0){
						returnError('Error:Unexpected content-type:' + response.headers['content-type'] + ' returned.\n', cb);
						return;
					}
                    // need to parse csv file
                    var Converter=csvtojson.Converter;
                    var csvConverter = new Converter({
                        noheader:true
                    });
                    var routerMasterSQL = '';

                    // end_parsed will be emitted once parsing is finished
                    csvConverter.on("end_parsed", function(respObj){

                        routerMasterSQL = routerMasterSQL.concat("INSERT INTO ROUTER_MASTER (crs_name, loopback_ip)");
                        for ( var x=0; x < respObj.length; x++ ){

                           	if ( respObj[x].field1.length == 0 ){
                               	returnError('Required field [crs_name] is null.', cb);
                           	}

							if (x!=0){
								routerMasterSQL = routerMasterSQL.concat(' union ');
							}
							routerMasterSQL = routerMasterSQL.concat(" SELECT " 
								+ "'" + respObj[x].field1 + "',"
                               	+ "'" + respObj[x].field2 + "' FROM DUAL ");
                        }
                        //logger.debug('SQL: ' + routerMasterSQL);
	
			if (debug != 'debug' && env != 'dev'){

                        	var tasks = [];
                        	tasks.push( function(callback) { updateRouterMaster(routerMasterSQL,callback); } );
                        	async.series(tasks, function(err,result){
                            	if(err) {
                                	returnError(err,cb);
					return;
                            	}
                            	else {
					logger.info('*** Router Master Table Replaced ***');
                                	returnSuccess(cb);
					return;
                            	}
                        	});
			}
			else{
logger.debug('*** debug ***');
                            	returnSuccess(cb);
					return;
			}

                    });
                    csvConverter.on("error",function(errMsg,errData){
                        returnError(errMsg,cb);
						return;
                    });
                    csvConverter.fromString(response_str, function(err,result){
                        if(err){
							returnError(err,cb);
							return;
                        }
                    });
                }
                else{
                    //logger.debug("no data");
					returnError('no data',cb);
					return;
                }
            }
            else if(response.statusCode == 404){
				returnError('Router Master Table for ' + date + ' is not Available.',cb);
				return;
			}
            else {
				returnError('Status Code:' + response.statudCode + ' returned for Router Master Table query.',cb);
				return;
            }
        });
    });
    request.on('error', function(e) {
	    if ( retSuccess == true ){
			var cnt = properties.netdbRetryInterval;
			count = ++cnt;
		}
		else{
			count++;
		}	
        returnError(e,cb);
		return;
    });
    request.end();
}

function getLinkMaster(cb){

	logger.info('getLinkMaster debug=' + debug + ' count=' + count);

    // setup connection
    var netdbEnv = properties.netdbEnv;
    var auth_param = '';
    if ( netdbEnv == 'e2e' || netdbEnv == 'prod' ){
        // conexus network
        auth_param = '?auth=' + admProperties['ip-addresses']['eth2'] + ';'
    }else{
        // app network
        auth_param = '?auth=' + admProperties['ip-addresses']['eth1:0'] + ';'
    }
    var username = properties.netdbUser;;
    var password = properties.netdbPassword;
    var auth = 'Basic ' + new Buffer(username + ':' + password).toString('base64');
    var host = properties.netdbHost;
    var port = properties.netdbPort;
	var date = moment().format('YYYYMMDD');
    var path = '/' + properties.netdbPath
                + '/' + properties.netdbNetwork
                + '/' + properties.netdbApiName
                + auth_param
                + 'client=' + properties.netdbClientName + ';'
                + 'date=' + date + ';'
                + 'reportName=' + dbTable + ';'
                + 'type=' + properties.netdbType;

    var header = { 'Content-Type': 'text/csv' };
    //var header = {'Host': host, 'Authorization': auth, 'Content-Type': 'text/csv' };
    var options = {
        method            : "GET",
        path              : path,
        host              : host,
        port              : port,
        headers           : header
    };

    logger.debug('options:\n' + JSON.stringify(options,null,2));

	var request = http.request(options, function(response) {

        logger.debug('STATUS: ' + response.statusCode + ' content-type=' + response.headers['content-type']);

		if ( retSuccess == true ){
        	var cnt = properties.netdbRetryInterval;
        	count = ++cnt;
    	}
		else{
    		count++
		}

        var response_str = '';

        // Read the response from ODL side
        response.on('data', function(chunk) {
            response_str += chunk;
        });

        response.on('end', function() {

			logger.debug('HEADERS:' + JSON.stringify(response.headers));

            if(response.statusCode == 200){

				if(response_str.length > 0){

					//logger.debug('response_str=' + response_str);
					// save the upload
                    try{
						fs.writeFileSync('/sdncvar/sdnc/files/netdb-updater/' + moment().unix() + ".netdb." + dbTable + '.csv', response_str);
                    }
                    catch(e){
                        // this is not in reqs, if it fails keep on going.
                        logger.error('Error writing NetDB file:' + e);
                    }

					if (response.headers['content-type'].indexOf('html') > 0){
						returnError('Error:Unexpected content-type:' + response.headers['content-type'] + ' returned.\n', cb);
						return;
					}
					// need to parse csv file
					var Converter=csvtojson.Converter;
					var csvConverter = new Converter({
						noheader:true
					});

					var linkMasterSQL = '';

					// end_parsed will be emitted once parsing is finished
					csvConverter.on("end_parsed", function(jsonObj){

						linkMasterSQL = linkMasterSQL.concat("INSERT INTO LINK_MASTER (link_interface_ip, source_crs_name, destination_crs_name, link_speed, default_cost, bundle_name, shutdown)"); 
						for ( var x=0; x < jsonObj.length; x++ ){
							if ( jsonObj[x].field1.length == 0 ){
								returnError('Required field [link_interface_ip] is null.', cb);
								return;
							}
							if ( jsonObj[x].field2.length == 0 ){
								returnError('Required field [source_crs_name] is null.', cb);
								return;
							}
							if ( jsonObj[x].field3.length == 0 ){
								returnError('Required field [destination_crs_name] is null.', cb);
								return;
							}
							if (x!=0){
								linkMasterSQL = linkMasterSQL.concat(' union ');
							}

							linkMasterSQL = linkMasterSQL.concat(" SELECT " 
								+ "'" + jsonObj[x].field1 + "',"
								+ "'" + jsonObj[x].field2 + "',"
								+ "'" + jsonObj[x].field3 + "',"
								+ jsonObj[x].field4 + ","
								+ jsonObj[x].field5 + ","
								+ "'" + jsonObj[x].field6 + "',"
								+ "'" + jsonObj[x].field7 + "' FROM DUAL");
						}
						//logger.debug('SQL: ' + linkMasterSQL);

						if (debug != 'debug' && env != 'dev'){
                    		// update db
							var tasks = [];
							tasks.push( function(callback) { updateLinkMaster(linkMasterSQL,callback); } );
    						async.series(tasks, function(err,result){
								if(err)
                            	{
                                	returnError(err,cb);
									return;
                            	}
                            	else
                            	{
									logger.info('*** Link Master Table Replaced ***');
                                	returnSuccess(cb);
									return;
                            	}
    						});
						}
						else{
                        	returnSuccess(cb);
							return;
						}
					});
					csvConverter.on("error",function(errMsg,errData){
    					returnError(errMsg,cb);
						return;
  					});
					csvConverter.fromString(response_str, function(err,result){
						if(err){
    						returnError(errMsg,cb);
							return;
						}
					});
                }
                else{
    				returnError('no data',cb);
					return;
                }
            }
            else if(response.statusCode == 404){
				returnError('Link Master Table for ' + date + ' is not Available.',cb);
				return;
			}
            else {
				returnError('Status Code:' + response.statudCode + ' returned for Link Master Table query.',cb);
				return;
            }
    	});
	});
	request.on('error', function(e) {
		if ( retSuccess == true ){
        	var cnt = properties.netdbRetryInterval;
        	count = ++cnt;
    	}
		else{
    		count++
		}
        returnError(e,cb);
        return;
	});
	request.end();
}
			

function dbConnect(callback){

        var l_db01 = admProperties['databases']['0'];
	var db01Array = l_db01.split("|");
	db01 = db01Array[0];

        var l_db02 = admProperties['databases']['1'];
	var db02Array = l_db02.split("|");
	db02 = db02Array[0];

	if ( admProperties.dbFabric == 'true' )
	{
		logger.debug('connectFabric()');

    // testing 
    var fabric_connection = mysql.createConnection({
        host            : admProperties.dbFabricServer,
        user            : admProperties.dbFabricUser,
        password        : admProperties.dbFabricPassword,
        database        : admProperties.dbFabricDB,
        port            : admProperties.dbFabricPort
    });


    fabric_connection.connect( function(err) {

        if (err) {
            callback(err);
            return;
        }
        fabric_connection.query('CALL dump.servers()', function(err,rows) {

            var masterDB = '';

            if (err) {
                callback(err);
                return;
            }
            fabric_connection.end();
            logger.debug('rows: ' + JSON.stringify(rows,null,2));

            // fabric servers
            for ( var x=0; x<rows.length; x++)
            {
                // database servers
                for ( var y=0; y<rows[x].length; y++)
                {
                    var row = rows[x][y];
                    if (row.group_id == admProperties.dbFabricGroupId)
                    {
                        if (row.status == '3' && row.mode == '3'){
                            masterDB = row.host;
                        }
                    }
                }
            }
            logger.debug('currentDB: ' + currentDB);
            logger.debug('masterDB: ' + masterDB);

            if (masterDB.length <=0)
            {
                logger.debug('no writable master db');
                callback('no writable master db');
                return;
            }

            if ( currentDB != masterDB )
            {
                currentDB = masterDB;
                dbConnection = mysql.createConnection({
                    connectionLimit   : admProperties.dbConnLimit,
                    host              : currentDB,
                    user              : admProperties.dbUser,
                    password          : admProperties.dbPassword,
                    database          : admProperties.dbName,
                    multipleStatements: true,
                    debug             : false
                });
            }
            logger.debug('new currentDB: ' + currentDB);
            logger.debug('new masterDB: ' + masterDB);
            callback(null);
            return;
        });
        fabric_connection.on('error', function(err){
             logger.debug(err.code);
             callback(err);
		     return;
        });
    });
	}
	else
	{
    	currentDB = db01;

    	var dbConn = mysql.createConnection({
        	connectionLimit : admProperties.dbConnLimit,
        	host            : currentDB,
        	user            : admProperties.dbUser,
        	password        : admProperties.dbPassword,
        	database        : admProperties.dbName,
        	multipleStatements: true,
        	debug           : false
    	});
		logger.debug('initDB currentDB=' + currentDB);

    	dbConn.connect(function(err,connection){

        	if(err){
            	logger.debug( String(err) ); // ALARM
            	callback(err);
            	return;
        	}
        	var sql = 'select @@read_only';
        	dbConn.query(sql, function(err,result){
            	dbConn.end();

            	// @@read_only=0 means db is writable
            	logger.debug('@@read_only=' + result[0]['@@read_only']);
            	if ( result[0]['@@read_only'] != '0' )
            	{
                	if (currentDB == db01)
                	{
                    	currentDB = db02;
                	}
                	else
                	{
                    	currentDB = db01;
                	}
logger.debug('initDB reconnect to currentDB '+ currentDB);
                	var newConnection = mysql.createConnection({
                    	connectionLimit : admProperties.dbConnLimit,
                    	host            : currentDB,
                    	user            : admProperties.dbUser,
                    	password        : admProperties.dbPassword,
                    	database        : admProperties.dbName,
                    	multipleStatements: true,
                    	debug           : false
                	}); // end create
                	dbConnection = newConnection;
                	callback(null);
					return;
            	}
                dbConnection = dbConn;
            	callback(null);
				return;
        	});
    	});
	}
}

function updateLinkMaster(linkMasterSQL,callback){

logger.debug('updateLinkMaster');

    dbConnection.connect(function(err,connection)
    {
        if(err){
            logger.debug( String(err) ); // ALARM
            callback(err, 'Unable to get database connection.');
            return;
        }
    }); // end connection
    dbConnection.beginTransaction(function(err) {
    	if(err){
            //dbConnection.release();
            callback(err,String(err));
            return;
        }
        var sql = "DELETE FROM LINK_MASTER";
        dbConnection.query(sql,function(err,result)
        {
            if(err){
                //dbConnection.release();
                dbConnection.rollback( {rollback: 'NO RELEASE'},function(){
                    callback(err,String(err));
                    return;
                });
            }
            dbConnection.query(linkMasterSQL,function(err,result)
            {
                if(err){
                    //dbConnection.release();
                    dbConnection.rollback( {rollback: 'NO RELEASE'},function(){
                        callback(err,String(err));
                        return;
                    });
                }
                dbConnection.commit(function(err){
                    if(err){
                        //dbConnection.release();
                		dbConnection.rollback( {rollback: 'NO RELEASE'},function(){
                            callback(err,String(err));
                            return;
                        });
                    }
                    //dbConnection.release();
                    callback(null);
                });
            })
        });
    }); // end transaction
}

function updateRouterMaster(routerMasterSQL,callback){

logger.debug('updateRouterMaster');

    dbConnection.connect(function(err,connection)
    {
        if(err){
            logger.debug( String(err) ); // ALARM
            callback(err, 'Unable to get database connection.');
            return;
        }
    }); // end connection
    dbConnection.beginTransaction(function(err) {
        if(err){
            //dbConnection.release();
            callback(err,String(err));
            return;
        }
        var sql = "DELETE FROM ROUTER_MASTER";
        dbConnection.query(sql,function(err,result)
        {
            if(err){
                //dbConnection.release();
                dbConnection.rollback( function(){
                    callback(err,String(err));
                    return;
                });
            }
            dbConnection.query(routerMasterSQL,function(err,result)
            {
                if(err){
                    //dbConnection.release();
                	dbConnection.rollback( function(){
                        callback(err,String(err));
                        return;
                    });
                }
                dbConnection.commit(function(err){
                    if(err){
                        //dbConnection.release();
                		dbConnection.rollback( function(){
                            callback(err,String(err));
                            return;
                        });
                    }
                    //dbConnection.release();
                    callback(null);
                });
            })
        });
    }); // end transaction
}

/*

	logger.debug('getLinkMaster - count=' + count);
	if ( true ) {
		//fail
		setTimeout( function(){
			cb(null);}, properties.netdbWaitTime);
		return;
	}
	// success
	count = 10;
	cb(null);
}
*/