// (C) Copyright 2020 Hewlett-Packard Enterprise Company, L.P.
define(['hp/services/REST', 'hp/services/IndexFilter',
        'hp/services/IndexThrottler', 'hp/services/Log', 'jquery'],
function(REST, IndexFilter, IndexThrottler, log) { "use strict";

    var IndexService = (function() {

        var URL_PREFIXES = {
            1: '/index/rest/index',
            // 2 is required for richer filtering
            2: '/rest/index',
            // 3 is required to support referenceUri for trees, associations and resource
            3: '/rest/index'
        };

        var URI_PROP_NAME = '_uri';

        function IndexService() {

            var version = 1;
            var urlPrefix = URL_PREFIXES[1];

            // TODO We are not able to recall why we need this method. Should
            // remove this eventually.
            function convertResource(resource) {
                if (resource.hasOwnProperty('displayName')) {
                    resource.name = resource.displayName;
                    resource.status = resource.healthStatus;
                    resource.state = resource.resourceState;
                    delete resource.displayName;
                    delete resource.healthStatus;
                    delete resource.resourceState;
                }
            }

            function convertIndexResources(indexResources) {
                if (indexResources.hasOwnProperty('resources')) {
                    indexResources.members = indexResources.resources;
                    delete indexResources.resources;
                    // convert properties
                    $.each(indexResources.members, function (index, resource) {
                        convertResource(resource);
                    });

                    indexResources.count = indexResources.totalIndexResources;
                    delete indexResources.totalIndexResources;
                }
                for (var i=indexResources.members.length; i--;) {
                    indexResources.members[i].__replyTimestamp = indexResources.__replyTimestamp;
                    // if this resource is a task in "Running" state, set the flag to use later
                    // to automatically update parent task progress when there are no changes from the backend that
                    // would trigger update
                    if (indexResources.members[i].hasOwnProperty('category') &&
                        (indexResources.members[i].category == "tasks") &&
                        ('running' === indexResources.members[i].state.toLowerCase() )){
                       indexResources.members[i].__updatedTimestamp =
                              indexResources.__replyTimestamp;
                    }
                }
            }

            function indexResourcesHandlers(handlers) {
                return {
                    success: function (data, status, xhr) {
                        data.__replyTimestamp = new Date(xhr.getResponseHeader('Date'));
                        convertIndexResources(data);
                        handlers.success(data);
                    },
                    error: handlers.error};
            }

            function convertTree(tree) {
                convertResource(tree.resource);
                $.each(tree.parents, function (assoc, parents) {
                    $.each(parents, function (index, parent) {
                        convertTree(parent);
                    });
                });
                $.each(tree.children, function (assoc, children) {
                    $.each(children, function (index, child) {
                        convertTree(child);
                    });
                });
            }

            function convertTrees(trees) {
                if (trees.hasOwnProperty('trees')) {
                    $.each(trees.trees, function (index, tree) {
                        convertTree(tree);
                    });
                } else {
                    convertTree(trees);
                }
            }

            function treesHandlers(handlers) {
                return {
                    success: function (data) {
                        convertTrees(data);
                        handlers.success(data);
                    },
                    error: handlers.error};
            }

            function serializeCategory(category) {
                var result;
                if ('array' === $.type(category)) {
                    result = $.map(category, function (cat, index) {
                        return 'category=' + cat;
                    }).join('&');
                } else {
                    result = 'category=' + category;
                }
                return result;
            }

            function hasUriArrayInFilter(filter) {
                return filter.data && filter.data.properties &&
                    filter.data.properties[URI_PROP_NAME] &&
                    $.isArray(filter.data.properties[URI_PROP_NAME]);
            }

            // TODO move this out into a separate module that both share, at some point.
            // Called by IndexThrottler. Not intend for public use.
            this.convertIndexResources = convertIndexResources;

            // TODO make a new method here that will take the new argument (associationNames)
            this.getIndexResources = function (category, start, count, handlers) {
                var uri = urlPrefix + '/resources?' +
                    serializeCategory(category) +
                    '&start=' + start + '&count=' + count + '&sort=name:asc';
                REST.getURI(uri, indexResourcesHandlers(handlers), {apiVersion: version});
            };

            this.getFilteredIndexResources = function (filter, handlers) {
                var params = filter.toRESTParameters(version);
                var uri = urlPrefix + '/resources?' + params.join('&');
                REST.getURI(uri, indexResourcesHandlers(handlers), {apiVersion: version});
            };

            this.getAssociationTree = function (uriArg, depth, handlers) {
                var uri = urlPrefix + '/associationtree?rootUri=' + uriArg +
                    '&depth=' + depth;
                REST.getURI(uri, handlers, {apiVersion: version});
            };

            this.searchIndexResources = function (filter, start, count, handlers) {
                if ('string' === typeof(filter)) {
                    var realFilter = new IndexFilter();
                    realFilter.setUserQuery(filter);
                    filter = realFilter;
                }
                var query = '';
                if (filter.data.terms) {
                    query = filter.data.terms.join(' ');
                }
                var uri = urlPrefix + '/resources?userQuery=' +
                    encodeURIComponent(query) +
                    '&start=' + start + '&count=' + count;
                REST.getURI(uri, indexResourcesHandlers(handlers), {apiVersion: version});
            };

            this.suggestions = function(category, query, start, count, handlers) {
                var uri = urlPrefix + '/' +
                    (1 === version ? 'suggestions' : 'search-suggestions') + '?';
                if (category) {
                    uri += serializeCategory(category) + '&';
                }
                uri += 'userQuery=' + encodeURIComponent(query) +
                    '&start=' + start + '&count=' + count;
                REST.getURI(uri, handlers, {apiVersion: version});
            };

            this.getAssociationTrees = function (category, depth, associationNames,
                start, count, handlers) {
                var uri = urlPrefix + '/associationtrees?'+
                    serializeCategory(category) +
                    '&depth=' + depth+
                    '&associationNames='+associationNames+
                    '&start=' + start+
                    '&count=' + count;
                REST.getURI(uri, handlers, {apiVersion: version});
            };

            this.getIndexForResource = function (resourceUri, handlers) {
                var uri = urlPrefix + '/resources?start=0&count=1&filter=_uri:' +
                    resourceUri;
                REST.getURI(uri, handlers, {apiVersion: version});
            };

            /**
             * Retrieves the associations from the index service
             * @param {string} startObjUri - The starting object uri (optionally null)
             * to look at when getting associations.
             * @param {string} endObjUri - The ending object uri (optionally null)
             * to look at when getting associations.
             * @param {string} associationName - One of a set association names
             * (optionally null).
             * @param {string} relationship - Boolean value (optionally null).
             * @param {object} handlers - Success and Error handlers.
             */
            this.getAssociations = function (startObjUri, endObjUri, associationName,
                relationship, handlers) {
                var params = [];

                if (startObjUri) {
                    if (version === 1) {
                        params.push("startObjUri=" + startObjUri);
                    } else {
                        params.push("parentUri=" + startObjUri);
                    }
                }
                if (endObjUri) {
                    if (version === 1) {
                        params.push("endObjUri=" + endObjUri);
                    } else {
                        params.push("childUri=" + endObjUri);
                    }
                }
                if (associationName) {
                    if (version === 1) {
                        params.push("associationName=" + associationName);
                    } else {
                        params.push("name=" + associationName);
                    }
                }
                if (relationship) {
                    params.push("hasARelationship=" + relationship);
                }

                var uri = urlPrefix + '/associations?' + params.join('&');
                REST.getURI(uri, handlers, {apiVersion: version});
            };

            this.getFilteredAssociations = function (filter, handlers) {
                var uri = version === 1 ? urlPrefix + '/associations?' : urlPrefix + '/associations/resources?';
                var params = filter.toRESTParameters(version);

                uri += params.join('&');
                REST.getURI(uri, handlers, {apiVersion: version});
            };

            function buildTreesParams(options, params) {
                if (options.filter) {
                    // Tree does not support all filter parameters as Resource does in V2.
                    // So if the current version is 2, then use version 1 logic.
                    params = params.concat(options.filter.toRESTParameters(version === 2 ? 1 : version));
                }
                if (options.childDepth) {
                    params.push("childDepth=" + options.childDepth);
                }
                if (options.parentDepth) {
                    params.push("parentDepth=" + options.parentDepth);
                }
                if (options.sort) {
                    params.push("sort=" + options.sort);
                }
                if (false === options.details) {
                    params.push("details=false");
                }
                if(options.treeLimit) {
                    params.push("treeLimit=" + options.treeLimit);
                }

                return params;
            }

            /**
             * Retrieves the parent and children associations from the index service
             * @param {object} Options object telling parameters to set in REST call.
             * Not used if not set.
             *   Supported params (Pass either uri or catagory, but not both)
             *
             *     uri = Uri of item to retrieve. Retrieves single item specified by Uri.
             *     filter = IndexFilter holding other info including start, count, userQuery and category.
             *     childDepth = Descent depth for following child associations. (Always valid)
             *     parentDepth = Descent depth for following parent associations. (Always valid)
             *     handlers = object w/ success/error handlers (Always)
             */
            this.getParentAndChildrenAssociations = function (options) {
                var params = [];
                var handlers = null;
                var uri = "";

                if (arguments.length === 1) {
                    // New version, parsing an Option object
                    uri = ((options.uri) ? options.uri : "");
                    params = buildTreesParams(options, params);
                    if (options.handlers) {
                        handlers = options.handlers;
                    }
                } else {
                    // Old version which took multiple arguments
                    uri = arguments[0];
                    var childDepth = arguments[1];
                    var parentDepth = arguments[2];
                    handlers = arguments[3];

                    if (childDepth) {
                        params.push("childDepth=" + childDepth);
                    }
                    if (parentDepth) {
                        params.push("parentDepth=" + parentDepth);
                    }
                }
                if (version <= 2) {
                    // trees doesn't align with urlPrefix
                    REST.getURI('/index/rest/trees' + uri + "?" + params.join('&'),
                        treesHandlers(handlers), {apiVersion: version});
                } else {
                    // version 3 and higher
                    REST.getURI(urlPrefix + '/trees' + uri + '?' + params.join('&'),
                        handlers, {apiVersion: version});
                }
            };

            this.getMap = function (options) {
                var uri = '/rest/index/trees/aggregated' +
                options.uris[0] + '?';
                var params = [];
                if (version < 3) {
                    log.warn('Cannot use aggregated Map with API version less than 3');
                } else {
                    if (options.hasOwnProperty('childLimit')) {
                        params.push('childLimit=' + options.childLimit);
                    }

                    uri += params.join('&');
                    REST.getURI(uri, options.handlers, {apiVersion: version});
                }
            };

            this.checkForExistence = function (filter, handlers) {
                var throttler;
                if (hasUriArrayInFilter(filter)) {
                    throttler = new IndexThrottler();
                    throttler.init({
                        urlPrefix: urlPrefix,
                        version: version,
                        filter: filter,
                        handlers: handlers
                     });
                     throttler.getFilteredUris();
                } else {
                    log.warn('The uri property is not set before checking for its existence.');
                }
            };

            this.init = function (versionArg) {
                if (URL_PREFIXES.hasOwnProperty(versionArg)) {
                    version = versionArg;
                    urlPrefix = URL_PREFIXES[version];
                }
            };

            this.version = function () {
                return version;
            };
        }
        return new IndexService();
    }());

    return IndexService;
});
