class Select {

    constructor( container ) {

        this.container = container;

        this.init();        
        this.reset();        
    }

   
    
    init() {

        this.container.style.display = 'none';
        this.container.insertAdjacentHTML('afterbegin', '<option value="" style="color:red">Seleziona un elemento</option>') ;       
        this.updateItems();
    }

    updateItems(data = []) {

        this.items = [].map.call( this.container.querySelectorAll('option'), item => { item.visible = true; return item } );
    }

    reset() {

        this.container.selectedIndex = 0;
    }

    getVisibles() {

        return this.items.filter( item => item.visible );
    }

    getIndexByValue( value ){

        let options = [].map.call( this.container.options, i => i );

        let results = options.filter( i => i.value == value );

        if( results.length == 0 ) return 0;
        
        let selectedOption =  results.shift(); 

        return options.indexOf( selectedOption );
    }


    populate( data ) {
            
            this.container.innerHTML = '';
    
            data.forEach( item => {
                let option = document.createElement('option');
                option.value = item.value;
                option.textContent = item.textContent;
                option.setAttribute('data-payload', JSON.stringify(item.payload));
                this.container.appendChild( option );
            });
    }
    
}




class Input {

    constructor( container, select, selectionCallback , apicall = '' ) {

        this.container         = container;
        this.select            = select;
        this.selectionCallback = selectionCallback;
        this.api_call          = apicall;
        this.dependencies      = [];

        this.filterResults = this.filterResults.bind(this);
        this.selection     = this.selection.bind(this);

        this.container.addEventListener('keyup' , this.filterResults );

        this.results = this.initResults();
        this.autocomplete();
    }




    initResults() {

        let results = document.createElement('div');
        results.id = "results";

        let ul = document.createElement('ul');
        
        results.insertAdjacentElement( 'beforeend', ul );

        this.container.insertAdjacentElement( 'afterend', results );

        return ul;
    }
    
    reset() {

        this.results.innerHTML = '';
    }

    async filterResults( evt ) {

        let filterText = this.container.value.length < 2 ?  '': this.container.value ;
        
        this.select.reset();        
        let data = await this.fetchData( filterText );
        this.select.populate( data.map((elem) => ({value: elem.id, textContent: elem.value, payload: elem })));

        this.autocomplete();
    }

    async fetchData(searchText = '') {
        if(searchText.length < 2) return [];    
        let api_call = (this.dependencies.length ? this.getApiCallWithDependencyValues() : this.api_call) +encodeURIComponent('?s='+encodeURIComponent(searchText));

        let data = fetch( `/wp-json/api/v1/proxy/${api_call}/` )
        .then( response => response.json() )
        .then( data => {

            let options = data ? data : [];

            // Bisogna controllare perché questa conversione è necessaria
            if ( !Array.isArray(options) ) { 

                options = Object.keys(options).map(key => options[key]).sort((a, b) => a.length - b.length);
            }

            return options;
        });

        return data;
    }

    getApiCallWithDependencyValues() {

        if( this.missingDependency() ) return '';

        return this.dependencies.reduce( (api_call, dependency) => api_call.replace( dependency.valueField, dependency.dropdown.value() ), this.api_call ); 
    }

    missingDependency() {
    
        return this.dependencies.some( dependency => !dependency.dropdown.value() );
    }

    initRow( option ){

        let row = document.createElement('li');
        row.className='autocomplete-row';
        row.textContent = option.textContent;
        row.dataset.value = option.value;

        row.addEventListener( 'click', this.selection );

        return row;
        
    }

    autocomplete() {

        let visibles = this.select.getVisibles();

        this.reset();

        visibles.forEach( option => {

            this.results.appendChild( this.initRow( option ) );
        });
    }


    selection( event ) {

        // this.close();        
        this.select.container.selectedIndex = this.select.getIndexByValue( event.target.dataset.value );
        this.selectionCallback();
    }

}

class ComboSearchLive {

