"use strict";

import {expect} from "chai"
import {Pathfinder} from "../../../source/data/pathfinder.mjs";

describe('Pathfinder', function () {

    let convertMapResult = function (r) {
        if (r instanceof Map) {
            r = Object.fromEntries(r);
            if (r instanceof Array) {
                r = r.map((e) => {
                    return convertMapResult(e);
                })
            } else if (typeof r === "object") {
                for (const [k, o] of Object.entries(r)) {
                    r[k] = convertMapResult(o);
                }
            }
        }

        return r;
    }


    describe('with Wildcard and Iterations', function () {
        let pf, obj;

        beforeEach(function () {
            obj = {
                a: [
                    {
                        b: [
                            {
                                c: "1-1"
                            },
                            {
                                c: "1-2"
                            }
                        ],
                        d: '!'

                    },
                    {
                        b: [
                            {
                                c: "2-1"
                            },
                            {
                                c: "2-2"
                            }
                        ],
                        d: '?'
                    }
                ]
            }
            pf = new Pathfinder(obj);
        });

        [
            ['a.*.b.*', '{"0":{"0":{"c":"1-1"},"1":{"c":"1-2"}},"1":{"0":{"c":"2-1"},"1":{"c":"2-2"}}}'],
            ['a.*.b', '{"0":[{"c":"1-1"},{"c":"1-2"}],"1":[{"c":"2-1"},{"c":"2-2"}]}'],
            ['a.1.b', '[{"c":"2-1"},{"c":"2-2"}]'],
            ['a.0.b', '[{"c":"1-1"},{"c":"1-2"}]'],
        ].forEach(function (data) {

            let a = data.shift()
            let b = data.shift()


            it('.setVia(' + a + ') should result in ' + b, function () {
                let r = pf.getVia(a);
                let i = convertMapResult(r);
                expect(JSON.stringify(i)).is.equal(b);
            });


        });


    });


    describe('api tests', function () {

        let pathfinder, object;


        class A {
            get a() {
                return 1
            }
        }

        beforeEach(() => {

            object = {
                a: {
                    b: {
                        c: [
                            4, "test1", false, undefined, parseInt("a")
                        ],
                        d: undefined,
                        e: false,
                        f: [
                            {
                                g: false,
                                h: 3,
                                i: ["test2"]
                            }
                        ],
                        j: {},
                        k: (new Map).set('m', {n: 3}),
                        l: (new Set).add({n: 4})
                    }
                },
                b: new A
            };

            pathfinder = new Pathfinder(object);


        });

        describe('create new object', function () {

            [
                [12],
                [undefined],
                [null],
                ["test"]
            ].forEach(function (data) {

                let a = data.shift()
                let label = typeof a;
                if (a === null) label = 'null';


                it(label + ' should throw error', function () {

                    expect(() => {
                        new Pathfinder(a)
                    }).to.throw(Error)
                });
            });
        })

        describe('.setVia(x)', function () {

            let pf, obj;

            beforeEach(function () {
                obj = {}
                pf = new Pathfinder(obj);
            });

            [
                ['a.b.0.0.c', true, '{"a":{"b":[[{"c":true}]]}}'],
                ['a.b.0.c', true, '{"a":{"b":[{"c":true}]}}'],
                ['a.b.3.c', true, '{"a":{"b":[null,null,null,{"c":true}]}}'],
                ['a.b.c', true, '{"a":{"b":{"c":true}}}']
            ].forEach(function (data) {

                let a = data.shift()
                let b = data.shift()
                let c = data.shift()


                it('.setVia(' + a + ', ' + b + ') should result in ' + c, function () {
                    pf.setVia(a, b)
                    expect(JSON.stringify(obj)).is.equal(c);
                });


            });

        })

        describe('.setVia()', function () {

            let a;
            let b;
            let c;

            beforeEach(function () {
                a = "a.x";
                b = "true";
                c = "a.y.d";
            })

            it('.setVia(' + a + ', ' + b + ') should return Pathfinder', function () {
                expect(pathfinder.setVia(a, b)).is.instanceOf(Pathfinder);
            });

            it('.setVia(' + a + ', ' + b + ') should change object', function () {
                pathfinder.setVia(a, b);
                expect(JSON.stringify(object)).is.equal('{"a":{"b":{"c":[4,"test1",false,null,null],"e":false,"f":[{"g":false,"h":3,"i":["test2"]}],"j":{},"k":{},"l":{}},"x":"true"},"b":{}}');
            });

            it('.setVia(' + c + ', ' + b + ') should change object', function () {
                pathfinder.setVia(c, b);
                expect(JSON.stringify(object)).is.equal('{"a":{"b":{"c":[4,"test1",false,null,null],"e":false,"f":[{"g":false,"h":3,"i":["test2"]}],"j":{},"k":{},"l":{}},"y":{"d":"true"}},"b":{}}');
            });

        });


        describe('.exists()', function () {
            [
                ['a.b.c.1', true],
                ['b.a', true],
                ['a.b.x', false],
                ['a.x', false],
                ['a.b.q', false],
                ['a.b.c.0', true],
                ['a.b.d', true],
                ['a.b.f.0.g', true],
                ['a.b.f.0.i.0', true],
                ['a.b.f.0.i.2', false],
                ['a.b.e', true],
            ].forEach(function (data) {

                let a = data.shift()
                let b = data.shift()


                it('.exists(' + a + ') should return ' + b + ' ', function () {
                    expect(pathfinder.exists(a)).is.equal(b);
                });


            });


        });

        describe('.deleteVia()', function () {
            [
                ['a.b.e', '{"a":{"b":{"c":[4,"test1",false,null,null],"f":[{"g":false,"h":3,"i":["test2"]}],"j":{},"k":{},"l":{}}},"b":{}}'],
                ['a.b.f', '{"a":{"b":{"c":[4,"test1",false,null,null],"e":false,"j":{},"k":{},"l":{}}},"b":{}}'],
                ['a.b.j', '{"a":{"b":{"c":[4,"test1",false,null,null],"e":false,"f":[{"g":false,"h":3,"i":["test2"]}],"k":{},"l":{}}},"b":{}}'],

            ].forEach(function (data) {

                let a = data.shift()
                let b = data.shift()

                it('.deleteVia(' + a + ') should return ' + b + ' ', function () {
                    pathfinder.deleteVia(a)
                    expect(JSON.stringify(object)).is.equal(b);
                });
            });
        });

        describe('.getVia()', function () {
            describe('.getVia()', function () {


                beforeEach(function () {

                    object = {
                        a: {
                            b: {
                                c: [
                                    4, "test1", false, undefined, parseInt("a")
                                ],
                                d: undefined,
                                e: false,
                                f: [
                                    {
                                        g: false,
                                        h: 3,
                                        i: ["test2"]
                                    }
                                ],
                                j: {},
                                k: (new Map).set('m', {n: 3}),
                                l: (new Set).add({n: 4}),
                                o: [
                                    {p: {q: 1, r: true}},
                                    {p: {q: 2, r: true}},
                                    {p: {q: 3, r: true}},
                                    {p: {q: 4, r: true}},
                                    {p: {q: 5, r: true}}
                                ],
                                s: {
                                    t: {a: 1},
                                    u: {a: 2},
                                    v: {a: 3},
                                    x: {a: 4}
                                }
                            }
                        }
                    };

                    pathfinder = new Pathfinder(object);


                });

                [
                    ['a.*', '[["b",{"c":[4,"test1",false,null,null],"e":false,"f":[{"g":false,"h":3,"i":["test2"]}],"j":{},"k":{},"l":{},"o":[{"p":{"q":1,"r":true}},{"p":{"q":2,"r":true}},{"p":{"q":3,"r":true}},{"p":{"q":4,"r":true}},{"p":{"q":5,"r":true}}],"s":{"t":{"a":1},"u":{"a":2},"v":{"a":3},"x":{"a":4}}}]]'],
                    ['a.b.s.*.a', '[["t",1],["u",2],["v",3],["x",4]]'],
                    ['a.b.s.*', '[["t",{"a":1}],["u",{"a":2}],["v",{"a":3}],["x",{"a":4}]]'],
                    ['a.b.o.*.p.q', '[["0",1],["1",2],["2",3],["3",4],["4",5]]'],
                    ['a.b.o.*.p.r', '[["0",true],["1",true],["2",true],["3",true],["4",true]]'],
                    ['a.b.o.*.p', '[["0",{"q":1,"r":true}],["1",{"q":2,"r":true}],["2",{"q":3,"r":true}],["3",{"q":4,"r":true}],["4",{"q":5,"r":true}]]']
                ].forEach(function (data) {

                    let a = data.shift()
                    let b = data.shift()

                    it('.getVia(' + a + ') should result ' + b, function () {
                        let r = pathfinder.getVia(a)
                        expect(JSON.stringify(Array.from(r))).is.equal(b);
                    });

                });

            })

            it('.getVia() should result ', function () {

                let p = new Pathfinder({
                    a: {
                        x: [
                            {c: 1}, {c: 2}
                        ],
                        y: true
                    },
                    b: {
                        x: [
                            {c: 1, d: false}, {c: 2}
                        ],
                        y: true
                    },
                });

                let r = p.getVia("*.x.*.c")

                function mapToObj(map) {
                    var obj = {}
                    map.forEach(function (v, k) {
                        if (v instanceof Map) {
                            obj[k] = mapToObj(v)
                        } else {
                            obj[k] = v
                        }

                    })
                    return obj
                }

                expect(JSON.stringify(mapToObj(r))).is.equal('{"a":{"0":1,"1":2},"b":{"0":1,"1":2}}');
            });

            it('.getVia(a.b.l.0.n) with map should return 4 ', function () {
                expect(pathfinder.getVia('a.b.l.0.n')).is.equal(4);
            });

            [
                ['a.b.k.m.n', 3],
                ['a.b.l.0.n', 4],
                ['a.x', undefined],
                ['a.b.q', undefined],
                ['a.b.c.1', "test1"],
                ['a.b.c.0', 4],
                ['a.b.d', undefined],
                ['a.b.f.0.g', false],
                ['a.b.f.0.i.0', "test2"],
                ['a.b.e', false],
            ].forEach(function (data) {

                let a = data.shift()
                let b = data.shift()


                it('.getVia(' + a + ') should return ' + b + ' ', function () {
                    expect(pathfinder.getVia(a)).is.equal(b);
                });


            });

            [
                ['a.b.d.e'],
                ['a.b.d.x'],
                ['a.b.l.e.n'],
            ].forEach(function (data) {

                let a = data.shift()

                it('.getVia(' + a + ') should throw Error ', function () {
                    expect(() => pathfinder.getVia(a)).to.throw(Error)
                });

            });

        });

    });
});