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

import { AlgorithmEditor as C } from '.';
import {  getValidAlgorithmObj as getValidObj } from '@helpers/beat';

import testAlgs from '@test/test_algs.json';
import testDfs from '@test/test_dfs.json';
import testLibs from '@test/test_libs.json';

const libs = [];
const dfs = [];

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

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

	describe('accepts', () => {
		const algs = [
			{
				name: 'test/non-analyzer-alg/1',
				contents: {
					language: 'python',
					description: 'A basic algorithm as a sanity test for the AlgorithmEditor',
					groups: [
						{
							name: 'group_1',
							inputs: {
								'input_1': { type: 'test/df/1' },
							},
							outputs: {
								'output_1': { type: 'test/df/2' },
							},
						}
					],
					uses: {
						'library_1': 'a/lib/1',
					},
					parameters: {
						'parameter_1': {
							type: 'string',
							choice: [
								'choice_1',
								'choice_2',
								'choice_3',
							],
							default: 'choice_1',
						}
					},
					// non-analyzer
					splittable: false,
				}

			}
		].concat(testAlgs.map(a => getValidObj(a)));

		algs.forEach(function(alg){
			const saveFunc = () => {};
			const updateFunc = () => {};
			it(`${ alg.name }`, () => {
				wrapper = mount(
					<C
						data={getValidObj(alg)}
						algorithms={algs}
						dataformats={dfs}
						libraries={libs}
						saveFunc={saveFunc}
						updateFunc={updateFunc}
					/>
				);

				expect(wrapper).to.have.props(
					['data', 'algorithms', 'dataformats', 'libraries', 'saveFunc', 'updateFunc']
				).deep.equal(
					[alg, algs, dfs, libs, saveFunc, updateFunc]
				);
			});
		});
	});

	describe('creates', () => {
		it('user/integers_echo/1', () => {
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			const algName = 'user/integers_echo/1';
			wrapper = mount(
				<C
					data={getValidObj({name: algName, contents: {}})}
					algorithms={[]}
					dataformats={testDfs}
					libraries={testLibs}
					saveFunc={saveFunc}
					updateFunc={updateFunc}
				/>
			);

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

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

			wrapper.find('#algSplittable input').prop('onChange')( { target: { checked: true }});
			expect(updateFunc.callCount).to.equal(1);
			expect(wrapper.props().data.contents).to.have.property('splittable', true);

			expect(updateFunc.callCount).to.equal(1);
			expect(wrapper.props().data.contents).to.have.property('groups').with.lengthOf(1);
			expect(wrapper.props().data.contents.groups[0]).to.have.property('inputs');
			expect(wrapper.props().data.contents.groups[0]).to.have.property('outputs');

			expect(wrapper.props().data.contents.groups[0]).to.have.property('name', 'group');

			wrapper.find('button#newInputBtn').simulate('click');
			expect(updateFunc.callCount).to.equal(2);
			wrapper.find('button#newOutputBtn').simulate('click');
			expect(updateFunc.callCount).to.equal(3);

			wrapper.find('.algInput').find('CacheInput').prop('onChange')( { target: { value: 'in_data' }});
			expect(updateFunc.callCount).to.equal(4);
			wrapper.find('.algOutput').find('CacheInput').prop('onChange')( { target: { value: 'out_data' }});
			expect(updateFunc.callCount).to.equal(5);

			const ioType = 'system/integer/1';
			wrapper.find('.algInput select').prop('onChange')( { target: { value: ioType }});
			expect(updateFunc.callCount).to.equal(6);
			wrapper.find('.algOutput select').prop('onChange')( { target: { value: ioType }});
			expect(updateFunc.callCount).to.equal(7);

			expect(wrapper.props().data.contents.groups[0].inputs).to.have.deep.property('in_data', { type: ioType });
			expect(wrapper.props().data.contents.groups[0].outputs).to.have.deep.property('out_data', { type: ioType });

			expect(wrapper.props().data.contents).to.deep.equal({
				description: '',
				'groups': [
					{
						'inputs': {
							'in_data': {
								'type': 'system/integer/1'
							}
						},
						'name': 'group',
						'outputs': {
							'out_data': {
								'type': 'system/integer/1'
							}
						}
					}
				],
				parameters: {},
				uses: {},
				'splittable': true,
				'language': 'python'
			});
		});

		it('user/integers_add/1', () => {
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			const algName = 'user/integers_add/1';
			wrapper = mount(
				<C
					data={getValidObj({name: algName, contents: {}})}
					algorithms={[]}
					dataformats={testDfs}
					libraries={testLibs}
					saveFunc={saveFunc}
					updateFunc={updateFunc}
				/>
			);

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

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

			wrapper.find('#algSplittable input').prop('onChange')( { target: { checked: true }});
			expect(wrapper.props().data.contents).to.have.property('splittable', true);

			expect(wrapper.props().data.contents).to.have.property('groups').with.lengthOf(1);
			expect(wrapper.props().data.contents.groups[0]).to.have.property('inputs');
			expect(wrapper.props().data.contents.groups[0]).to.have.property('outputs');

			expect(wrapper.props().data.contents.groups[0]).to.have.property('name', 'group');

			wrapper.find('button#newInputBtn').simulate('click');
			wrapper.find('button#newOutputBtn').simulate('click');

			wrapper.find('.algInput').find('CacheInput').prop('onChange')( { target: { value: 'in_data' }});
			wrapper.find('.algOutput').find('CacheInput').prop('onChange')( { target: { value: 'out_data' }});

			const ioType = 'system/integer/1';
			wrapper.find('.algInput select').prop('onChange')( { target: { value: ioType }});
			wrapper.find('.algOutput select').prop('onChange')( { target: { value: ioType }});

			expect(wrapper.props().data.contents.groups[0].inputs).to.have.deep.property('in_data', { type: ioType });
			expect(wrapper.props().data.contents.groups[0].outputs).to.have.deep.property('out_data', { type: ioType });

			wrapper.find('a')
			.findWhere(w => {
				const html = w.html();
				return html && html.includes('Parameters');
			})
			.at(0)
			.simulate('click');

			wrapper.find('button#newParameterBtn').simulate('click');

			wrapper.find('CacheInput[placeholder="Parameter name..."]').prop('onChange')( { target: { value: 'offset' }});
			wrapper.find('[tabId="1"] select').prop('onChange')( { target: { value: 'int32' }});
			wrapper.find('input[placeholder="Default"]').simulate('change', { target: { value: '1' }});

			expect(wrapper.props().data.contents.parameters).to.have.deep.property('offset', {
				'default': '1',
				'type': 'int32',
				'description': '',
			});

			wrapper.find('a')
			.findWhere(w => {
				const html = w.html();
				return html && html.includes('Libraries');
			})
			.at(0)
			.simulate('click');

			wrapper.find('button#newLibraryBtn').simulate('click');

			wrapper.find('[tabId="2"] CacheInput[type="text"]').prop('onChange')( { target: { value: 'lib' }});
			wrapper.find('[tabId="2"] select').prop('onChange')( { target: { value: 'user/thelib/1' }});

			expect(wrapper.props().data.contents.uses).to.have.property('lib', 'user/thelib/1');

			expect(wrapper.props().data.contents).to.deep.equal({
				description: '',
				'groups': [
					{
						'inputs': {
							'in_data': {
								'type': 'system/integer/1'
							}
						},
						'name': 'group',
						'outputs': {
							'out_data': {
								'type': 'system/integer/1'
							}
						}
					}
				],
				'parameters': {
					'offset': {
						'default': '1',
						'type': 'int32',
						description: '',
					}
				},
				'uses': {
					'lib': 'user/thelib/1'
				},
				'splittable': true,
				'language': 'python'
			});
		});

		it('user/integers_echo_analyzer/1', () => {
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			const algName = 'user/integer_echo_analyzer/1';
			wrapper = mount(
				<C
					data={getValidObj({name: algName, contents: {}})}
					algorithms={[]}
					dataformats={testDfs}
					libraries={testLibs}
					saveFunc={saveFunc}
					updateFunc={updateFunc}
				/>
			);

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

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

			wrapper.find('#algAnalyzer input').prop('onChange')( { target: { checked: true }});
			expect(wrapper.props().data.contents).to.have.property('results');

			expect(wrapper.props().data.contents).to.have.property('groups').with.lengthOf(1);
			expect(wrapper.props().data.contents.groups[0]).to.have.property('inputs');
			expect(wrapper.props().data.contents.groups[0]).to.not.have.property('outputs');

			expect(wrapper.props().data.contents.groups[0]).to.have.property('name', 'group');

			wrapper.find('button#newInputBtn').simulate('click');

			wrapper.find('.algInput').find('CacheInput').prop('onChange')( { target: { value: 'in_data' }});

			const ioType = 'system/integer/1';
			wrapper.find('.algInput select').prop('onChange')( { target: { value: ioType }});

			expect(wrapper.props().data.contents.groups[0].inputs).to.have.deep.property('in_data', { type: ioType });

			wrapper.find('a')
			.findWhere(w => {
				const html = w.html();
				return html && html.includes('Results');
			})
			.at(0)
			.simulate('click');

			wrapper.find('button#newResultBtn').simulate('click');
			wrapper.find('button#newResultBtn').simulate('click');
			wrapper.find('button#newResultBtn').simulate('click');
			wrapper.find('button#newResultBtn').simulate('click');

			wrapper.find('[tabId="3"] CacheInput[type="text"]').at(0).prop('onChange')( { target: { value: 'roc' }});
			wrapper.find('[tabId="3"] select').at(0).prop('onChange')( { target: { value: 'plot/isoroc/1' }});

			wrapper.find('[tabId="3"] CacheInput[type="text"]').at(1).prop('onChange')( { target: { value: 'plot' }});
			wrapper.find('[tabId="3"] select').at(1).prop('onChange')( { target: { value: 'plot/scatter/1' }});

			wrapper.find('[tabId="3"] CacheInput[type="text"]').at(2).prop('onChange')( { target: { value: 'out_data' }});
			wrapper.find('[tabId="3"] select').at(2).prop('onChange')( { target: { value: 'int32' }});

			wrapper.find('[tabId="3"] CacheInput[type="text"]').at(3).prop('onChange')( { target: { value: 'bar_plot' }});
			wrapper.find('[tabId="3"] select').at(3).prop('onChange')( { target: { value: 'plot/bar/1' }});

			expect(wrapper.props().data.contents.results).to.deep.equal({
				'roc': {
					'type': 'plot/isoroc/1',
					'display': false
				},
				'plot': {
					'type': 'plot/scatter/1',
					'display': false
				},
				'out_data': {
					'type': 'int32',
					'display': false
				},
				'bar_plot': {
					'type': 'plot/bar/1',
					'display': false
				}
			});

			expect(wrapper.props().data.contents).to.deep.equal({
				description: '',
				'groups': [
					{
						'inputs': {
							'in_data': {
								'type': 'system/integer/1'
							}
						},
						'name': 'group'
					}
				],
				parameters: {},
				uses: {},
				'language': 'python',
				'results': {
					'roc': {
						'type': 'plot/isoroc/1',
						'display': false
					},
					'plot': {
						'type': 'plot/scatter/1',
						'display': false
					},
					'out_data': {
						'type': 'int32',
						'display': false
					},
					'bar_plot': {
						'type': 'plot/bar/1',
						'display': false
					}
				}
			});
		});

		it('user/integers_echo_ignore/1', () => {
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			const algName = 'user/integers_echo/1';
			wrapper = mount(
				<C
					data={getValidObj({name: algName, contents: {}})}
					algorithms={[]}
					dataformats={testDfs}
					libraries={testLibs}
					saveFunc={saveFunc}
					updateFunc={updateFunc}
				/>
			);

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

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

			wrapper.find('#algSplittable input').prop('onChange')( { target: { checked: true }});
			expect(wrapper.props().data.contents).to.have.property('splittable', true);

			wrapper.find('button#newGroupBtn').simulate('click');

			expect(wrapper.props().data.contents).to.have.property('groups').with.lengthOf(2);
			expect(wrapper.props().data.contents.groups[0]).to.have.property('inputs');
			expect(wrapper.props().data.contents.groups[0]).to.have.property('outputs');
			expect(wrapper.props().data.contents.groups[1]).to.have.property('inputs');
			expect(wrapper.props().data.contents.groups[1]).to.not.have.property('outputs');

			// Group 1
			expect(wrapper.props().data.contents.groups[0]).to.have.property('name', 'group');

			wrapper.find('#endpoint0 button#newInputBtn').simulate('click');
			wrapper.find('#endpoint0 button#newOutputBtn').simulate('click');

			wrapper.find('#endpoint0 .algInput').find('CacheInput').prop('onChange')( { target: { value: 'in_data' }});
			wrapper.find('#endpoint0 .algOutput').find('CacheInput').prop('onChange')( { target: { value: 'out_data' }});

			const ioType = 'system/integer/1';
			wrapper.find('#endpoint0 .algInput select').prop('onChange')( { target: { value: ioType }});
			wrapper.find('#endpoint0 .algOutput select').prop('onChange')( { target: { value: ioType }});

			expect(wrapper.props().data.contents.groups[0].inputs).to.have.deep.property('in_data', { type: ioType });
			expect(wrapper.props().data.contents.groups[0].outputs).to.have.deep.property('out_data', { type: ioType });

			// Group 2
			expect(wrapper.props().data.contents.groups[1]).to.have.property('name', 'group0');

			wrapper.find('#endpoint1 button#newInputBtn').simulate('click');

			wrapper.find('#endpoint1 .algInput').find('CacheInput').prop('onChange')( { target: { value: 'in_data2' }});
			wrapper.find('#endpoint1 .algInput select').prop('onChange')( { target: { value: ioType }});
			expect(wrapper.props().data.contents.groups[1].inputs).to.have.deep.property('in_data2', { type: ioType });

			expect(wrapper.props().data.contents).to.be.deep.equal({
				description: '',
				'groups': [
					{
						'inputs': {
							'in_data': {
								'type': 'system/integer/1'
							}
						},
						'name': 'group',
						'outputs': {
							'out_data': {
								'type': 'system/integer/1'
							}
						}
					},
					{
						'inputs': {
							'in_data2': {
								'type': 'system/integer/1'
							}
						},
						'name': 'group0'
					}
				],
				parameters: {},
				uses: {},
				'splittable': true,
				'language': 'python'
			});
		});

		it('system/integers_echo_sleep/1', () => {
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			const algName = 'user/integers_echo_sleep/1';
			wrapper = mount(
				<C
					data={getValidObj({name: algName, contents: {}})}
					algorithms={[]}
					dataformats={testDfs}
					libraries={testLibs}
					saveFunc={saveFunc}
					updateFunc={updateFunc}
				/>
			);

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

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

			wrapper.find('#algSplittable input').prop('onChange')( { target: { checked: true }});
			expect(wrapper.props().data.contents).to.have.property('splittable', true);

			expect(wrapper.props().data.contents).to.have.property('groups').with.lengthOf(1);
			expect(wrapper.props().data.contents.groups[0]).to.have.property('inputs');
			expect(wrapper.props().data.contents.groups[0]).to.have.property('outputs');

			expect(wrapper.props().data.contents.groups[0]).to.have.property('name', 'group');

			wrapper.find('button#newInputBtn').simulate('click');
			wrapper.find('button#newOutputBtn').simulate('click');

			wrapper.find('.algInput').find('CacheInput').prop('onChange')( { target: { value: 'in_data' }});
			wrapper.find('.algOutput').find('CacheInput').prop('onChange')( { target: { value: 'out_data' }});

			const ioType = 'system/integer/1';
			wrapper.find('.algInput select').prop('onChange')( { target: { value: ioType }});
			wrapper.find('.algOutput select').prop('onChange')( { target: { value: ioType }});

			expect(wrapper.props().data.contents.groups[0].inputs).to.have.deep.property('in_data', { type: ioType });
			expect(wrapper.props().data.contents.groups[0].outputs).to.have.deep.property('out_data', { type: ioType });

			wrapper.find('a')
			.findWhere(w => {
				const html = w.html();
				return html && html.includes('Parameters');
			})
			.at(0)
			.simulate('click');

			wrapper.find('button#newParameterBtn').simulate('click');

			wrapper.find('CacheInput[placeholder="Parameter name..."]').prop('onChange')( { target: { value: 'delay' }});
			wrapper.find('[tabId="1"] select').prop('onChange')( { target: { value: 'uint32' }});
			wrapper.find('input[placeholder="Default"]').simulate('change', { target: { value: '30' }});
			wrapper.find('input[placeholder="Description"]').simulate('change', { target: { value: 'Time to sleep in seconds' }});

			expect(wrapper.props().data.contents.parameters).to.have.deep.property('delay', {
				'default': '30',
				'type': 'uint32',
				'description': 'Time to sleep in seconds',
			});

			expect(wrapper.props().data.contents).to.be.deep.equal({
				description: '',
				uses: {},
				'language': 'python',
				'splittable': true,
				'groups': [
					{
						'name': 'group',
						'inputs': {
							'in_data': {
								'type': 'system/integer/1'
							}
						},
						'outputs': {
							'out_data': {
								'type': 'system/integer/1'
							}
						}
					}
				],
				'parameters': {
					'delay': {
						'default': '30',
						'type': 'uint32',
						'description': 'Time to sleep in seconds'
					}
				}
			});
		});

		it('user/integers_mean_analyzer/1', () => {
			const saveFunc = sinon.spy();
			const _updateFunc = (obj) => {
				wrapper.setProps && wrapper.setProps({ data: obj });
			};
			const updateFunc = sinon.spy(_updateFunc);
			const algName = 'user/integers_mean_analyzer/1';
			wrapper = mount(
				<C
					data={getValidObj({name: algName, contents: {}})}
					algorithms={[]}
					dataformats={testDfs}
					libraries={testLibs}
					saveFunc={saveFunc}
					updateFunc={updateFunc}
				/>
			);

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

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

			wrapper.find('#algAnalyzer input').prop('onChange')( { target: { checked: true }});
			expect(wrapper.props().data.contents).to.have.property('results');

			expect(wrapper.props().data.contents).to.have.property('groups').with.lengthOf(1);
			expect(wrapper.props().data.contents.groups[0]).to.have.property('inputs');
			expect(wrapper.props().data.contents.groups[0]).to.not.have.property('outputs');

			expect(wrapper.props().data.contents.groups[0]).to.have.property('name', 'group');

			wrapper.find('button#newInputBtn').simulate('click');

			wrapper.find('.algInput').find('CacheInput').prop('onChange')( { target: { value: 'in_data' }});

			const ioType = 'system/integer/1';
			wrapper.find('.algInput select').prop('onChange')( { target: { value: ioType }});

			expect(wrapper.props().data.contents.groups[0].inputs).to.have.deep.property('in_data', { type: ioType });

			wrapper.find('a')
			.findWhere(w => {
				const html = w.html();
				return html && html.includes('Results');
			})
			.at(0)
			.simulate('click');

			wrapper.find('button#newResultBtn').simulate('click');
			wrapper.find('button#newResultBtn').simulate('click');

			wrapper.find('[tabId="3"] CacheInput[type="text"]').at(0).prop('onChange')( { target: { value: 'plot' }});
			wrapper.find('[tabId="3"] select').at(0).prop('onChange')( { target: { value: 'plot/scatter/1' }});

			wrapper.find('[tabId="3"] CacheInput[type="text"]').at(1).prop('onChange')( { target: { value: 'out_data' }});
			wrapper.find('[tabId="3"] select').at(1).prop('onChange')( { target: { value: 'float32' }});

			expect(wrapper.props().data.contents.results).to.deep.equal({
				'plot': {
					'type': 'plot/scatter/1',
					'display': false,
				},
				'out_data': {
					'type': 'float32',
					'display': false
				},
			});

			expect(wrapper.props().data.contents).to.deep.equal({
				description: '',
				uses: {},
				parameters: {},
				'language': 'python',
				'groups': [
					{
						name: 'group',
						'inputs': {
							'in_data': {
								'type': 'system/integer/1'
							}
						}
					}
				],
				'results': {
					'out_data': {
						'type': 'float32',
						'display': false
					},
					'plot': {
						'type': 'plot/scatter/1',
						'display': false
					}
				}
			});
		});
	});

});
