/* global describe, xdescribe, it, xit, expect, beforeEach, spyOn */
/* eslint max-len:0 */

import Handlebars from 'handlebars';

import {
    buildDisplayName,
    removeUrlProtocol,
    initHandlebars,
    makeArrayFromSubobjectValues
} from '../../app/js/utils.js';

import { Utils, JSContext } from 'poly-js-utils';

describe("building a display name", function() {
    const profile = {};
    describe("No names are in the profile", function() {
        it('should return an empty string if the profile has no name', function() {
            expect(buildDisplayName(profile)).toBe('');
        });
    });

    describe("The profile has a display_name key", function() {
        const profile = {
            first_name: "Farrokh",
            last_name: "Bulsara",
            name: "F. Mercury",
            display_name: "Freddy Mercury",
            foo: "bar"
        };

        it('should return the display name if that key exists', function() {
            expect(buildDisplayName(profile)).toBe('Freddy Mercury');
        });
    });

    describe("The profile has no display_name", function() {
        it('should return first_name and last_name if both of those keys exist', function() {
            const profile = {
                first_name: "Farrokh",
                last_name: "Bulsara",
                name: "F. Mercury",
                foo: "bar"
            };
            expect(buildDisplayName(profile)).toBe('Farrokh Bulsara');
        });

        it("should return the team_name if the team_name exists", function() {
            const profile = {
                team_name: "Team Name",
                foo: "bar"
            };
            expect(buildDisplayName(profile)).toBe("Team Name");
        });

        it('should return the value of the name key instead of first_name or last_name if one of those is missing', function() {
            const last_profile = {
                last_name: "May",
                name: "Brian May",
                foo: "bar"
            };
            expect(buildDisplayName(last_profile)).toBe('Brian May');

            const first_profile = {
                first_name: "Roger",
                name: "Roger Taylor",
                foo: "bar"
            };
            expect(buildDisplayName(first_profile)).toBe('Roger Taylor');

            const name_only_profile = {
                name: "Otis Redding",
                foo: "bar"
            };
            expect(buildDisplayName(name_only_profile)).toBe('Otis Redding');
        });

    });
});

describe("replacing the url protocol", function() {
    it("replaces a url protocol with //", function() {
        expect(removeUrlProtocol("http://foo")).toBe("//foo");
        expect(removeUrlProtocol("ftp://foo.bar.com/whatever?param=thingy&stuff")).toBe("//foo.bar.com/whatever?param=thingy&stuff");
        expect(removeUrlProtocol("https://www.foo.com#anchor")).toBe("//www.foo.com#anchor");
    });
});

describe("making an array from subobject values", function() {
    it("joins a list of subobject values", function() {
        var stooges = [
            {"stooge": "larry"},
            {"stooge": "moe"},
            {"stooge": "curly"}
        ];
        expect(makeArrayFromSubobjectValues(stooges)).toEqual(["larry", "moe", "curly"]);
    });
    it("handles an empty list", function() {
        expect(makeArrayFromSubobjectValues([])).toEqual([]);
    });
});

