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

import * as Selectors from '@store/selectors.js';
import { ExperimentEditor as C } from '.';
import {  getValidExperimentObj as getValidObj } from '@helpers/beat';

import testAlgs from '@test/test_algs.json';
import testDbs from '@test/test_dbs.json';
import testExps from '@test/test_exps.json';
import testTcs from '@test/test_tcs.json';

const envs = {
	'Scientific Python 2.7': {
		name: 'Scientific Python 2.7',
		version: '1.0.0',
		queues: {
			Default: {}
		},
		packages: {}
	}
};

describe('<ExperimentEditor />', () => {
	let wrapper;
	const normalBlocks = Selectors.normalBlocks({ algorithm: testAlgs });
	const analyzerBlocks = Selectors.analyzerBlocks({ algorithm: testAlgs });
	const datasets = Selectors.flattenedDatabases({ database: testDbs });

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

	describe('accepts', () => {
		const exps = [
		].concat(testExps);

		exps.forEach(function(exp){
			if(exp.name !== 'user/user/single/1/single_add')
				return;
			const saveFunc = () => {};
			const updateFunc = () => {};
			const tc = testTcs.find(tc => exp.name.includes(tc.name));

			it(`${ exp.name }`, () => {
				wrapper = mount(
					<C
						data={exp}
						experiments={exps}
						normalBlocks={normalBlocks}
						analyzerBlocks={analyzerBlocks}
						datasets={datasets}
						toolchain={tc}
						saveFunc={saveFunc}
						environments={envs}
						updateFunc={updateFunc}
					/>
				);

				expect(wrapper).to.have.props(
					['data', 'experiments', 'normalBlocks', 'analyzerBlocks', 'datasets', 'toolchain', 'saveFunc', 'environments', 'updateFunc']
				);
			});
		});
	});

	describe('creates', () => {
		it(`user/user/single/1/single_add`, () => {
			const expName = 'user/user/single/1/single_add';
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			const tc = testTcs.find(tc => expName.includes(tc.name));
			wrapper = mount(
				<C
					data={getValidObj({name: expName, contents: {}}, tc, [...normalBlocks, ...analyzerBlocks])}
					experiments={[]}
					normalBlocks={normalBlocks}
					analyzerBlocks={analyzerBlocks}
					datasets={datasets}
					toolchain={tc}
					saveFunc={saveFunc}
					environments={envs}
					updateFunc={updateFunc}
				/>
			);

			//console.log('doing name');

			expect(wrapper.props().data).to.have.property('name', expName);

			//console.log('finished name change, doing dataset');

			wrapper.find('svg #block_set').simulate('click');
			wrapper.find('div.dataset0 select').simulate('change', { target: { value: 'protocol/set (simple/1)'}});
			expect(updateFunc.callCount).to.equal(1);
			expect(wrapper.props().data.contents).to.have.deep.property('datasets', {
				'set': {
					'set': 'set',
					'protocol': 'protocol',
					'database': 'simple/1'
				}
			});

			//console.log('finished dataset, doing block');

			wrapper.find('svg #block_echo').simulate('click');
			expect(wrapper.find({ name: 'echo', set: 'blocks'}).find('.tcBlockBackground').prop('className')).to.include('highlighted');
			wrapper.find('div.block0 div.algorithm select').at(0).simulate('change', { target: { value: 'user/integers_add/1'}});
			expect(updateFunc.callCount).to.equal(2);
			expect(wrapper.props().data.contents).to.have.deep.property('blocks', {
				'echo': {
					'inputs': {
						'in_data': 'in'
					},
					'algorithm': 'user/integers_add/1',
					'outputs': {
						'out_data': 'out'
					},
					parameters: {}
				}
			});

			//console.log('finished block, doing analyzer');

			wrapper.find('svg #block_analysis').simulate('click');
			expect(wrapper.find({ name: 'analysis', set: 'analyzers'}).find('.tcBlockBackground').prop('className')).to.include('highlighted');
			wrapper.find('div.block0 div.algorithm select').at(0).simulate('change', { target: { value: 'user/integers_echo_analyzer/1'}});
			expect(updateFunc.callCount).to.equal(3);
			expect(wrapper.props().data.contents).to.have.deep.property('analyzers', {
				'analysis': {
					'inputs': {
						'in_data': 'in'
					},
					'algorithm': 'user/integers_echo_analyzer/1',
					parameters: {},
				}
			});

			//console.log('finished analyzer, doing env');

			wrapper.find('.globals select.env').simulate('change', { target: { value: 'Scientific Python 2.7 (1.0.0)'}});
			expect(updateFunc.callCount).to.equal(4);
			wrapper.find('.globals select.queue').simulate('change', { target: { value: 'Default'}});
			expect(updateFunc.callCount).to.equal(5);
			expect(wrapper.props().data.contents).to.have.deep.property('globals', {
				'queue': 'Default',
				'environment': {
					'version': '1.0.0',
					'name': 'Scientific Python 2.7'
				},
				'user/integers_add/1': {
					offset: 1
				},
			});

			//console.log('finished env, doing cache check');

			expect(wrapper.props().data).to.be.deep.equal({
				'name': 'user/user/single/1/single_add',
				'contents': {
					description: '',
					'analyzers': {
						'analysis': {
							'inputs': {
								'in_data': 'in'
							},
							'algorithm': 'user/integers_echo_analyzer/1',
							parameters: {},
						}
					},
					'datasets': {
						'set': {
							'set': 'set',
							'protocol': 'protocol',
							'database': 'simple/1'
						}
					},
					'blocks': {
						'echo': {
							'inputs': {
								'in_data': 'in'
							},
							'algorithm': 'user/integers_add/1',
							'outputs': {
								'out_data': 'out'
							},
							parameters: {}
						}
					},
					'globals': {
						'queue': 'Default',
						'environment': {
							'version': '1.0.0',
							'name': 'Scientific Python 2.7'
						},
						'user/integers_add/1': {
							offset: 1
						},
					}
				}
			});
		});

		// tests:
		// - assigning protocol for datasets
		// - copy block algs
		it('test/test/iris_advanced/1/iris', () => {
			const expName ='test/test/iris_advanced/1/iris';
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			const tc = testTcs.find(tc => expName.includes(tc.name));
			wrapper = mount(
				<C
					data={getValidObj({name: expName, contents: {}}, tc, [...normalBlocks, ...analyzerBlocks])}
					experiments={[]}
					normalBlocks={normalBlocks}
					analyzerBlocks={analyzerBlocks}
					datasets={datasets}
					toolchain={tc}
					saveFunc={saveFunc}
					environments={envs}
					updateFunc={updateFunc}
				/>
			);

			expect(wrapper.props().data).to.have.property('name', expName);

			//console.log('doing dataset');

			wrapper.find('svg #block_training_data').simulate('click');
			wrapper.find('div.datasets select').at(0).simulate('change', { target: { value: '{"testing_data":{"database":"iris/1","protocol":"Main","set":"testing"},"training_data":{"database":"iris/1","protocol":"Main","set":"training"}}'}});
			expect(updateFunc.callCount).to.equal(1);
			expect(wrapper.props().data.contents).to.have.deep.property('datasets', {
				'testing_data': {
					'database': 'iris/1',
					'protocol': 'Main',
					'set': 'testing'
				},
				'training_data': {
					'database': 'iris/1',
					'protocol': 'Main',
					'set': 'training'
				}
			});

			//console.log('finished dataset, doing preprocessor blocks');

			wrapper.find('svg #block_pre_training').simulate('click');
			expect(wrapper.find({ name: 'pre_training', set: 'blocks'}).find('.tcBlockBackground').prop('className')).to.include('highlighted');
			wrapper.find('div.block0 div.algorithm select').at(0).simulate('change', { target: { value: 'test/iris_preprocessor/1'}});
			//console.log('finished preprocessor_training, copying alg to other preprocessors');
			expect(updateFunc.callCount).to.equal(2);
			wrapper.find('div.block0 div.algorithm button').at(0).simulate('click');
			expect(updateFunc.callCount).to.equal(3);

			expect(wrapper.props().data.contents).to.have.deep.property('blocks', {
				'pre_testing': {
					'algorithm': 'test/iris_preprocessor/1',
					'inputs': {
						'measurements': 'measurements'
					},
					'outputs': {
						'measurements': 'measurements'
					},
					'parameters': {}
				},
				'pre_training': {
					'algorithm': 'test/iris_preprocessor/1',
					'inputs': {
						'measurements': 'measurements'
					},
					'outputs': {
						'measurements': 'measurements'
					},
					'parameters': {}
				},
				'testing_alg': {
					'algorithm': '',
					'inputs': {
						'lda_machine': '',
						'measurements': ''
					},
					'outputs': {
						'scores': ''
					},
					'parameters': {}
				},
				'training_alg': {
					'algorithm': '',
					'inputs': {
						'measurements': '',
						'species': ''
					},
					'outputs': {
						'lda_machine': ''
					},
					'parameters': {}
				}
			});

			//console.log('finished preprocessors, doing training');

			wrapper.find('svg #block_training_alg').simulate('click');
			expect(wrapper.find({ name: 'training_alg', set: 'blocks'}).find('.tcBlockBackground').prop('className')).to.include('highlighted');
			wrapper.find('div.block0 div.algorithm select').at(0).simulate('change', { target: { value: 'test/iris_training/1'}});
			expect(updateFunc.callCount).to.equal(4);
			expect(wrapper.props().data.contents).to.have.deep.property('blocks', {
				'pre_testing': {
					'algorithm': 'test/iris_preprocessor/1',
					'inputs': {
						'measurements': 'measurements'
					},
					'outputs': {
						'measurements': 'measurements'
					},
					'parameters': {}
				},
				'pre_training': {
					'algorithm': 'test/iris_preprocessor/1',
					'inputs': {
						'measurements': 'measurements'
					},
					'outputs': {
						'measurements': 'measurements'
					},
					'parameters': {}
				},
				'testing_alg': {
					'algorithm': '',
					'inputs': {
						'lda_machine': '',
						'measurements': ''
					},
					'outputs': {
						'scores': ''
					},
					'parameters': {}
				},
				'training_alg': {
					'algorithm': 'test/iris_training/1',
					'inputs': {
						'measurements': 'measurements',
						'species': 'species'
					},
					'outputs': {
						'lda_machine': 'lda_machine'
					},
					'parameters': {}
				}
			});

			//console.log('finished training, doing testing');

			wrapper.find('svg #block_testing_alg').simulate('click');
			expect(wrapper.find({ name: 'testing_alg', set: 'blocks'}).find('.tcBlockBackground').prop('className')).to.include('highlighted');
			wrapper.find('div.block0 div.algorithm select').at(0).simulate('change', { target: { value: 'test/iris_testing/1'}});
			expect(updateFunc.callCount).to.equal(5);
			expect(wrapper.props().data.contents).to.have.deep.property('blocks', {
				'pre_testing': {
					'algorithm': 'test/iris_preprocessor/1',
					'inputs': {
						'measurements': 'measurements'
					},
					'outputs': {
						'measurements': 'measurements'
					},
					'parameters': {}
				},
				'pre_training': {
					'algorithm': 'test/iris_preprocessor/1',
					'inputs': {
						'measurements': 'measurements'
					},
					'outputs': {
						'measurements': 'measurements'
					},
					'parameters': {}
				},
				'testing_alg': {
					'algorithm': 'test/iris_testing/1',
					'inputs': {
						'lda_machine': 'lda_machine',
						'measurements': 'measurements'
					},
					'outputs': {
						'scores': 'scores'
					},
					'parameters': {}
				},
				'training_alg': {
					'algorithm': 'test/iris_training/1',
					'inputs': {
						'measurements': 'measurements',
						'species': 'species'
					},
					'outputs': {
						'lda_machine': 'lda_machine'
					},
					'parameters': {}
				}
			});

			//console.log('finished blocks, doing analyzer');

			wrapper.find('svg #block_analyzer').simulate('click');
			expect(wrapper.find({ name: 'analyzer', set: 'analyzers'}).find('.tcBlockBackground').prop('className')).to.include('highlighted');
			wrapper.find('div.block0 div.algorithm select').at(0).simulate('change', { target: { value: 'test/iris_analyzer/1'}});
			expect(updateFunc.callCount).to.equal(6);
			expect(wrapper.props().data.contents).to.have.deep.property('analyzers', {
				'analyzer': {
					'algorithm': 'test/iris_analyzer/1',
					'inputs': {
						'scores': 'scores',
						'species': 'species'
					},
					'parameters': {}
				}
			});

			//console.log('finished analyzer, doing cache check');

			expect(wrapper.props().data).to.be.deep.equal({
				'name': 'test/test/iris_advanced/1/iris',
				'contents': {
					'description': '',
					'analyzers': {
						'analyzer': {
							'algorithm': 'test/iris_analyzer/1',
							'inputs': {
								'scores': 'scores',
								'species': 'species'
							},
							'parameters': {}
						}
					},
					'blocks': {
						'pre_testing': {
							'algorithm': 'test/iris_preprocessor/1',
							'inputs': {
								'measurements': 'measurements'
							},
							'outputs': {
								'measurements': 'measurements'
							},
							'parameters': {}
						},
						'pre_training': {
							'algorithm': 'test/iris_preprocessor/1',
							'inputs': {
								'measurements': 'measurements'
							},
							'outputs': {
								'measurements': 'measurements'
							},
							'parameters': {}
						},
						'testing_alg': {
							'algorithm': 'test/iris_testing/1',
							'inputs': {
								'lda_machine': 'lda_machine',
								'measurements': 'measurements'
							},
							'outputs': {
								'scores': 'scores'
							},
							'parameters': {}
						},
						'training_alg': {
							'algorithm': 'test/iris_training/1',
							'inputs': {
								'measurements': 'measurements',
								'species': 'species'
							},
							'outputs': {
								'lda_machine': 'lda_machine'
							},
							'parameters': {}
						}
					},
					'datasets': {
						'testing_data': {
							'database': 'iris/1',
							'protocol': 'Main',
							'set': 'testing'
						},
						'training_data': {
							'database': 'iris/1',
							'protocol': 'Main',
							'set': 'training'
						}
					},
					'globals': {
						'queue': 'Default',
						'environment': {
							'name': 'Scientific Python 2.7',
							'version': '0.0.4'
						}
					}
				}
			});
		});
	});

	describe('Regression Tests', () => {
		it(`clears the 'echo' & 'analysis' blocks from user/user/single/1/single_add`, () => {
			const expName = 'user/user/single/1/single_add';
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			const exp = testExps.find(exp => expName === exp.name);
			const tc = testTcs.find(tc => expName.includes(tc.name));
			wrapper = mount(
				<C
					data={getValidObj(exp, tc, [...normalBlocks, ...analyzerBlocks])}
					experiments={[]}
					normalBlocks={normalBlocks}
					analyzerBlocks={analyzerBlocks}
					datasets={datasets}
					toolchain={tc}
					saveFunc={saveFunc}
					environments={envs}
					updateFunc={updateFunc}
				/>
			);

			expect(wrapper.props().data).to.have.property('name', expName);
			expect(wrapper.props().data.contents).to.have.deep.property('blocks', {
				'echo': {
					'inputs': {
						'in_data': 'in'
					},
					'algorithm': 'user/integers_add/1',
					'outputs': {
						'out_data': 'out'
					},
					parameters: {}
				}
			});

			wrapper.find('svg #block_echo').simulate('click');
			wrapper.update();
			wrapper.find('.algorithm button.btn-secondary').simulate('click');
			expect(updateFunc.callCount).to.equal(1);
			expect(wrapper.props().data.contents).to.have.deep.property('blocks',
				{ echo: {
					inputs: {},
					algorithm: '',
					outputs: {},
					parameters: {}
				}}
			);

			wrapper.find('svg #block_analysis').simulate('click');
			wrapper.update();
			wrapper.find('.algorithm button.btn-secondary').simulate('click');
			expect(updateFunc.callCount).to.equal(2);
			expect(wrapper.props().data.contents).to.have.deep.property('analyzers',
				{ analysis: {
					inputs: {},
					algorithm: '',
					parameters: {}
				}}
			);

			expect(wrapper.props().data).to.be.deep.equal({
				'name': 'user/user/single/1/single_add',
				'contents': {
					description: '',
					'analyzers': {
						'analysis': {
							inputs: {},
							algorithm: '',
							parameters: {}
						}
					},
					'datasets': {
						'set': {
							'set': 'set',
							'protocol': 'protocol',
							'database': 'simple/1'
						}
					},
					'blocks': {
						'echo': {
							algorithm: '',
							inputs: {},
							outputs: {},
							parameters: {}
						}
					},
					'globals': {
						'queue': 'queue',
						'environment': {
							'version': '1.2.0',
							'name': 'Python 2.7'
						},
					}
				}
			});
		});

		it(`doesnt change the lockMap after an algorithm or dataset is assigned`, () => {
			const expName = 'user/user/single/1/single_add';
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			const tc = testTcs.find(tc => expName.includes(tc.name));
			wrapper = mount(
				<C
					data={getValidObj({name: expName, contents: {}}, tc, [...normalBlocks, ...analyzerBlocks])}
					experiments={[]}
					normalBlocks={normalBlocks}
					analyzerBlocks={analyzerBlocks}
					datasets={datasets}
					toolchain={tc}
					saveFunc={saveFunc}
					environments={envs}
					updateFunc={updateFunc}
				/>
			);

			expect(wrapper.props().data).to.have.property('name', expName);

			//console.log('finished name change, doing dataset');

			wrapper.find('svg #block_set').simulate('click');
			wrapper.find('div.dataset0 select').simulate('change', { target: { value: 'protocol/set (simple/1)'}});
			expect(updateFunc.callCount).to.equal(1);
			expect(wrapper.props().data.contents).to.have.deep.property('datasets', {
				'set': {
					'set': 'set',
					'protocol': 'protocol',
					'database': 'simple/1'
				}
			});

			//console.log('finished dataset, doing block');

			wrapper.find('svg #block_echo').simulate('click');
			expect(wrapper.find({ name: 'echo', set: 'blocks'}).find('.tcBlockBackground').prop('className')).to.include('highlighted');
			wrapper.find('div.block0 div.algorithm select').at(0).simulate('change', { target: { value: 'user/integers_add/1'}});
			expect(updateFunc.callCount).to.equal(2);
			expect(wrapper.props().data.contents).to.have.deep.property('blocks', {
				'echo': {
					'inputs': {
						'in_data': 'in'
					},
					'algorithm': 'user/integers_add/1',
					'outputs': {
						'out_data': 'out'
					},
					parameters: {}
				}
			});

			//console.log('finished block, doing analyzer');

			wrapper.find('svg #block_analysis').simulate('click');
			expect(wrapper.find({ name: 'analysis', set: 'analyzers'}).find('.tcBlockBackground').prop('className')).to.include('highlighted');
			wrapper.find('div.block0 div.algorithm select').at(0).simulate('change', { target: { value: 'user/integers_echo_analyzer/1'}});
			expect(updateFunc.callCount).to.equal(3);
			expect(wrapper.props().data.contents).to.have.deep.property('analyzers', {
				'analysis': {
					'inputs': {
						'in_data': 'in'
					},
					'algorithm': 'user/integers_echo_analyzer/1',
					parameters: {},
				}
			});

			expect(wrapper.state().lockMap).to.deep.equal({
				set: false,
				echo: false,
				analysis: false,
			});
		});
	});
});
