// @flow
import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import sinon from 'sinon';
import { spies } from '@test';

import { getValidDatabaseObj as getValidObj } from '@helpers/beat';
import { DatabaseEditor as C } from '.';
import { databaseProtocols } from '@store/selectors';

import testDbs from '@test/test_dbs.json';
import testDfs from '@test/test_dfs.json';

const dfs = [];

describe('<DatabaseEditor />', () => {
	let wrapper;

	afterEach(() => {
		if(wrapper && wrapper.unmount)
			wrapper.unmount();
	});

	describe('accepts', () => {
		const dbs = [
			{
				name: 'test/test/1',
				contents: {
					'root_folder': '/testing',
					description: 'this is the basic test case for database schema validation',
					protocols: [
						{
							name: 'protocol_1',
							template: 'template_1',
							sets: [
								{
									name: 'set_1',
									view: 'view_1',
									template: 'set_template_1',
									parameters: {
										param1: 'i dont know what these are'
									},
									outputs: {
										output_1: 'a/specific_dataformat/1',
									}
								}
							]
						}
					]
				}
			}
		].concat(testDbs.map(db => getValidObj(db)));

		dbs.forEach(function(db){
			const saveFunc = () => {};
			const updateFunc = () => {};
			it(`${ db.name }`, () => {
				const protocols = databaseProtocols({ database: [ db ]});
				wrapper = mount(
					<C
						data={db}
						databases={dbs}
						dataformats={dfs}
						saveFunc={saveFunc}
						updateFunc={updateFunc}
						protocols={protocols}
					/>
				);

				expect(wrapper).to.have.props(
					['data', 'databases', 'dataformats', 'saveFunc', 'updateFunc', 'protocols']
				).deep.equal(
					[db, dbs, dfs, saveFunc, updateFunc, protocols]
				);
			});
		});
	});

	describe('creates', () => {
		it('simple/1', () => {
			const dbName = 'simple/1';
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			wrapper = mount(
				<C
					data={getValidObj({name: dbName, contents: {}})}
					databases={testDbs.filter(d => d.name !== dbName).map(d => getValidObj(d))}
					dataformats={testDfs}
					saveFunc={saveFunc}
					updateFunc={updateFunc}
					protocols={[]}
				/>
			);

			expect(wrapper).to.have.props(
				['data', 'databases', 'dataformats', 'saveFunc']
			);

			// general db
			expect(wrapper.props().data).to.have.property('name', dbName);

			wrapper.find('input[name="description"]').simulate('change', { target: { value: 'A test database that emits integers' }});
			expect(updateFunc.callCount).to.equal(1);
			expect(wrapper.props().data.contents).to.have.property('description', 'A test database that emits integers');

			wrapper.find('input[name="rootFolder"]').simulate('change', { target: { value: '/this/database/does/not/require/a/path' }});
			expect(updateFunc.callCount).to.equal(2);
			expect(wrapper.props().data.contents).to.have.property('root_folder', '/this/database/does/not/require/a/path');

			// create protocols
			wrapper.find('button#newProtocol').simulate('click');
			expect(updateFunc.callCount).to.equal(3);
			wrapper.find('button#newProtocol').simulate('click');
			expect(updateFunc.callCount).to.equal(4);

			expect(wrapper.props().data.contents).to.have.property('protocols').with.lengthOf(2);

			// protocol 1
			wrapper.find('button#protocolChoice0').simulate('click');

			wrapper.find('CacheInput[name="protocolName"]').prop('onChange')( { target: { value: 'protocol' }});
			expect(updateFunc.callCount).to.equal(5);
			expect(wrapper.props().data.contents.protocols[0]).to.have.property('name', 'protocol');

			wrapper.find('CacheInput[name="protocolTemplate"]').prop('onChange')( { target: { value: 'test_integers' }});
			expect(updateFunc.callCount).to.equal(6);
			expect(wrapper.props().data.contents.protocols[0]).to.have.property('template', 'test_integers');

			// protocol 1 sets
			wrapper.find('button#newSet').simulate('click');
			expect(updateFunc.callCount).to.equal(7);
			wrapper.find('button#newSet').simulate('click');
			expect(updateFunc.callCount).to.equal(8);
			expect(wrapper.props().data.contents.protocols[0]).to.have.property('sets').with.lengthOf(2);

			// protocol 1 set 1
			wrapper.find('CacheInput[name="setName0"]').prop('onChange')( { target: { value: 'set' }});
			expect(updateFunc.callCount).to.equal(9);
			expect(wrapper.props().data.contents.protocols[0].sets[0]).to.have.property('name', 'set');
			wrapper.find('CacheInput[name="setTemplate0"]').prop('onChange')( { target: { value: 'set' }});
			expect(updateFunc.callCount).to.equal(10);
			expect(wrapper.props().data.contents.protocols[0].sets[0]).to.have.property('template', 'set');
			wrapper.find('CacheInput[name="setView0"]').prop('onChange')( { target: { value: 'View' }});
			expect(updateFunc.callCount).to.equal(11);
			expect(wrapper.props().data.contents.protocols[0].sets[0]).to.have.property('view', 'View');

			wrapper.find('.set0 button.newOutput').simulate('click');
			expect(updateFunc.callCount).to.equal(12);
			wrapper.find('.set0 .setOutput CacheInput[type="text"]').prop('onChange')( { target: { value: 'out' }});
			expect(updateFunc.callCount).to.equal(13);
			wrapper.find('.set0 .setOutput select').simulate('change', { target: { value: 'system/integer/1' }});
			expect(updateFunc.callCount).to.equal(14);
			expect(wrapper.props().data.contents.protocols[0].sets[0].outputs).to.have.property('out', 'system/integer/1');

			// protocol 1 set 2
			wrapper.find('CacheInput[name="setName1"]').prop('onChange')( { target: { value: 'set2' }});
			expect(updateFunc.callCount).to.equal(15);
			expect(wrapper.props().data.contents.protocols[0].sets[1]).to.have.property('name', 'set2');
			wrapper.find('CacheInput[name="setTemplate1"]').prop('onChange')( { target: { value: 'set' }});
			expect(updateFunc.callCount).to.equal(16);
			expect(wrapper.props().data.contents.protocols[0].sets[1]).to.have.property('template', 'set');
			wrapper.find('CacheInput[name="setView1"]').prop('onChange')( { target: { value: 'View2' }});
			expect(updateFunc.callCount).to.equal(17);
			expect(wrapper.props().data.contents.protocols[0].sets[1]).to.have.property('view', 'View2');

			wrapper.find('.set1 button.newOutput').simulate('click');
			expect(updateFunc.callCount).to.equal(18);
			wrapper.find('.set1 .setOutput CacheInput[type="text"]').prop('onChange')( { target: { value: 'out' }});
			expect(updateFunc.callCount).to.equal(19);
			wrapper.find('.set1 .setOutput select').simulate('change', { target: { value: 'system/integer/1' }});
			expect(updateFunc.callCount).to.equal(20);
			expect(wrapper.props().data.contents.protocols[0].sets[1].outputs).to.have.property('out', 'system/integer/1');

			// protocol 2
			wrapper.find('button#protocolChoice1').simulate('click');
			wrapper.find('CacheInput[name="protocolName"]').prop('onChange')( { target: { value: 'protocol2' }});
			expect(updateFunc.callCount).to.equal(21);
			expect(wrapper.props().data.contents.protocols[1]).to.have.property('name', 'protocol2');

			wrapper.find('CacheInput[name="protocolTemplate"]').prop('onChange')( { target: { value: 'test_integers' }});
			expect(updateFunc.callCount).to.equal(22);
			expect(wrapper.props().data.contents.protocols[1]).to.have.property('template', 'test_integers');

			// protocol 2 sets
			wrapper.find('button#newSet').simulate('click');
			expect(updateFunc.callCount).to.equal(23);
			wrapper.find('button#newSet').simulate('click');
			expect(updateFunc.callCount).to.equal(24);
			expect(wrapper.props().data.contents.protocols[1]).to.have.property('sets').with.lengthOf(2);

			// protocol 2 set 1
			wrapper.find('CacheInput[name="setName0"]').prop('onChange')( { target: { value: 'set' }});
			expect(updateFunc.callCount).to.equal(25);
			expect(wrapper.props().data.contents.protocols[1].sets[0]).to.have.property('name', 'set');
			wrapper.find('CacheInput[name="setTemplate0"]').prop('onChange')( { target: { value: 'set' }});
			expect(updateFunc.callCount).to.equal(26);
			expect(wrapper.props().data.contents.protocols[1].sets[0]).to.have.property('template', 'set');
			wrapper.find('CacheInput[name="setView0"]').prop('onChange')( { target: { value: 'LargeView' }});
			expect(updateFunc.callCount).to.equal(27);
			expect(wrapper.props().data.contents.protocols[1].sets[0]).to.have.property('view', 'LargeView');

			wrapper.find('.set0 button.newOutput').simulate('click');
			expect(updateFunc.callCount).to.equal(28);
			wrapper.find('.set0 .setOutput CacheInput[type="text"]').prop('onChange')( { target: { value: 'out' }});
			expect(updateFunc.callCount).to.equal(29);
			wrapper.find('.set0 .setOutput select').simulate('change', { target: { value: 'system/integer/1' }});
			expect(updateFunc.callCount).to.equal(30);
			expect(wrapper.props().data.contents.protocols[1].sets[0].outputs).to.have.property('out', 'system/integer/1');

			// protocol 2 set 2
			wrapper.find('CacheInput[name="setName1"]').prop('onChange')( { target: { value: 'set2' }});
			expect(updateFunc.callCount).to.equal(31);
			expect(wrapper.props().data.contents.protocols[1].sets[1]).to.have.property('name', 'set2');
			wrapper.find('CacheInput[name="setTemplate1"]').prop('onChange')( { target: { value: 'set' }});
			expect(updateFunc.callCount).to.equal(32);
			expect(wrapper.props().data.contents.protocols[1].sets[1]).to.have.property('template', 'set');
			wrapper.find('CacheInput[name="setView1"]').prop('onChange')( { target: { value: 'View2' }});
			expect(updateFunc.callCount).to.equal(33);
			expect(wrapper.props().data.contents.protocols[1].sets[1]).to.have.property('view', 'View2');

			wrapper.find('.set1 button.newOutput').simulate('click');
			expect(updateFunc.callCount).to.equal(34);
			wrapper.find('.set1 .setOutput CacheInput[type="text"]').prop('onChange')( { target: { value: 'out' }});
			expect(updateFunc.callCount).to.equal(35);
			wrapper.find('.set1 .setOutput select').simulate('change', { target: { value: 'system/integer/1' }});
			expect(updateFunc.callCount).to.equal(36);
			expect(wrapper.props().data.contents.protocols[1].sets[1].outputs).to.have.property('out', 'system/integer/1');

			expect(wrapper.props().data.contents).to.deep.equal({
				'root_folder': '/this/database/does/not/require/a/path',
				'description': 'A test database that emits integers',
				'protocols': [
					{
						'name': 'protocol',
						'template': 'test_integers',
						'sets': [
							{
								'outputs': {
									'out': 'system/integer/1'
								},
								'name': 'set',
								'template': 'set',
								'view': 'View',
								parameters: {},
							},
							{
								'outputs': {
									'out': 'system/integer/1'
								},
								'name': 'set2',
								'template': 'set',
								'view': 'View2',
								parameters: {},
							}
						]
					},
					{
						'name': 'protocol2',
						'template': 'test_integers',
						'sets': [
							{
								'outputs': {
									'out': 'system/integer/1'
								},
								'name': 'set',
								'template': 'set',
								'view': 'LargeView',
								parameters: {},
							},
							{
								'outputs': {
									'out': 'system/integer/1'
								},
								'name': 'set2',
								'template': 'set',
								'view': 'View2',
								parameters: {},
							}
						]
					}
				]
			});
		});

		// tests inserting a protocol from a database
		it('simple/2', () => {
			const dbName = 'simple/2';
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({
					data: obj,
					protocols: databaseProtocols({ database: [ obj ] })
				});
			};
			const updateFunc = sinon.spy(_updateFunc);
			const startData = getValidObj({name: dbName, contents: {}});
			wrapper = mount(
				<C
					data={startData}
					databases={testDbs.filter(d => d.name !== dbName).map(d => getValidObj(d))}
					dataformats={testDfs}
					saveFunc={saveFunc}
					updateFunc={updateFunc}
					protocols={databaseProtocols({ database: [ startData ] })}
				/>
			);

			expect(wrapper).to.have.props(
				['data', 'databases', 'dataformats', 'saveFunc', 'protocols']
			);

			// general db
			expect(wrapper.props().data).to.have.property('name', dbName);

			wrapper.find('input[name="description"]').simulate('change', { target: { value: 'A test database that emits integers' }});
			expect(updateFunc.callCount).to.equal(1);
			expect(wrapper.props().data.contents).to.have.property('description', 'A test database that emits integers');

			wrapper.find('input[name="rootFolder"]').simulate('change', { target: { value: '/this/database/does/not/require/a/path' }});
			expect(updateFunc.callCount).to.equal(2);
			expect(wrapper.props().data.contents).to.have.property('root_folder', '/this/database/does/not/require/a/path');

			// create protocols
			wrapper.find('button#newProtocol').simulate('click');
			expect(updateFunc.callCount).to.equal(3);

			expect(wrapper.props().data.contents).to.have.property('protocols').with.lengthOf(1);

			// protocol 1
			wrapper.find('button#protocolChoice0').simulate('click');

			wrapper.find('CacheInput[name="protocolName"]').prop('onChange')( { target: { value: 'protocol' }});
			expect(updateFunc.callCount).to.equal(4);
			expect(wrapper.props().data.contents.protocols[0]).to.have.property('name', 'protocol');

			wrapper.find('CacheInput[name="protocolTemplate"]').prop('onChange')( { target: { value: 'test_integers' }});
			expect(updateFunc.callCount).to.equal(5);
			expect(wrapper.props().data.contents.protocols[0]).to.have.property('template', 'test_integers');

			// protocol 1 sets
			wrapper.find('button#newSet').simulate('click');
			expect(updateFunc.callCount).to.equal(6);
			wrapper.find('button#newSet').simulate('click');
			expect(updateFunc.callCount).to.equal(7);
			expect(wrapper.props().data.contents.protocols[0]).to.have.property('sets').with.lengthOf(2);

			// protocol 1 set 1
			wrapper.find('CacheInput[name="setName0"]').prop('onChange')( { target: { value: 'set' }});
			expect(updateFunc.callCount).to.equal(8);
			expect(wrapper.props().data.contents.protocols[0].sets[0]).to.have.property('name', 'set');
			wrapper.find('CacheInput[name="setTemplate0"]').prop('onChange')( { target: { value: 'set' }});
			expect(updateFunc.callCount).to.equal(9);
			expect(wrapper.props().data.contents.protocols[0].sets[0]).to.have.property('template', 'set');
			wrapper.find('CacheInput[name="setView0"]').prop('onChange')( { target: { value: 'View' }});
			expect(updateFunc.callCount).to.equal(10);
			expect(wrapper.props().data.contents.protocols[0].sets[0]).to.have.property('view', 'View');

			wrapper.find('.set0 button.newOutput').simulate('click');
			expect(updateFunc.callCount).to.equal(11);
			wrapper.find('.set0 .setOutput CacheInput[type="text"]').prop('onChange')( { target: { value: 'out' }});
			expect(updateFunc.callCount).to.equal(12);
			wrapper.find('.set0 .setOutput select').simulate('change', { target: { value: 'system/integer/1' }});
			expect(updateFunc.callCount).to.equal(13);
			expect(wrapper.props().data.contents.protocols[0].sets[0].outputs).to.have.property('out', 'system/integer/1');

			// protocol 1 set 2
			wrapper.find('CacheInput[name="setName1"]').prop('onChange')( { target: { value: 'set2' }});
			expect(updateFunc.callCount).to.equal(14);
			expect(wrapper.props().data.contents.protocols[0].sets[1]).to.have.property('name', 'set2');
			wrapper.find('CacheInput[name="setTemplate1"]').prop('onChange')( { target: { value: 'set' }});
			expect(updateFunc.callCount).to.equal(15);
			expect(wrapper.props().data.contents.protocols[0].sets[1]).to.have.property('template', 'set');
			wrapper.find('CacheInput[name="setView1"]').prop('onChange')( { target: { value: 'View2' }});
			expect(updateFunc.callCount).to.equal(16);
			expect(wrapper.props().data.contents.protocols[0].sets[1]).to.have.property('view', 'View2');

			wrapper.find('.set1 button.newOutput').simulate('click');
			expect(updateFunc.callCount).to.equal(17);
			wrapper.find('.set1 .setOutput CacheInput[type="text"]').prop('onChange')( { target: { value: 'out' }});
			expect(updateFunc.callCount).to.equal(18);
			wrapper.find('.set1 .setOutput select').simulate('change', { target: { value: 'system/integer/1' }});
			expect(updateFunc.callCount).to.equal(19);
			expect(wrapper.props().data.contents.protocols[0].sets[1].outputs).to.have.property('out', 'system/integer/1');

			// protocol 2
			wrapper.find('input#insertProtocolSearch').prop('onChange')( { target: { value: 'test_integers' }});
			wrapper.update();
			wrapper.find('button.insertProtocolOption').at(0).simulate('click');
			expect(updateFunc.callCount).to.equal(20);
			wrapper.find('button#protocolChoice1').simulate('click');
			wrapper.find('CacheInput[name="protocolName"]').prop('onChange')( { target: { value: 'protocol2' }});
			expect(updateFunc.callCount).to.equal(21);
			expect(wrapper.props().data.contents.protocols[1]).to.have.property('name', 'protocol2');

			expect(wrapper.props().data.contents).to.deep.equal({
				'root_folder': '/this/database/does/not/require/a/path',
				'description': 'A test database that emits integers',
				'protocols': [
					{
						'name': 'protocol',
						'template': 'test_integers',
						'sets': [
							{
								'outputs': {
									'out': 'system/integer/1'
								},
								'name': 'set',
								'template': 'set',
								'view': 'View',
								parameters: {},
							},
							{
								'outputs': {
									'out': 'system/integer/1'
								},
								'name': 'set2',
								'template': 'set',
								'view': 'View2',
								parameters: {},
							}
						]
					},
					{
						'name': 'protocol2',
						'template': 'test_integers',
						'sets': [
							{
								'outputs': {
									'out': 'system/integer/1'
								},
								'name': 'set',
								'template': 'set',
								'view': 'View',
								parameters: {},
							},
							{
								'outputs': {
									'out': 'system/integer/1'
								},
								'name': 'set2',
								'template': 'set',
								'view': 'View2',
								parameters: {},
							}
						]
					}
				]
			});
		});
	});
});