describe("Handlebars helpers", function() {
    initHandlebars("", {}); // the way our code is set up, the helpers are not registered without this.
    var helpers = Handlebars.helpers;

    describe("contains helper tests", function() {
        beforeEach(function() {
            this.value = "blue";
            this.list = ["red", "blue", "yellow"];
            this.output = "some result";
        });

        it('should be registered', function() {
            expect(helpers.contains).toBeDefined();
        });

        it('should return the output if the value is in the list', function() {
            expect(helpers.contains(this.value, this.list, this.output)).toBe("some result");
        });
        it('should return an empty string if the value is not in the list', function() {
            this.value = "orange";
            expect(helpers.contains(this.value, this.list, this.output)).toBe("");
        });
        it('should return an empty string if the value is undefined', function() {
            this.value = undefined;
            expect(helpers.contains(this.value, this.list, this.output)).toBe("");
        });
        it('should return an empty string if the list is empty', function() {
            this.list = [];
            this.value = "blue";
            expect(helpers.contains(this.value, this.list, this.output)).toBe("");
        });
    });

    describe("tagLabel helper tests", function() {
        beforeEach(function() {
            var testCategories = [
                {
                    field: "languages",
                    options: [
                        {category_tag_value: "113_Arabic", value: "Arabic", label: "Arabic"},
                        {category_tag_value: "113_Italian", value: "Italian", label: "Italian"},
                        {category_tag_value: "113_Japanese", value: "Japanese", label: "Japanese"},
                        {category_tag_value: "113_Korean", value: "Korean", label: "Korean"},
                        {category_tag_value: "113_Mandarin", value: "Mandarin", label: "Mandarin"}
                    ]
                },
                {
                    field: "states",
                    options: [
                        {category_tag_value: "213_AL", value: "AL", label: "Alabama"},
                        {category_tag_value: "213_AK", value: "AK", label: "Alaska"},
                        {category_tag_value: "213_AS", value: "AS", label: "American Samoa"}
                    ]
                }
            ];
            spyOn(JSContext, 'getTagCategories').and.returnValue(testCategories);
        });

        it('should be registered', function() {
            expect(helpers.tagLabel).toBeDefined();
        });

        it('should return the label for a given tag value', function() {
            expect(helpers.tagLabel('113_Korean')).toBe("Korean");
        });

        it('should return a blank string if there is no tag value of that kind', function() {
            expect(helpers.tagLabel('foo')).toBe("");
        });

        it('should return a blank string if the desired tag value is undefined', function() {
            expect(helpers.tagLabel(undefined)).toBe("");
        });

        it('should return a blank string if there are no tag categories at all', function() {
            JSContext.getTagCategories.and.returnValue(undefined);
            expect(helpers.tagLabel('foo')).toBe("");
        });

        // Can duplicate tag values exist in the wild?
    });

    describe("concat helper tests", function() {
        it('should be registered', function() {
            expect(helpers.concat).toBeDefined();
        });

        it('should concatenate strings provided to it', function() {
            expect(helpers.concat("a", "b", "c", "d")).toBe("abcd");
        });

        it('should not change a single argument', function() {
            expect(helpers.concat("all alone")).toBe("all alone");
        });

        it('should return a blank string with no arguments', function() {
            expect(helpers.concat("")).toBe("");
        });

        it('should concatenate lots of types', function() {
            expect(helpers.concat(['a', 'b', 'c'], "def", 4)).toBe("a,b,cdef4");
        });
    });

    describe("ifCond helper tests", function() {
        beforeEach(function() {
            this.options = {
                fn: function() { return "yes"; },
                inverse: function() { return "no"; }
            };
        });

        it('should be registered', function() {
            expect(helpers.ifCond).toBeDefined();
        });

        it('throws an exception if an invalid operator is used', function() {
            expect(function() { helpers.ifCond(1, "==", 2); }).toThrow("Invalid equivalence operator.  Use === or !== instead.");
            expect(function() { helpers.ifCond(1, "!=", 2); }).toThrow("Invalid equivalence operator.  Use === or !== instead.");
        });

        it('should do === correctly', function() {
            expect(helpers.ifCond(1, "===", 1, this.options)).toBe(this.options.fn());
            expect(helpers.ifCond(1, "===", 2, this.options)).toBe(this.options.inverse());
        });

        it('should do !== correctly', function() {
            expect(helpers.ifCond(1, "!==", 1, this.options)).toBe(this.options.inverse());
            expect(helpers.ifCond(1, "!==", 2, this.options)).toBe(this.options.fn());
        });

        it('should do < correctly', function() {
            expect(helpers.ifCond(1, "<", 1, this.options)).toBe(this.options.inverse());
            expect(helpers.ifCond(1, "<", 2, this.options)).toBe(this.options.fn());
        });

        it('should do <= correctly', function() {
            expect(helpers.ifCond(1, "<=", 1, this.options)).toBe(this.options.fn());
            expect(helpers.ifCond(1, "<=", 2, this.options)).toBe(this.options.fn());
            expect(helpers.ifCond(1, "<=", 0, this.options)).toBe(this.options.inverse());
        });

        it('should do > correctly', function() {
            expect(helpers.ifCond(1, ">", 1, this.options)).toBe(this.options.inverse());
            expect(helpers.ifCond(2, ">", 1, this.options)).toBe(this.options.fn());
        });

        it('should do >= correctly', function() {
            expect(helpers.ifCond(1, ">=", 2, this.options)).toBe(this.options.inverse());
            expect(helpers.ifCond(2, ">=", 2, this.options)).toBe(this.options.fn());
            expect(helpers.ifCond(3, ">=", 2, this.options)).toBe(this.options.fn());
        });

        it('should do && correctly', function() {
            expect(helpers.ifCond(true, "&&", true, this.options)).toBe(this.options.fn());
            expect(helpers.ifCond(true, "&&", false, this.options)).toBe(this.options.inverse());
        });

        it('should do || correctly', function() {
            expect(helpers.ifCond(true, "||", false, this.options)).toBe(this.options.fn());
            expect(helpers.ifCond(false, "||", false, this.options)).toBe(this.options.inverse());
        });

        it('should return the inverse option if another operator is used', function() {
            expect(helpers.ifCond(true, "foo", true, this.options)).toBe(this.options.inverse());
        });
    });

    describe("choose helper tests", function() {
        it('should be registered', function() {
            expect(helpers.choose).toBeDefined();
        });

        it('should choose the first choice if it is truthy', function() {
            expect(helpers.choose(1, false)).toBe(1);
        });
        it('should choose the second choice if the first is falsy', function() {
            expect(helpers.choose(false, "yay")).toBe("yay");
        });
        it('should not error out if the first choice is undefined', function() {
            var foo = {some: 'thing'};
            expect(helpers.choose(foo.another, "it's fine")).toBe("it's fine");
        });
    });

    describe("agentName helper tests", function() {
        it('should be registered', function() {
            expect(helpers.agentName).toBeDefined();
        });

        // We can't currently spy on this because of the way it's exported and used -
        // if I did import * as utils from '../../app/js/utils.js'; then the spy wants
        // it to be called like utils.buildDisplayName from inside agentName.
        xit('should call buildDisplayName', function() {
            spyOn(buildDisplayName);
            expect(helpers.agentName({ name: "Aretha Franklin" })).toBe("Aretha Franklin");
            expect(buildDisplayName).toHaveBeenCalled();
        });
    });

    describe("phoneHref helper tests", function() {
        it('should be registered', function() {
            expect(helpers.phoneHref).toBeDefined();
        });

        it('should return undefined if there is no phone number in the profile', function() {
            expect(helpers.phoneHref({})).toBeUndefined();
            expect(helpers.phoneHref({phones: {}})).toBeUndefined();
        });

        it('should call stripNonDigits if there is a phone number in the profile', function() {
            spyOn(Utils, 'stripNonDigits').and.callThrough();
            expect(helpers.phoneHref({phones: {phone: "+1 44 (32) 555-1211"}})).toBe('144325551211');
            expect(Utils.stripNonDigits).toHaveBeenCalled();
        });
    });
});
