'use strict';

var q = require('q');
var logger = require('../../../utils/Logger').loggers.get('db');
var cbConnection = require('./CouchbaseConnection');
var UtilDocumentId = require('../../../../shared/utils/UtilDocumentId');
var ElasticIndexingService= require('../../../search/ElasticIndexingService');
var async = require('async');
var cbUtil = require('./CouchbaseUtil');
var cbBucket;

function buildUpdateQuery(document, oldDocument, bucketName) {

	var statement = "UPDATE `" + bucketName + "` ";

	var setStatement = "";
	var removeUnset = {};

	for (var key in document) {
		if (document[key] !== oldDocument[key]) {
			setStatement += " `" + key + "` = ";

            if (document.hasOwnProperty(key) && document[key] == null){
                document[key] = null;
                removeUnset[key] = "";
            }

			if (typeof document[key] === 'object' || typeof document[key] === 'string') {
				setStatement += JSON.stringify(document[key]);
			} else if (typeof document[key] === 'boolean') {
                setStatement +=  document[key] + " ";
			} else {
				statement += document[key];
			}

			setStatement += ",";
		}
	}

	if (setStatement && setStatement !== "") {
		statement += " SET " + setStatement.slice(0, -1);
	}

	var unsetStatement = "";
	for (var key in oldDocument) {
	    if (!removeUnset.hasOwnProperty(key) && document[key] === undefined){
            unsetStatement += " `" + key + "`,";
        }
	}

	if (unsetStatement && unsetStatement !== "") {
		statement += " UNSET " + unsetStatement.slice(0, -1);
	}

	statement += " WHERE documentId = '" + document.documentId +
		"' AND systemHeader.currentVersion = true;";

	return statement;
}

async function update(params){
    var documentId = params.documentId;
    var finalDocumentIndexingId = params.finalDocumentIndexingId;
    var updateDocument = params.updateDocument;
    var versionDocuments = params.versionDocuments;
    var connectionName = params.connectionName;
    var oldDocument = params.oldDocument;

    var bucketName = cbConnection.getConfig(connectionName).bucket;

    cbBucket = cbConnection.getBucket(connectionName);
    if (!updateDocument.systemHeader.currentVersion) {
        await new Promise((resolve, reject) => {
            cbBucket.insert(updateDocument.systemHeader.versionId, updateDocument, (err, res) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(true);
                }
            });
        });
    }

    return new Promise((resolve, reject) => {
        insertVersionDocuments(versionDocuments, cbBucket, function (err, res) {
            if(err) {
                reject(err);
            } else {
                if (updateDocument.systemHeader.currentVersion) {
                    getCurrentVersion(documentId, oldDocument, bucketName, cbBucket, function(err, curVerDoc) {
                        if (err) {
                            reject(err);
                        } else {
                            var statement = buildUpdateQuery(updateDocument, curVerDoc, bucketName);

                            var query = cbUtil.getN1QlQuery(statement);
                            cbBucket.query(query, function(err, updatedDoc) {
                                if (err) {
                                    reject(err);
                                } else {
                                    var searchStatement = "SELECT ft.*, meta(ft).id as _id FROM `" + bucketName + "` ft WHERE " +
                                        "documentId = '" + updateDocument.documentId + "' AND systemHeader.currentVersion = true;";
                                    query = cbUtil.getN1QlQuery(searchStatement);
                                    cbBucket.query(query, function(err, docs) {
                                        if (err) {
                                            reject(err);
                                        } else {
                                            resolve(true);
                                        }
                                    });
                                }
                            });
                        }
                    });
                } else {
                    resolve(true);
                }

            }
        });
    });
}

function insertVersionDocuments(versionDocuments, cbBucket, callBack){

   if (versionDocuments.length > 0){

       async.eachSeries(versionDocuments, function(version, next) {
           var documentId = getVersionIndexId(version);
           cbBucket.insert(documentId, version, function(err, res) {
               logger.info("Versioning for document: " + version.documentId);
               if(!err) {
                   next();
               } else {
                   logger.error("Error on versioning document" + version.documentId);
                   callBack(err);
               }
           });

       }, function() {
           //end of loop
           callBack(null, true);
       });
   }
   else{
       callBack();
   }


};

function getIndexId(document){
    return document.documentId;
}

function getCurrentVersion(documentId, oldDocument, bucketName, cbBucket, callBack){

        var statement = "SELECT `" + bucketName + "`.* FROM `" + bucketName + "` ";
        statement += " WHERE documentId = $1 AND systemHeader.currentVersion = true"  ;
        var query = cbUtil.getN1QlQuery(statement);
        cbBucket.query(query, [documentId], function(queryErr, results) {
            if (queryErr) {
                callBack(queryErr);
            } else {
                if (results && results.length > 0){
                    callBack(null, results[0]);
                }
                else{
                    callBack(null, oldDocument);
                }

            }
        });

}

function setVersionDocumentId(document) {
    document._id = UtilDocumentId.generateId();
    return document;
}

function getVersionIndexId(document){
    var idxId =document._id;
    delete document._id;
    return idxId;
}

module.exports = {

    update: update,

    getIndexId: getIndexId,

    getLoadVersionQuery: function(versionId){
        var bucketName = cbConnection.getConfig().bucket;

        var query = "SELECT `" + bucketName + "`.* FROM `" + bucketName + "` WHERE ";
        query += " systemHeader.versionId = '" + versionId +  "' ";

        var sortQuery = 'ORDER BY systemHeader.createdDate DESC';

        return {query : query, sortQuery: sortQuery};
    },

    setVersionDocumentId: setVersionDocumentId,

    getVersionIndexId: getVersionIndexId
};
