import React from 'react';
import SearchTermInput from '../components/search/SearchTermInput';
import NISVCatalogueConfig from '../collection/mappings/NISVCatalogueConfig';
import IDUtil from '../util/IDUtil';
import ExternalAPI from "../api/ExternalAPI";
import {shallow} from 'enzyme';
import {mount} from 'enzyme';
import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({adapter: new Adapter()});
import each from 'jest-each';

// ---------------------------------------------- TESTS -------------------------------------

describe("Component: SearchTermInput", () => {
	it("test: basic render", () => {
        let collectionConfig = new NISVCatalogueConfig();

        const searchTermInput = shallow(<SearchTermInput
                                        fieldCategories = {[]}
                                        term = ""
                                        entities = {[]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);
		expect(searchTermInput).toBeDefined();
		expect(searchTermInput).toBeTruthy();
	});

    each([
    [[{"value":"discussed-person"}]],
    [[{"value":"guest-person"}]],
    [[{"value":"production-person"}]],
    [[{"value":"discussed-person"}, {"value":"guest-person"}]],
    [[{"value":"discussed-person"}, {"value":"descriptions"}]],
    [[{"value":"titles"}, {"value":"guest-person"}]]
    ]).it( 'Opens the autocomplete for %s', (fieldCategory) => {
            let collectionConfig = new NISVCatalogueConfig();

			let searchTermInput = mount(<SearchTermInput
                                        fieldCategories = {fieldCategory}
                                        term = ""
                                        entities = {[]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);

			const loadSuggestionsSpy = jest.spyOn(searchTermInput.instance(), 'loadSuggestions')
			searchTermInput.find( 'input' ).simulate( 'change', {
				target: { value: 'bouwman'}
			});
			expect(loadSuggestionsSpy).toHaveBeenCalledTimes(1)
		});

/*
        each([
        [[]],
        [[{"value":"descriptions"}]],
        [[{"value":"descriptions"}, {"value":"titles"}]]
        ]).it( 'Does not open the autocomplete for category %s', (fieldCategory) => {
            let collectionConfig = new NISVCatalogueConfig();
			let searchTermInput = mount(
                <SearchTermInput
                    fieldCategories = {fieldCategory}
                    term = ""
                    collectionConfig = {collectionConfig}
                    newSearch = {jest.fn()}
                />);
			const loadSuggestionsSpy = jest.spyOn(searchTermInput.instance(), 'loadSuggestions')
			searchTermInput.find( 'input' ).simulate( 'change', {
				target: { value: 'bouwman'}
			});
			expect(loadSuggestionsSpy).toHaveBeenCalledTimes(0)
		});

*/
		it( 'Does not open the autocomplete for missing autocomplete mapping', () => {

            let collectionConfig = new NISVCatalogueConfig();
            collectionConfig.getEntityConfig = jest.fn().mockImplementation(() => { return{}; });
			let searchTermInput = mount(<SearchTermInput
                                        fieldCategories = {["discussed-person"]}
                                        term = ""
                                        entities = {[]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);

			const loadSuggestionsSpy = jest.spyOn(searchTermInput.instance(), 'loadSuggestions')
			searchTermInput.find( 'input' ).simulate( 'change', {
				target: { value: 'bouwman'}
			});
			expect(loadSuggestionsSpy).toHaveBeenCalledTimes(0)
		});

	    each([
        [[]],
        [[{"value":"descriptions"}]],
        [[{"value":"descriptions"}, {"value":"titles"}]],
        [[{"value":"discussed-person"}]],
        [[{"value":"guest-person"}]],
        [[{"value":"production-person"}]],
        [[{"value":"discussed-person"}, {"value":"guest-person"}]],
        [[{"value":"discussed-person"}, {"value":"descriptions"}]],
        [[{"value":"titles"}, {"value":"guest-person"}]]
        ]).it( 'Clicking search button triggers search %s', (fieldCategory) => {
            let collectionConfig = new NISVCatalogueConfig();

			let searchTermInput = mount(<SearchTermInput
                                        fieldCategories = {fieldCategory}
                                        term = ""
                                        entities = {[]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);

			const newSearchSpy = jest.spyOn(searchTermInput.instance().props, 'newSearch')
			searchTermInput.find( 'i' ).simulate( 'click', {});
			expect(newSearchSpy).toHaveBeenCalledTimes(1)
		});

	    each([
        [[]],
        [[{"value":"descriptions"}]],
        [[{"value":"descriptions"}, {"value":"titles"}]],
        [[{"value":"discussed-person"}]],
        [[{"value":"guest-person"}]],
        [[{"value":"production-person"}]],
        [[{"value":"discussed-person"}, {"value":"guest-person"}]],
        [[{"value":"discussed-person"}, {"value":"descriptions"}]],
        [[{"value":"titles"}, {"value":"guest-person"}]]
        ]).it( 'Pressing enter triggers search %s', (fieldCategory) => {
            let collectionConfig = new NISVCatalogueConfig();

			let searchTermInput = mount(<SearchTermInput
                                        fieldCategories = {fieldCategory}
                                        term = ""
                                        entities = {[]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);
			const newSearchSpy = jest.spyOn(searchTermInput.instance().props, 'newSearch')
			searchTermInput.find('input').simulate('keydown', {key: 'Enter'})

			expect(newSearchSpy).toHaveBeenCalledTimes(1)
		});

		it( 'Search works with missing autocomplete mapping', () => {
            let collectionConfig = new NISVCatalogueConfig();
            collectionConfig.getEntityConfig = jest.fn().mockImplementation(() => { return{}; });
			let searchTermInput = mount(<SearchTermInput
                                        fieldCategories = {[]}
                                        term = ""
                                        entities = {[]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);

			const newSearchSpy = jest.spyOn(searchTermInput.instance().props, 'newSearch')
			searchTermInput.find('input').simulate('keydown', {key: 'Enter'})

			expect(newSearchSpy).toHaveBeenCalledTimes(1)
		});

	    each([
        [[]],
        [[{"value":"descriptions"}]],
        [[{"value":"descriptions"}, {"value":"titles"}]],
        [[{"value":"discussed-person"}]],
        [[{"value":"guest-person"}]],
        [[{"value":"production-person"}]],
        [[{"value":"discussed-person"}, {"value":"guest-person"}]],
        [[{"value":"discussed-person"}, {"value":"descriptions"}]],
        [[{"value":"titles"}, {"value":"guest-person"}]]
        ]).it( 'Choosing an item triggers output %s', (fieldCategory) => {
            let collectionConfig = new NISVCatalogueConfig();

			let searchTermInput = mount(<SearchTermInput
                                        fieldCategories = {fieldCategory}
                                        term = ""
                                        entities = {[]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);
			const newSearchSpy = jest.spyOn(searchTermInput.instance().props, 'onSuggestionOutput')
			searchTermInput.find( 'Autosuggest' ).instance().props.onSuggestionSelected({}, {suggestionValue: "testSuggestion",
			                                                                                    suggestion: {
			                                                                                    value: "testSuggestion",
			                                                                                    label: "testSuggestion | dummyType | dummyNote | otherLabel1;otherLabel2"
			                                                                                    }});

			expect(newSearchSpy).toHaveBeenCalledTimes(1)
		});

		it( 'An autocomplete failure is handled gracefully', () => {
            let collectionConfig = new NISVCatalogueConfig();
            function sleep(milliseconds) {
                const start = new Date().getTime();
                for (let i = 0; i < 1e7; i++) {
                    if ((new Date().getTime() - start) > milliseconds) {
                        break;
                    }
                }
            }
			let searchTermInput = mount(<SearchTermInput
                                        fieldCategories = {[{"value":"guest-person"}]}
                                        term = ""
                                        entities = {[]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);

            const autocomplete = jest.fn().mockImplementation(function (vocabulary, term, conceptScheme, callback) {
                callback("{\"error\": \"ValueError\"}");
                return new XMLHttpRequest();
              });
            ExternalAPI.autocomplete = autocomplete.bind(ExternalAPI);
			const loadSuggestionsSpy = jest.spyOn(searchTermInput.instance(), 'loadSuggestions');

            searchTermInput.find( 'input' ).simulate( 'change', {
				    target: { value: 'duin'}
			    });
			expect(loadSuggestionsSpy).toHaveBeenCalledTimes(1);
			expect(searchTermInput.state('suggestions')).toEqual([]);

		});

			    each([
        [[]],
        [[{"value":"descriptions"}]],
        [[{"value":"descriptions"}, {"value":"titles"}]],
        [[{"value":"discussed-person"}]],
        [[{"value":"guest-person"}]],
        [[{"value":"production-person"}]],
        [[{"value":"discussed-person"}, {"value":"guest-person"}]],
        [[{"value":"discussed-person"}, {"value":"descriptions"}]],
        [[{"value":"titles"}, {"value":"guest-person"}]]
        ]).it( 'Choosing a suggested item adds an entity %s', (fieldCategory) => {
            let collectionConfig = new NISVCatalogueConfig();

			let searchTermInput = mount(<SearchTermInput
                                        fieldCategories = {fieldCategory}
                                        term = ""
                                        entities = {[]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);
			const onSuggestionOutputSpy = jest.spyOn(searchTermInput.instance().props, 'onSuggestionOutput');
			const newSearchSpy = jest.spyOn(searchTermInput.instance().props, 'newSearch')
			searchTermInput.find( 'Autosuggest' ).instance().props.onSuggestionSelected({}, {suggestionValue: "testSuggestion",
			                                                                                    suggestion: {
			                                                                                    value: "testSuggestion",
			                                                                                    label: "testSuggestion | dummyType | dummyNote | otherLabel1;otherLabel"
			                                                                                    }});
			expect(onSuggestionOutputSpy).toHaveBeenCalledTimes(1); //should call the suggestion output function
			expect(newSearchSpy).toHaveBeenCalledTimes(0); //should NOT call the new search
			expect(searchTermInput.state('entities')).toEqual([{
                                                                id: "testSuggestion",
                                                                label: "testSuggestion",
                                                                type: "dummyType",
                                                                otherLabels: "otherLabel1;otherLabel"
                                                                }]);
            });

			    each([
        [[]],
        [[{"value":"descriptions"}]],
        [[{"value":"descriptions"}, {"value":"titles"}]],
        [[{"value":"discussed-person"}]],
        [[{"value":"guest-person"}]],
        [[{"value":"production-person"}]],
        [[{"value":"discussed-person"}, {"value":"guest-person"}]],
        [[{"value":"discussed-person"}, {"value":"descriptions"}]],
        [[{"value":"titles"}, {"value":"guest-person"}]]
        ]).it( 'Deleting a suggested item removes an entity %s', (fieldCategory) => {
            let collectionConfig = new NISVCatalogueConfig();

            let testEntity = {
                            id: "testSuggestion",
                            label: "testSuggestion",
                            type: "dummyType",
                            otherLabels: "otherLabel1;otherLabel"
                            }

			let searchTermInput = mount(<SearchTermInput
                                        fieldCategories = {fieldCategory}
                                        term = ""
                                        entities = {[testEntity]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);
			const onSuggestionOutputSpy = jest.spyOn(searchTermInput.instance().props, 'onSuggestionOutput');
			const newSearchSpy = jest.spyOn(searchTermInput.instance().props, 'newSearch')
			searchTermInput.find( 'Entity' ).instance().props.delete(testEntity);
			expect(onSuggestionOutputSpy).toHaveBeenCalledTimes(1);  //should call the suggestion output function
			expect(newSearchSpy).toHaveBeenCalledTimes(0); //should NOT call the new search
			expect(searchTermInput.state('entities')).toEqual([]);

		});

			    each([
        [[]],
        [[{"value":"descriptions"}]],
        [[{"value":"descriptions"}, {"value":"titles"}]],
        [[{"value":"discussed-person"}]],
        [[{"value":"guest-person"}]],
        [[{"value":"production-person"}]],
        [[{"value":"discussed-person"}, {"value":"guest-person"}]],
        [[{"value":"discussed-person"}, {"value":"descriptions"}]],
        [[{"value":"titles"}, {"value":"guest-person"}]]
        ]).it( 'Entities are displayed as tags %s', (fieldCategory) => {
            let collectionConfig = new NISVCatalogueConfig();

            let testEntity1 = {
                            id: "testSuggestion",
                            label: "testSuggestion",
                            type: "dummyType",
                            otherLabels: "otherLabel1;otherLabel"
                            }

            let testEntity2 = {
                            id: "testSuggestion2",
                            label: "testSuggestion2",
                            type: "dummyType2",
                            otherLabels: "otherLabel1;otherLabel"
                            }

			let searchTermInput = mount(<SearchTermInput
                                        fieldCategories = {fieldCategory}
                                        term = ""
                                        entities = {[testEntity1, testEntity2]}
                                        collectionConfig = {collectionConfig}
                                        newSearch = {jest.fn()}
                                        onSuggestionOutput = {jest.fn()}
                                        />);

			expect(searchTermInput.state('entities')).toEqual([testEntity1, testEntity2]);
			let firstNode = searchTermInput.find( 'Entity' ).at(0);
			let secondNode = searchTermInput.find( 'Entity' ).at(1);
			expect(firstNode.props()["entity"]).toEqual(testEntity1);
			expect(secondNode.props()["entity"]).toEqual(testEntity2);

		});
	});

