"use strict";

const assert = require( 'chai' ).assert;
const ModuleBase = require( '../src/modules/Base' );

class ModuleTestParent extends ModuleBase {};
class ModuleTestChildA extends ModuleBase {};
class ModuleTestChildB extends ModuleBase {};
class ModuleTestChildC extends ModuleBase {};
class ModuleTestThrows extends ModuleBase {
	moduleSetup() {
		throw new Error( "setup throw" );
	}
	moduleClose() {
		throw new Error( 'close throw' );
	}
};


describe( "ModuleBase", function() {

	describe( "#constructor", function() {
		var module = new ModuleBase;
		it( "should exist", function() {
			assert.instanceOf( module, ModuleBase );
		});
	});

	describe( "#addModule", function() {
		var parent = new ModuleTestParent;
		it( 'should add modules', function() {
			parent.addModule( 'childA', ModuleTestChildA );
			parent.addModule( 'childB', new ModuleTestChildB( parent ) );
			assert.instanceOf( parent.childA, ModuleTestChildA );
			assert.instanceOf( parent.childB, ModuleTestChildB );
		});

		it( 'should NOT add module with existing name', function() {
			assert.throws( function() {
				parent.addModule( 'parent', null );
			}, /cannot add module/i);
			assert.throws( function() {
				parent.addModule( 'childA', null );
				parent.addModule( 'childA', null );
			}, /module being readded/i);
		});
		it( 'should NOT allow module being monkey patched', function() {
			assert.throws( function() {
				parent.childA = 10;
				assert.equal( parent.childA, 10 );
			});
		});
		it( 'should NOT allow module being added after setup', function( done ) {
			parent
				.setup()
				.then(function() {
					assert.throws( function() {
						parent.addModule( 'childC', ModuleTestChildC );
					});
				})
				.nodeify( done );
		});
	});

	describe( "#setup", function() {
		var parent = new ModuleTestParent;
		parent.addModule( 'childA', ModuleTestChildA );
		parent.addModule( 'childB', ModuleTestChildB );
		var moduleThrows = new ModuleTestThrows;
		
		it( 'should setup modules', function( done ) {
			parent
				.setup()
				.then(function() {
					assert.isTrue( parent.isSetupped(), 'parent must be setupped' );
					assert.isTrue( parent.childA.isSetupped(), 'child A must be setupped' );
					assert.isTrue( parent.childB.isSetupped(), 'child B must be setupped' );
				})
				.nodeify( done )
			;
		});
		
		it( 'should catch error when setuping modules', function( done ) {
			moduleThrows
				.setup()
				.then(function() {
					done( "should have thrown" );
				}).catch(function() {
					assert.isFalse( moduleThrows.isSetupped() );
					done();
				})
			;
		});
	});

	describe( "#close", function() {
		var parent = new ModuleTestParent;
		parent.addModule( 'childA', ModuleTestChildA );
		parent.addModule( 'childB', ModuleTestChildB );
		var moduleThrows = new ModuleTestThrows;
		
		it( 'should close modules', function( done ) {
			parent
				.close()
				.then(function() {
					assert.isFalse( parent.isSetupped(), 'parent must be closed' );
					assert.isFalse( parent.childA.isSetupped(), 'child A must be closed' );
					assert.isFalse( parent.childB.isSetupped(), 'child B must be closed' );
				})
				.nodeify( done )
			;
		});
		it( 'should catch error when closing modules', function( done ) {
			moduleThrows
				.close()
				.then(function() {
					done( "should have thrown" );
				}).catch(function() {
					done();
				})
			;
		});
	});

	
});
