/*****************************************************************************
 * Open MCT Web, Copyright (c) 2014-2015, United States Government
 * as represented by the Administrator of the National Aeronautics and Space
 * Administration. All rights reserved.
 *
 * Open MCT Web is licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 * Open MCT Web includes source code licensed under additional open source
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 * this source code distribution or the Licensing information page available
 * at runtime from the About dialog for additional information.
 *****************************************************************************/
/*global define,describe,it,expect,beforeEach,jasmine,Promise,waitsFor,spyOn*/

/**
 *  SearchSpec. Created by shale on 07/31/2015.
 */
define([
    "../../src/services/SearchAggregator"
], function (SearchAggregator) {
    "use strict";

    describe("SearchAggregator", function () {
        var $q,
            objectService,
            providers,
            aggregator;

        beforeEach(function () {
            $q = jasmine.createSpyObj(
                '$q',
                ['all']
            );
            $q.all.andReturn(Promise.resolve([]));
            objectService = jasmine.createSpyObj(
                'objectService',
                ['getObjects']
            );
            providers = [];
            aggregator = new SearchAggregator($q, objectService, providers);
        });

        it("has a fudge factor", function () {
            expect(aggregator.FUDGE_FACTOR).toBe(5);
        });

        it("has default max results", function () {
            expect(aggregator.DEFAULT_MAX_RESULTS).toBe(100);
        });

        it("can order model results by score", function () {
            var modelResults = {
                    hits: [
                        {score: 1},
                        {score: 23},
                        {score: 11}
                    ]
                },
                sorted = aggregator.orderByScore(modelResults);

            expect(sorted.hits).toEqual([
                {score: 23},
                {score: 11},
                {score: 1}
            ]);
        });

        it('filters results without a function', function () {
            var modelResults = {
                    hits: [
                        {thing: 1},
                        {thing: 2}
                    ],
                    total: 2
                },
                filtered = aggregator.applyFilter(modelResults);

            expect(filtered.hits).toEqual([
                {thing: 1},
                {thing: 2}
            ]);

            expect(filtered.total).toBe(2);
        });

        it('filters results with a function', function () {
            var modelResults = {
                    hits: [
                        {model: {thing: 1}},
                        {model: {thing: 2}},
                        {model: {thing: 3}}
                    ],
                    total: 3
                },
                filterFunc = function (model) {
                    return model.thing < 2;
                },
                filtered = aggregator.applyFilter(modelResults, filterFunc);

            expect(filtered.hits).toEqual([
                {model: {thing: 1}}
            ]);
            expect(filtered.total).toBe(1);
        });

        it('can remove duplicates', function () {
            var modelResults = {
                    hits: [
                        {id: 15},
                        {id: 23},
                        {id: 14},
                        {id: 23}
                    ],
                    total: 4
                },
                deduped = aggregator.removeDuplicates(modelResults);

            expect(deduped.hits).toEqual([
                {id: 15},
                {id: 23},
                {id: 14}
            ]);
            expect(deduped.total).toBe(3);
        });

        it('can convert model results to object results', function () {
            var modelResults = {
                    hits: [
                        {id: 123, score: 5},
                        {id: 234, score: 1}
                    ],
                    total: 2
                },
                objects = {
                    123: '123-object-hey',
                    234: '234-object-hello'
                },
                promiseChainComplete = false;

            objectService.getObjects.andReturn(Promise.resolve(objects));

            aggregator
                .asObjectResults(modelResults)
                .then(function (objectResults) {
                    expect(objectResults).toEqual({
                        hits: [
                            {id: 123, score: 5, object: '123-object-hey'},
                            {id: 234, score: 1, object: '234-object-hello'}
                        ],
                        total: 2
                    });
                })
                .then(function () {
                    promiseChainComplete = true;
                });

            waitsFor(function () {
                return promiseChainComplete;
            });
        });

        it('can send queries to providers', function () {
            var provider = jasmine.createSpyObj(
                    'provider',
                    ['query']
                );
            provider.query.andReturn('i prooomise!');
            providers.push(provider);

            aggregator.query('find me', 123, 'filter');
            expect(provider.query)
                .toHaveBeenCalledWith(
                    'find me',
                    123 * aggregator.FUDGE_FACTOR
                );
            expect($q.all).toHaveBeenCalledWith(['i prooomise!']);
        });

        it('supplies max results when none is provided', function () {
            var provider = jasmine.createSpyObj(
                    'provider',
                    ['query']
                );
            providers.push(provider);
            aggregator.query('find me');
            expect(provider.query).toHaveBeenCalledWith(
                'find me',
                aggregator.DEFAULT_MAX_RESULTS * aggregator.FUDGE_FACTOR
            );
        });

        it('can combine responses from multiple providers', function () {
            var providerResponses = [
                    {
                        hits: [
                            'oneHit',
                            'twoHit'
                        ],
                        total: 2
                    },
                    {
                        hits: [
                            'redHit',
                            'blueHit',
                            'by',
                            'Pete'
                        ],
                        total: 4
                    }
                ],
                promiseChainResolved = false;

            $q.all.andReturn(Promise.resolve(providerResponses));
            spyOn(aggregator, 'orderByScore').andReturn('orderedByScore!');
            spyOn(aggregator, 'applyFilter').andReturn('filterApplied!');
            spyOn(aggregator, 'removeDuplicates')
                .andReturn('duplicatesRemoved!');
            spyOn(aggregator, 'asObjectResults').andReturn('objectResults');

            aggregator
                .query('something', 10, 'filter')
                .then(function (objectResults) {
                    expect(aggregator.orderByScore).toHaveBeenCalledWith({
                        hits: [
                            'oneHit',
                            'twoHit',
                            'redHit',
                            'blueHit',
                            'by',
                            'Pete'
                        ],
                        total: 6
                    });
                    expect(aggregator.applyFilter)
                        .toHaveBeenCalledWith('orderedByScore!', 'filter');
                    expect(aggregator.removeDuplicates)
                        .toHaveBeenCalledWith('filterApplied!');
                    expect(aggregator.asObjectResults)
                        .toHaveBeenCalledWith('duplicatesRemoved!');

                    expect(objectResults).toBe('objectResults');
                })
                .then(function () {
                    promiseChainResolved = true;
                });

            waitsFor(function () {
                return promiseChainResolved;
            });
        });

    });
});
