import {
    addItemToDict,
    addItemToTree,
    getItemByIdFromDict,
    getItemSiblingsFromTree,
    getRootNodeFromTree,
    resolveItemsMaxBranchLengthInTree,
    resolveItemPath,
    getItemsChildrenFromTree
} from './TreeItemMap';

const item1 = {
    id: 1,
    name: 'ROOT',
    parentId: 0
};
const item2 = {
    id: 2,
    name: 'LEVEL 2 ITEM 1',
    parentId: 1
};
const item3 = {
    id: 3,
    name: 'LEVEL 2 ITEM 2',
    parentId: 1
};
const item4 = {
    id: 4,
    name: 'LEVEL 3 ITEM 1',
    parentId: 2
};
const item5 = {
    id: 5,
    name: 'LEVEL 3 ITEM 2',
    parentId: 3
};
const item6 = {
    id: 6,
    name: 'LEVEL 4 ITEM 1',
    parentId: 5
};
const outOfStructuresItem = {
    id: 999,
    name: 'OUT OF STRUCTURES',
    parentId: 777
};

const data = [item1, item2, item3, item4, item5, item6];

const dict = {
    1: item1,
    2: item2,
    3: item3,
    4: item4,
    5: item5,
    6: item6
};

const tree = {
    0: [item1],
    1: [item2, item3],
    2: [item4],
    3: [item5],
    5: [item6]
};

describe('TreeItemMap', () => {
    it('should not allow cyclic structures', callback => {
        const cyclicData = [
            {
                id: 1,
                name: 1,
                parentId: 2
            },
            {
                id: 2,
                name: 2,
                parentId: 1
            }
        ];

        try {
            const dict = cyclicData.reduce((dict, item) => addItemToDict(item, dict), {});
            cyclicData.reduce((tree, item) => addItemToTree(item, tree, dict), {});

            callback('cyclic structure not caught');
        } catch (e) {
            callback();
        }
    });

    it('should map proper flat dictionary from input data', () => {
        let actualDict = data.reduce((dict, item) => addItemToDict(item, dict), {});

        expect(actualDict).toEqual(dict);
    });

    it('should map proper tree structures from input data and dict', () => {
        let actualTree = data.reduce((tree, item) => addItemToTree(item, tree, dict), {});

        expect(actualTree).toEqual(tree);
    });

    describe('getItemByIdFromDict', () => {
        it('should return undefined if id not found', () => {
            const actualItem = getItemByIdFromDict(999, dict);

            expect(actualItem).toBe(undefined);
        });

        it('should resolve item by id', () => {
            const {id} = item1;
            const actualItem = getItemByIdFromDict(id, dict);

            expect(actualItem).toEqual(item1);
        });
    });

    describe('getItemSiblingsFromTree', () => {
        it('should return undefined if id not found', () => {
            const siblings = getItemSiblingsFromTree(999, {});

            expect(siblings).toBe(undefined);
        });

        it('should resolve array of items with same parentId as passed', () => {
            const {parentId} = item2;
            const siblings = getItemSiblingsFromTree(item2, tree);

            expect(siblings).toEqual(tree[parentId]);
        });
    });

    describe('getItemsChildrenFromTree', () => {
        it('should return undefined if id not found', () => {
            const children = getItemsChildrenFromTree(outOfStructuresItem, tree);

            expect(children).toBe(undefined);
        });

        it('should resolve array of items with same parentId', () => {
            const {id} = item1;
            const children = getItemsChildrenFromTree(item1, tree);

            expect(children).toEqual(tree[id]);
        });
    });

    describe('getRootNodeFromTree', () => {
        it('should return root node', () => {
            const rootNode = getRootNodeFromTree(tree);

            expect(rootNode).toEqual(tree[0]);
        });
    });

    describe('resolveItemsMaxBranchLengthInTree', () => {
        it('should return 0 for items, that not in map', () => {
            const length = resolveItemsMaxBranchLengthInTree(outOfStructuresItem, tree);

            expect(length).toBe(0);
        });

        it('should return 0 for items without children', () => {
            const length = resolveItemsMaxBranchLengthInTree(item4, tree);

            expect(length).toBe(0);
        });

        it('should return length of longest chain of children', () => {
            const length = resolveItemsMaxBranchLengthInTree(item1, tree);
            const path = resolveItemPath(item6, dict);

            expect(length).toBe(path.length - 1);
        });
    });

    describe('resolveItemPath', () => {
        it('should return nothing if id not found', () => {
            const path = resolveItemPath(outOfStructuresItem, dict);

            expect(path).toBe(undefined);
        });

        it("should get array of item ids leading from passed item to it's root", () => {
            const expectedPath = [1, 2, 4];
            const path = resolveItemPath(item4, dict);

            expect(path).toEqual(expectedPath);
        });
    });
});
