import sinon from "sinon"
import {expect} from "chai"
import buildModuleDefinitionStatement from "../../../src/util/stmt/buildModuleDefinitionStatement"

describe("Test buildModuleDefinitionStatement(t, tree, content) function", () => {
    let t = {}
    beforeEach(() => {
        // Literals and identifiers
        t.stringLiteral = sinon.stub().callsFake((lit) => {
            return {
                literal: lit
            }
        })

        t.identifier = sinon.stub().callsFake((id) => {
            return {
                identifier: id
            }
        })

        // Declarations and properties
         t.variableDeclarator = sinon.stub().callsFake((id, init) => {
            return {
                id: id,
                init: init
            }
        })

        t.variableDeclaration = sinon.stub().callsFake((type, decls) => {
            return {
                type: type,
                decls: decls
            }
        })

        t.objectProperty = sinon.stub().callsFake((key,val) => {
            return {
                key: key,
                val: val
            }
        })

        // Expressions
        t.objectExpression = sinon.stub().callsFake((props) => {
            return {
                props: props
            }
        })

        t.arrayExpression = sinon.stub().callsFake((lits) => {
            return {
                literals: lits
            }
        })

        t.memberExpression = sinon.stub().callsFake((mem, prop) => {
            return {
                member: mem,
                property: prop
            }            
        })

        t.callExpression = sinon.stub().callsFake((callee, args) => {
            return {
                callee: callee,
                arguments: args
            }
        })

        t.functionExpression = sinon.stub().callsFake((id, params, block) => {
            return {
                id: id,
                params: params,
                block: block
            }
        })

        t.arrowFunctionExpression = sinon.stub().callsFake((params, block) => {
            return {
                params: params,
                block: block
            }
        })

        // Statements
        t.returnStatement = sinon.stub().callsFake((object) => {
            return {
                returnedObject: object
            }
        })

        t.blockStatement = sinon.stub().callsFake((content) => {
            return {
                content: content
            }
        })
       
       t.expressionStatement = sinon.stub().callsFake((expr) => {
           return {
               expression: expr
           }
       })
    })

    // Arguments is null
    it("Test buildModuleDefinitionStatement(t, tree, content) where t = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, null, {}, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where t = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, undefined, {}, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, {}, null, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, {}, undefined, null)).to.throw(ReferenceError)
    })

    // Tree fields expects
    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.exported = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: null,
            common: {deps: [], reqs: []}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}, 
            exports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.exported = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            common: {deps: [], reqs: []}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}, 
            exports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.common = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: null, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}, 
            exports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.common = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}, 
            exports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.bem = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: null,
            inheritances: [], 
            imports: {}, 
            exports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.bem = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []},
            inheritances: [], 
            imports: {}, 
            exports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.inheritances = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: null,
            inheritances: null, 
            imports: {}, 
            exports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.inheritances = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            imports: {}, 
            exports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.imports = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: null, 
            exports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.imports = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            exports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.exports = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}, 
            exports: null
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.exports = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.common.deps = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: null, reqs: []}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}, 
            exports: null
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.common.deps = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {reqs: []}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.common.reqs = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: null}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}, 
            exports: null
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.common.reqs = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: []}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.bem.deps = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: {deps: null, reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}, 
            exports: null
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.bem.deps = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: {reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.bem.reqs = null", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: {deps: [], reqs: null}, 
            inheritances: [], 
            imports: {}, 
            exports: null
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.bem.reqs = undefined", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: {deps: []}, 
            inheritances: [], 
            imports: {}
        }, null)).to.throw(ReferenceError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.common.deps.length != tree.common.reqs.length", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: ["item"], reqs: []}, 
            bem: {deps: [], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}, 
            exports: {}
        }, null)).to.throw(RangeError)
    })

    it("Test buildModuleDefinitionStatement(t, tree, content) where tree.bem.deps.length != tree.bem.reqs.length-2", () => {
        expect(buildModuleDefinitionStatement.bind({}, t, {
            exported: "exported",
            common: {deps: [], reqs: []}, 
            bem: {deps: ["item"], reqs: ["_provide","_self"]}, 
            inheritances: [], 
            imports: {}, 
            exports: {}
        }, null)).to.throw(RangeError)
    })


    it("Test buildModuleDefinitionStatement(t, tree, content) for successfull case", () => {
        expect(buildModuleDefinitionStatement(t, {
            "exported":"export-name",
            "common": {
                "deps": [
                    "dep-1",
                    "dep-2",
                    "inheritance-1",
                    "inheritance-2",
                    "inheritance-3"
                ],
                "reqs": [
                    "dep1",
                    "dep2",
                    "inheritance1",
                    "inheritance2",
                    "inheritance3"
                ]
            },
            "bem": {
                "deps": [
                    "bem__dep-1",
                    "bem__dep-2",
                    "bem__inheritance-1",
                    "bem__inheritance-2",
                    "bem__inheritance-3",
                ],
                "reqs": [
                    "_provide",
                    "bemDep1",
                    "bemDep2",
                    "bemInheritance1",
                    "bemInheritance2",
                    "bemInheritance3",
                    "_self"
                ]
            },
            "inheritances": [
                "inheritance1",
                "inheritance2",
                "inheritance3",
                "bemInheritance1",
                "bemInheritance2",
                "bemInheritance3",
                "_self"
            ],
            "imports": {
                "alias1": {
                    "member": "dep1",
                    "property": "property1"
                },
                "alias2": {
                    "member": "dep1",
                    "property": "default"
                },
                "alias3": {
                    "member": "dep2",
                    "property": "default"
                },
                "alias4": {
                    "member": "bemDep1",
                    "property": null
                },
                "alias5": {
                    "member": "bemDep2",
                    "property": "default"
                },
                "alias6": {
                    "member": "inheritance1",
                    "property": "default"
                },
                "alias7": {
                    "member": "inheritance2",
                    "property": "member1"
                }
            },
            "exports": {
                "default": "_default",
                "alias7": "alias7",
                "export2": "anyLocal"
            }
        },["Wrapped"])).to.deep.equals({
            "expression": {
                "callee": {
                    "member":{"identifier":"modules"},
                    "property":{"identifier":"define"}
                },
                "arguments":[
                    {"literal":"export-name"},
                    {
                        "literals":[
                            {"literal":"bem__dep-1"},
                            {"literal":"bem__dep-2"},
                            {"literal":"bem__inheritance-1"},
                            {"literal":"bem__inheritance-2"},
                            {"literal":"bem__inheritance-3"}
                        ]
                    },
                    {
                        "params":[
                            {"identifier":"_provide"},
                            {"identifier":"bemDep1"},
                            {"identifier":"bemDep2"},
                            {"identifier":"bemInheritance1"},
                            {"identifier":"bemInheritance2"},
                            {"identifier":"bemInheritance3"},
                            {"identifier":"_self"}
                        ],
                        "block":{
                            "content":[
                                {
                                    "type":"const",
                                    "decls":[
                                        {
                                            "id":{"identifier":"dep1"},
                                            "init":{
                                                "callee":{"identifier":"require"},
                                                "arguments":[{"literal":"dep-1"}]
                                            }
                                        },
                                        {
                                            "id":{"identifier":"dep2"},
                                            "init":{
                                                "callee":{"identifier":"require"},
                                                "arguments":[{"literal":"dep-2"}]
                                            }
                                        },
                                        {
                                            "id":{"identifier":"inheritance1"},
                                            "init":{"callee":{"identifier":"require"},
                                            "arguments":[{"literal":"inheritance-1"}]
                                        }
                                        },
                                        {
                                            "id":{"identifier":"inheritance2"},
                                            "init":{
                                                "callee":{"identifier":"require"},
                                                "arguments":[{"literal":"inheritance-2"}]
                                            }
                                        }, 
                                        {
                                            "id":{"identifier":"inheritance3"},
                                            "init":{
                                                "callee":{"identifier":"require"},
                                                "arguments":[{"literal":"inheritance-3"}]
                                            }
                                        }
                                    ]
                                },
                                {
                                    "expression": {
                                        "callee":{"identifier":"_provide"},
                                        "arguments": [
                                            {
                                                "callee":{
                                                    "member":{"identifier":"Object"},
                                                    "property":{"identifier":"assign"}
                                                },
                                                "arguments":[
                                                    {"props": []},
                                                    {"identifier":"inheritance1"},
                                                    {"identifier":"inheritance2"},
                                                    {"identifier":"inheritance3"},
                                                    {"identifier":"bemInheritance1"},
                                                    {"identifier":"bemInheritance2"},
                                                    {"identifier":"bemInheritance3"},
                                                    {"identifier":"_self"},
                                                    {
                                                        "callee":{
                                                            "id":null,
                                                            "params":[
                                                                {"identifier":"alias1"},
                                                                {"identifier":"alias2"},
                                                                {"identifier":"alias3"},
                                                                {"identifier":"alias4"},
                                                                {"identifier":"alias5"},
                                                                {"identifier":"alias6"},
                                                                {"identifier":"alias7"}
                                                            ],
                                                            "block":{
                                                                "content":[
                                                                    "Wrapped",
                                                                    {
                                                                        "returnedObject":{
                                                                            "props":[{
                                                                                "key":{"identifier":"default"},
                                                                                "val":{"identifier":"_default"}
                                                                            },
                                                                            {
                                                                                "key":{"identifier":"alias7"},
                                                                                "val":{"identifier":"alias7"}
                                                                            },
                                                                            {
                                                                                "key":{"identifier":"export2"},
                                                                                "val":{"identifier":"anyLocal"}
                                                                            }]
                                                                        }
                                                                    }
                                                                ]
                                                            }
                                                        },
                                                        "arguments":[
                                                            {
                                                                "member":{"identifier":"dep1"},
                                                                "property":{"identifier":"property1"}
                                                            },
                                                            {
                                                                "member":{"identifier":"dep1"},
                                                                "property":{"identifier":"default"}
                                                            },
                                                            {
                                                                "member":{"identifier":"dep2"},
                                                                "property":{"identifier":"default"}
                                                            },
                                                            {"identifier":"bemDep1"},
                                                            {
                                                                "member":{"identifier":"bemDep2"},
                                                                "property":{"identifier":"default"}
                                                            },
                                                            {
                                                                "member":{"identifier":"inheritance1"},
                                                                "property":{"identifier":"default"}
                                                            },
                                                            {
                                                                "member":{"identifier":"inheritance2"},
                                                                "property":{"identifier":"member1"}
                                                            }
                                                        ]
                                                    }
                                                ]
                                            }
                                        ]
                                    }
                                }
                            ]
                        }
                    }
                ]
            }
        })
    })

})