'use strict';

var q = require("q");
var MongoConnection = require('./MongoConnection');
var lodashCollection = require('lodash/collection');
var lodashLang = require('lodash/lang');
var logger = require('../../../utils/Logger').loggers.get('db');

function constructQuery(params){
    var values = params.query;
    var query = {};
    lodashCollection.forEach(values, function(value, key) {
        if (lodashLang.isArray(value)){
            query[key] =   { $in: value };
        }
        else{
            query[key] =  value;
        }
    });

    var finalQuery = query;
    if (!params.includeAccountSecurity){
       finalQuery = {
            $and: [
                query,
                {
                    $or: [{"systemHeader.systemType":{"$ne": SharedConstants.SYSTEM_TYPE_ACCOUNT_SECURITY}}]
                }
            ]
        };
    }

    if (params.useCurrentVersion){
        finalQuery["systemHeader.currentVersion"] = true;
    }

    return finalQuery;
}

function getAccessKeysQuery(accessKeys, options) {
    var result;

    // if the excludeMissingKeys option is specified, documents with no keys won't be included in the results. This allows querying
    // for documents that must have the key. It must be combined with the alternativeKey option, otherwise the query won't include
    // anything
    if (options && options.excludeMissingKeys) {
        result = [];
    } else {
        result = [
            {"systemHeader.keyIds": {$exists: false}},
            {"systemHeader.keyIds": {$size: 0}}
        ];
    }


    result.push({"systemHeader.keyIds": {$in: accessKeys}});

    return result;
}

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

    MongoConnection.getCollection(function (err, collection) {
        if (err) {
            if (callBack) callBack(err);
            deferred.reject(err);
        } else {
            var query = constructQuery(params);
            collection.findOne(query,
                function (queryErr, doc) {
                    if (queryErr) {
                        if (callBack) callBack(queryErr); 
                        deferred.reject(queryErr);
                    } else {
                        if (callBack) callBack(null, doc);
                        deferred.resolve(doc);
                    }
                });
        }
    }, connectionName);

    return deferred.promise;
}

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

    MongoConnection.getCollection(function (err, collection) {
        if (err) {
            if (callBack) callBack(err);
            deferred.reject(err);
        } else {
            var query = constructQuery(params);
            collection.find(query).toArray(
                function (queryErr, results) {
                    if (queryErr) {
                        if (callBack) callBack(queryErr);
                        deferred.reject(queryErr);
                    }else {
                        if (callBack) callBack(null, results);
                        deferred.resolve(results);
                    }
                });
        }
    }, connectionName);

    return deferred.promise;
}

function findAll(query, keys, sortQuery, allVersions, options) {
    var deferred = q.defer();
    var connectionName = options.connectionName;
    var countOnly = options.countOnly;
    var pageNumber = options.pageNumber;
    var numberPerPage = options.numberPerPage;
    const raw = options.raw;
    if (!numberPerPage && options.nPerPage){
        logger.warn("DEPRECATED: Please use numberPerPage parameter instead of nPerPage");
        numberPerPage = options.nPerPage;
    }
    var errMsg;
    MongoConnection.getCollection(async function (err, collection) {
        if (err) {
            deferred.reject(err);
        } else {

            var accessKeysQuery = getAccessKeysQuery(keys, options);

            var finalQuery = {
                $and: [
                    query,
                    {
                        $or: accessKeysQuery
                    }
                ]
            };

            if (!allVersions) {
                finalQuery.$and.push({"systemHeader.currentVersion": true});
            }

            //Filter out deleted documents
            var unDeletedQuery = {"$or": [
                {"systemHeader.deleted": false},
                {"systemHeader.deleted": {$exists: false}}
            ]};
            finalQuery["$and"].push(unDeletedQuery);

            //account Security document should be removed from result
            var removeAccountSecurity = {"$or": [
                    {"systemHeader.systemType":{"$ne": SharedConstants.SYSTEM_TYPE_ACCOUNT_SECURITY}}
                ]};
            finalQuery["$and"].push(removeAccountSecurity);

            logger.info("Final query to find documents: " + JSON.stringify(finalQuery));

            let mongoCursor;
            if (raw){
               mongoCursor = collection.find(finalQuery, {raw: true});
            } else {
               mongoCursor = collection.find(finalQuery);
            }


            if (countOnly) {
                mongoCursor.count().then(function (integerDocCount) {
                    deferred.resolve(integerDocCount);
                });

                return;
            }

            if (sortQuery) {
                mongoCursor = mongoCursor.sort(sortQuery);
                logger.info("results sorted by: " + JSON.stringify(sortQuery));
            }

            if (pageNumber && numberPerPage) {
                mongoCursor.skip(pageNumber > 0 ? ((pageNumber - 1) * numberPerPage) : 0).limit(numberPerPage);
            }
          if (raw){
              try {
                const data = [];
                for await (const doc of mongoCursor) {
                  data.push(doc);
                }
                deferred.resolve(data);
              } catch (err) {
                deferred.reject(err);
              }
          }else {
              mongoCursor.toArray(
                function(err, data) {
                  if (err) {
                    deferred.reject(err);
                  } else {
                    deferred.resolve(data);
                  }
                }
              );
          }
        }
    }, connectionName);

    return deferred.promise;

}

function findVersions(ids, keys, sortQuery, allVersions, options) {
    const query = { "systemHeader.versionId": { "$in": ids } };
    return findAll(query, keys, sortQuery, allVersions, options);
}

function findByQuery(query, connectionName){
    var deferred = q.defer();
    var errMsg;
    MongoConnection.getCollection(function (err, collection) {
        if (err) {
            deferred.reject(err);
        } else {
            collection.find(query).toArray(
                function (queryErr, results) {
                    if (queryErr) {
                        deferred.reject(queryErr);
                    }else {
                        deferred.resolve(results);
                    }
                });
        }
    }, connectionName);

    return deferred.promise;
}


const getFindTemplateQuery = (options) => {

  return {
    "documentId": options.documentId,
    "systemHeader.systemType": SharedConstants.SYSTEM_TYPE_TEMPLATE,
    "systemHeader.currentVersion": true
  };

};


const getMultipleFindTemplateQuery = (options) => {

  return {
    "documentId": options.documentId,
    "systemHeader.currentVersion": true
  };

};

module.exports = {
    findOne: findOne,

    find: find,

    findAll: findAll,

    findVersions: findVersions,

    findByQuery: findByQuery,

    getFindTemplateQuery: getFindTemplateQuery,

    getMultipleFindTemplateQuery: getMultipleFindTemplateQuery
};
