'use strict';

var q = require('q');
var logger = require('../../../utils/Logger').loggers.get('db');
var cbConnection = require('./CouchbaseConnection');
var lodashCollection = require('lodash/collection');
var lodashLang = require('lodash/lang');
var cbUtil = require('./CouchbaseUtil');

var cbBucket;

function constructQuery(params, bucketName){
	var query = {};

    var statement = "SELECT `" + bucketName + "`.* FROM `" + bucketName + "` ";
	var whereCond = "";
	var values = [];
	var index = 0;

    lodashCollection.forEach(params.query, function(value, key) {
		var field;
        values.push(value);
		if (lodashLang.isArray(value)){
            field =   key + " IN $" + values.length;
        }
        else{
            field =  key + " = $" + values.length;
        }
        if (whereCond){
            whereCond += "  AND " + field +  " ";
		}
		else{
            whereCond += field + " ";
		}
    });

    if (!params.includeAccountSecurity){
        if (! lodashLang.isEmpty(whereCond)){
            whereCond += "  AND systemHeader.systemType != '" + SharedConstants.SYSTEM_TYPE_ACCOUNT_SECURITY +  "' ";
        }
        else{
            whereCond += " systemHeader.systemType != '" + SharedConstants.SYSTEM_TYPE_ACCOUNT_SECURITY +  "' ";
        }
    }

    if (params.useCurrentVersion){
        whereCond += " AND systemHeader.currentVersion = true ";		
    }

    query.statement = statement;
	if (! lodashLang.isEmpty(whereCond)){
        query.statement = statement + " WHERE "  + whereCond;
	}
    query.values = values;

    return query;
}

function findOne(params, connectionName, callBack){
    var deferred = q.defer();

    find(params, connectionName, function(queryErr, results) {
        if (queryErr) {
            if (callBack) callBack(queryErr);
            deferred.reject(queryErr);
        } else {
            var doc;
            if (results.length !== 0) {
                doc = results[0];
            }

            if (callBack) callBack(null, doc);
            deferred.resolve(doc);
        }
    });

    return deferred.promise;
}

function find(params, connectionName, callBack){
    var deferred = q.defer();
    logger.info('Invoke findOne function in CouchbaseDataService');

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

    var qry = constructQuery(params, bucketName);

    var query = cbUtil.getN1QlQuery(qry.statement);
    cbBucket = cbConnection.getBucket(connectionName);
    cbBucket.query(query, qry.values, function(queryErr, results) {
        if (queryErr) {
            if (callBack) callBack(queryErr);
            deferred.reject(queryErr);
        } else {
            if (callBack) callBack(null, results);
            deferred.resolve(results);
        }
    });

    return deferred.promise;
}

function findAll(query, keys, sortQuery, allVersions, options){
    logger.info('Invoke findAll function in CouchbaseDataService');
    var deferred = q.defer();
    var connectionName = options.connectionName;
    var countOnly = options.countOnly;
    var pageNumber = options.pageNumber;
    var numberPerPage = options.numberPerPage;
    if (!numberPerPage && options.nPerPage){
        logger.warn("DEPRECATED: Please use numberPerPage parameter instead of nPerPage");
        numberPerPage = options.nPerPage;
    }

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

    // it is valid to pass through no query to get all documents for a user,
    // but if a query is passed in it needs to be a string so reject the query if it's defined but not a string. This will help
    // diagnose issues if a mongo query is passed to the Couchbase findAll function
    if (query && typeof query !== "string") {
        var errorMsg = "Query is not a string in findAll: " + JSON.stringify(query);
        deferred.reject(new Error(errorMsg));
    } else {

		var statement = query;
		var params = [];

		if (options && options.params) {
			params = options.params;
		}

		//ex. SELECT bucketName.* FROM bucketName WHERE ........
		if (!query) {
			statement = "SELECT `" + bucketName + "`.* FROM `" + bucketName + "` WHERE ";
		} else {
			statement += " AND ";
		}

		var keyIdsStatement = "";
		if (keys && keys.length > 0) {
			params.push(keys);
			keyIdsStatement = " ANY keyId IN systemHeader.keyIds SATISFIES keyId IN $" + params.length + " END ";
		}

		if (options && options.excludeMissingKeys && keyIdsStatement) {
			statement += " (" + keyIdsStatement;
		} else {
			//add access query check
			statement += " (" +
				"systemHeader.keyIds IS NULL " +
				"OR systemHeader.keyIds = [] ";

			if (keyIdsStatement !== "") {
				statement += " OR " + keyIdsStatement;
			}
		}

		statement += ") ";

		if (!allVersions) {
			statement += " AND systemHeader.currentVersion = true ";
		}

		statement += " AND (systemHeader.deleted = false OR systemHeader.deleted IS MISSING) ";

        //account Security document should be removed from result
        statement += " AND systemHeader.systemType != '" + SharedConstants.SYSTEM_TYPE_ACCOUNT_SECURITY + "' ";

        //must be couchbase sort query
		if (sortQuery) {
			statement += " " + sortQuery;
		}

		logger.info('Executing FindAll Query: ' + statement + '    /* Parameters: ' + JSON.stringify(params) + ' */');

		var n1ql = cbUtil.getN1QlQuery(statement);
		cbBucket = cbConnection.getBucket(connectionName);
		cbBucket.query(n1ql, params, function (err, results) {
			if (err) {
				deferred.reject(err);
			} else {
				deferred.resolve(results);
			}
		});
    }

    return deferred.promise;

}


const getSelect = (options) => {
  let connectionName = options ? options.connectionName : null;
  const bucketName = cbConnection.getConfig(connectionName).bucket;

  return "SELECT `" + bucketName + "`.* FROM `" + bucketName + "` " ;

};


function findVersions(ids, keys, sortQuery, allVersions, options){

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

    let statement = "SELECT `" + bucketName + "`.* FROM `" + bucketName + "` WHERE ";
    statement += "systemHeader.versionId in " +  JSON.stringify(ids);

    return findAll(statement, keys, sortQuery, allVersions, options);
}


const getFindTemplateQuery = (options) =>{

  let statement = getSelect(options) +
    ' WHERE systemHeader.systemType="' + SharedConstants.SYSTEM_TYPE_TEMPLATE + '"' +
    ' AND documentId="' + options.documentId  +'" AND systemHeader.currentVersion = true ' ;

  return statement;

};


const getMultipleFindTemplateQuery = (options) =>{

  let statement = getSelect(options) +
    ' WHERE documentId="' + options.documentId  +'" AND systemHeader.currentVersion = true ' ;

  return statement;

};


module.exports = {
    findOne: findOne,

    find: find,

    findAll: findAll,

    findVersions: findVersions,

    getFindTemplateQuery: getFindTemplateQuery,

    getMultipleFindTemplateQuery: getMultipleFindTemplateQuery
};