    constructor( selectElement, apicall = '', callback = null ){
        this.select     = new Select( selectElement.container );

        this.container  = this.render();

        this.callback   = callback != null ? callback : () => {};
        
        this.selected   = this.container.querySelector('.selected-element');        
        this.searchbox  = this.container.querySelector('.search-container');  

        this.input      = new Input( this.container.querySelector('input'), this.select , this.choice.bind(this) , apicall );

        selectElement.dependencies = [];

        this.toggle  = this.toggle.bind(this);
        
        this.isOpen = false;
        this.selected.addEventListener('click', this.toggle );
        
        this.choice();
        this.observe();
        
    }
    
    dependsOn( valueField, dropdown ) {

        /// aggiunge la dipendenza dalla dropdown
        this.input.dependencies.push( { valueField , dropdown } );
    }
    
    updateItems() {

        this.select.updateItems();
        this.input.autocomplete();        
    }


    observe() {

        // Select the node that will be observed for mutations
        const targetNode = this.select.container;

        // Options for the observer (which mutations to observe)
        const config = { attributes: false, childList: true, subtree: false };

        // Callback function to execute when mutations are observed
        const callback = ( mutationList, observer ) => {
            
            for (const mutation of mutationList) {
                if (mutation.type === "childList") {
                    
                    this.updateItems();
                }
            }
        };

        // Create an observer instance linked to the callback function
        const observer = new MutationObserver(callback);

        // Start observing the target node for configured mutations
        observer.observe(targetNode, config);
    }
    
    choice() {

        this.selected.innerHTML = this.select.container.selectedOptions[0].textContent;
        this.callback();
        this.close();
    }


    toggle( event ) {

        this.isOpen = !this.isOpen;
        
        if( this.isOpen ) this.open( event );
        else              this.close( event );
    } 

    open( event ) { 

        this.selected.classList.add('up');
        this.searchbox.classList.remove('hidden');
    }

    close(){
        
        this.searchbox.classList.add('hidden');
        this.selected.classList.remove('up');
    }

    render() {

        let selectContainer = document.createElement('div');

        selectContainer.className = 'select-container';

        selectContainer.insertAdjacentHTML('afterbegin', this.markup() );
        
        this.select.container.insertAdjacentElement('afterend', selectContainer );
        
        selectContainer.insertAdjacentHTML('afterend',   this.style()  );

        return selectContainer;
    }


    markup() {

        return `          
            <div class="selected-element"></div>
            <div class="search-container hidden">
                <input type="text" placeholder="cosa stai cercando?" value="">
            </div>            
        `;
    }

    style() {

        return `
            <style>
            .hidden { display:none }

            .selected-element {
                border-radius: 5px;
                border: 1px solid lightgrey;
                padding: 6px 0 2px 6px;
                position: relative;                    
            }

            .select-container {
                border-radius: 5px;
                font: 14px/1em sans-serif;
                width: 90%;
                margin: 0 auto;
                background: white;
                font-size: 14px;
            }
            
            .search-container {
                border-radius: 0 0 5px 5px;
                border: 1px solid lightgrey;
                font: 12px/1em sans-serif;
                padding: 5px;
                position: relative;
            }

            .search-container input {
                line-height: 1.2em;
                width: 100%;
                border: 1px solid lightgrey;
                border-color: transparent transparent #afafaf transparent;
                outline: none;
                margin: 0px 0 10px 0;
                margin: 0 0 10px 0;
                padding: 5px 0 10px 0;
            }

        
            .selected-element:after {
                position: absolute;
                content: "";
                top: 9px;
                right: 10px;
                width: 0;
                height: 0;
                border: 6px solid #8d8d8d;
                border-color: #505050 transparent transparent transparent;
                border-radius: 2px;
            }
            
            .selected-element.up:after {
                border-color: transparent transparent #505050 transparent;
                top: 3px;
            }

            
            #results {

                background-color: #f8f8f8;
                border-radius: 0 0 5px 5px;
                overflow-y: scroll;
                height: 10em;
            }
            #results ul {
                list-style: none;   
                margin:0;             
                padding:0;           
            }

            #results ul li {
                margin:3px 5px;
                padding:1px 3px;
            }

            #results ul li:hover {
                background:lightgrey;
                cursor: pointer;
                
            }

            </style>
        `;
        
    }

}
