"use strict";

var SharedConstants  = require('../../shared/constants/SharedConstants');
const AccessManager = require('../../shared/access/AccessManager');
const UtilError = require('../../shared/utils/UtilError');
const UtilDocument = require('../../shared/utils/UtilDocument');
const componentService = require('../database/dataServices/ComponentService');

var q = require("q");
const DataService = require('../database/DataService');

var componentCache;

//Load account control doc so that the user data is already there and only calling the check access as synchronous
var getAccountControlDoc = function(isExecuteCheck, userId) {
    if (isExecuteCheck) {
        const AccountService = require("../database/dataServices/AccountService");

        return AccountService.getAccountControlDocument(userId);
    }

    return Promise.resolve();
};

const findComponent = function(field, userId, options) {
    let deferred = q.defer();

    let isExecuteCheck = options && options.targetOperation === SharedConstants.OPERATION_TYPE_EXECUTE;
    getAccountControlDoc(isExecuteCheck, userId).then(function(accControlDoc) {

        componentService.getComponent(field, userId, options).then(function(docs) {
            let retObj;

            if (docs && docs.length) {
                let doc = docs[0];
                if (doc.documentId === field.documentId  || doc.documentId === field.componentName ||
                    doc.name === field.componentName){
                    if (isExecuteCheck) {

                    let hasAccess = AccessManager.checkAccess(doc, accControlDoc, SharedConstants.OPERATION_TYPE_EXECUTE);
                        if (hasAccess) {
                            retObj = UtilDocument.filterDocumentFields(doc, options.targetFieldNames);
                        }
                    } else {
                        retObj = doc;
                    } 
                }
                
                if (!retObj && isExecuteCheck) {
                    deferred.reject(UtilError.createForbiddenError(docs[0], SharedConstants.OPERATION_TYPE_EXECUTE));
                } else {
                    deferred.resolve(retObj);
                }
            } else {
                deferred.reject("Component documents not found for " + field.componentName);
            }

        }, function(err) {
            const errMsg = 'Error getting component documents:' + err;
            deferred.reject(errMsg);
        });
    });

    return deferred.promise;
};

var getComponent = function(field, userId, options){
    var deferred = q.defer();

    if (!componentCache) {
        componentCache = require('../cache/ComponentCache');
    }

    componentCache.getComponent(field.componentName, null, userId, options).then(function(component) {
        if (component){

            deferred.resolve(component);

        } else {
            var promise;

            if (field.documentId) {
                promise = DataService.findOne(field.documentId, userId, options);
            } else {
                promise = findComponent(field, userId, options);
            }

            promise.then(function (component) {
                if (component){

                    if (!options || (options && options.targetOperation !== SharedConstants.OPERATION_TYPE_EXECUTE)) {
                        componentCache.setComponent(field.componentName, component, userId);
                    }

                    deferred.resolve(component);
                }
                else{
                    deferred.reject(new Error("Component "+ field.componentName + " not found."));
                }
            }, function (err) {
                deferred.reject(err);
            });
        }
    }, function (err) {
        deferred.reject(err);
    });

    return deferred.promise;
};

module.exports = {
    getComponent: getComponent
};
