import {create} from 'zustand';
import {produce} from 'immer';
import * as rsssl_api from "../utils/api";
import sleeper from "../utils/sleeper.js";
import {__} from '@wordpress/i18n';
import {dispatch} from '@wordpress/data';

const fetchFields = () => {
    return rsssl_api.getFields().then((response) => {
        let fields = response.fields;
        let progress = response.progress;
        let error = response.error;
        return {fields, progress, error};
    }).catch((error) => {
        console.error(error);
    });
}

const useFields = create(( set, get ) => ({
    fieldsLoaded: false,
    error:false,
    fields: [],
    changedFields:[],
    progress:[],
    nextButtonDisabled:false,
    refreshTests:false,
    highLightField: '',
    setHighLightField: (highLightField) => {
        set(state => ({ highLightField }))
    },

    setRefreshTests: (refreshTests) => set(state => ({ refreshTests })),
    handleNextButtonDisabled: (nextButtonDisabled) => set(state => ({ nextButtonDisabled })),
    setChangedField: (id, value) => {
        set(
            produce((state) => {
                //remove current reference
                const existingFieldIndex = state.changedFields.findIndex(field => {
                    return field.id===id;
                });

                if (existingFieldIndex!==-1){
                    state.changedFields.splice(existingFieldIndex, 1);
                }

                //add again, with new value
                let field = {};
                field.id = id;
                field.value = value;
                state.changedFields.push(field);
            })
        )
    },
    showSavedSettingsNotice : (text) => {
        handleShowSavedSettingsNotice(text);
    },

    updateField: (id, value) => {
        set(
            produce((state) => {
                let index = state.fields.findIndex(fieldItem => fieldItem.id === id);
                if (index !== -1) {
                    state.fields[index].value = value;
                }
            })
        )
    },
    updateFieldAttribute: (id, attribute, value) => {
        set(
            produce((state) => {
                let index = state.fields.findIndex(fieldItem => fieldItem.id === id);
                if (index !== -1) {
                    state.fields[index][attribute] = value;
                }
            })
        )
    },
    updateSubField: (id, subItemId, value) => {
        set(
            produce((state) => {
                let index = state.fields.findIndex(fieldItem => fieldItem.id === id);
                let itemValue = state.fields[index].value;
                if (!Array.isArray(itemValue)) {
                    itemValue = [];
                }

                let subIndex = itemValue.findIndex(subItem => subItem.id === subItemId);
                if (subIndex !== -1) {
                    state.fields[index].updateItemId = subItemId;
                    state.fields[index].value[subIndex]['value'] = value;
                    state.fields[index].value = itemValue.map(item => {
                        const { deleteControl, valueControl, statusControl, ...rest } = item;
                        return rest;
                    });
                }
            })
        )
    },
    removeHelpNotice: (id) => {
        set(
            produce((state) => {
                const fieldIndex = state.fields.findIndex(field => {
                    return field.id===id;
                });
                state.fields[fieldIndex].help = false;
            })
        )
    },
    addHelpNotice : (id, label, text, title, url) => {
        get().removeHelpNotice(id);
        //create help object

        let help = {};
        help.label=label;
        help.text=text;
        if (url) help.url=url;
        if (title) help.title=title;
        set(
            produce((state) => {
                const fieldIndex = state.fields.findIndex(field => {
                    return field.id===id;
                });
                if (fieldIndex!==-1) {
                    state.fields[fieldIndex].help = help;
                }
            })
        )
    },
    fieldAlreadyEnabled: (id) => {
        let fieldIsChanged = get().changedFields.filter(field => field.id === id ).length>0;
        let fieldIsEnabled = get().getFieldValue(id);
        return !fieldIsChanged && fieldIsEnabled;
    },
    getFieldValue : (id) => {
        let fields = get().fields;
        let fieldItem = fields.filter(field => field.id === id )[0];
        if (fieldItem){
            return fieldItem.value;
        }
        return false;
    },
    getField : (id) => {
        let fields = get().fields;
        let fieldItem = fields.filter(field => field.id === id )[0];
        if (fieldItem){
            return fieldItem;
        }
        return false;
    },
    saveFields: (skipRefreshTests, showSavedNotice) => {
        let refreshTests = typeof skipRefreshTests !=='undefined' ? skipRefreshTests : true;
        showSavedNotice = typeof showSavedNotice !=='undefined' ? showSavedNotice : true;
        let fields = get().fields;
        fields = fields.filter( field => field.data_target !== 'banner');
        let changedFields = get().changedFields;
        let progress = get().progress;
        let saveFields = [];
        //data_target
        for ( const field of fields ){
            let fieldIsIncluded = changedFields.filter( changedField => changedField.id===field.id ).length>0;
            //also check if there's no saved value yet for radio fields, by checking the never_saved attribute.
            //a radio or select field looks like it's completed, but won't save if it isn't changed.
            //this should not be the case for disabled fields, as these fields often are enabled server side because they're enabled outside Really Simple SSL.
            let select_or_radio = field.type==='select' || field.type==='radio';
            if ( fieldIsIncluded || (field.never_saved && !field.disabled && select_or_radio) ){
                saveFields.push(field);
            }
        }

        //if no fields were changed, do nothing.
        if ( saveFields.length>0 ) {
            rsssl_api.setFields(saveFields).then(( response ) => {
                progress = response.progress;
                fields = response.fields;
                set(
                    produce((state) => {
                        state.changedFields = [];
                        state.fields = fields;
                        state.progress = progress;
                        state.refreshTests = refreshTests;
                    })
                )
            });
        }
        if (showSavedNotice) {
            handleShowSavedSettingsNotice();
        }

    },

    updateFieldsData: (selectedSubMenuItem) => {
        let fields = get().fields;
        fields = updateFieldsListWithConditions(fields);
        const nextButtonDisabled = isNextButtonDisabled(fields, selectedSubMenuItem);

        set(
            produce((state) => {
                state.fields = fields;
                state.nextButtonDisabled = nextButtonDisabled;
            })
        )
    },
    fetchFieldsData: async ( selectedSubMenuItem ) => {
        const { fields, progress, error }   = await fetchFields();
        let conditionallyEnabledFields = updateFieldsListWithConditions(fields);
        let selectedFields = conditionallyEnabledFields.filter(field => field.menu_id === selectedSubMenuItem);
        set({fieldsLoaded: true, fields:conditionallyEnabledFields, selectedFields:selectedFields, progress:progress, error: error });
    }
}));

export default useFields;

//check if all required fields have been enabled. If so, enable save/continue button
const isNextButtonDisabled = (fields, selectedMenuItem) => {
    let fieldsOnPage = [];
    //get all fields with group_id this.props.group_id
    for (const field of fields){
        if (field.menu_id === selectedMenuItem ){
            fieldsOnPage.push(field);
        }
    }

    let requiredFields = fieldsOnPage.filter(field => field.required && !field.conditionallyDisabled && (field.value.length==0 || !field.value) );
    return requiredFields.length > 0;
}

const updateFieldsListWithConditions = (fields) => {
    let newFields = [];
    if (!fields || !Array.isArray(fields)) {
        return [];
    }
    fields.forEach(function(field, i) {
        let enabled = !( field.hasOwnProperty('react_conditions') && !validateConditions(field.react_conditions, fields, field.id) );
        let previouslyEnabled = !field.conditionallyDisabled;
        //we want to update the changed fields if this field has just become visible. Otherwise the new field won't get saved.
        const newField = {...field};
        newField.conditionallyDisabled = !enabled;
        newField.visible = !(!enabled && (newField.type === 'letsencrypt' || newField.condition_action === 'hide'));

        newFields.push(newField);
        //if this is a learning mode field, do not add it to the changed fields list
        if ( !previouslyEnabled && newField.enabled && field.type!=='learningmode') {
            set().setChangedField(field.id, field.value);
        }
    });
    return newFields;
}

const handleShowSavedSettingsNotice = (text) => {
    if (typeof text === 'undefined') {
        text = __( 'Settings Saved', 'really-simple-ssl' );
    }
    dispatch('core/notices').createNotice(
        'success',
        text,
        {
            __unstableHTML: true,
            id: 'rsssl_settings_saved',
            type: 'snackbar',
            isDismissible: false,
        }
    ).then(sleeper(2000)).then(( response ) => {
        dispatch('core/notices').removeNotice('rsssl_settings_saved');
    });
}



const validateConditions = (conditions, fields, fieldId) => {
    let relation = conditions[0].relation === 'OR' ? 'OR' : 'AND';
    let conditionApplies = relation==='AND';
    for (const key in conditions) {
        if ( conditions.hasOwnProperty(key) ) {
            let thisConditionApplies = relation==='AND';
            let subConditionsArray = conditions[key];
            if ( subConditionsArray.hasOwnProperty('relation') ) {
                thisConditionApplies = validateConditions(subConditionsArray, fields, fieldId)
            } else {
                for ( let conditionField in subConditionsArray ) {
                    let invert = conditionField.indexOf('!')===0;
                    if ( subConditionsArray.hasOwnProperty(conditionField) ) {
                        let conditionValue = subConditionsArray[conditionField];
                        conditionField = conditionField.replace('!','');
                        let conditionFields = fields.filter(field => field.id === conditionField);
                        if ( conditionFields.hasOwnProperty(0) ){
                            let field = conditionFields[0];
                            let actualValue = field.value;
                            if ( field.type==='text_checkbox' ) {
                                thisConditionApplies = actualValue.hasOwnProperty('show') && actualValue['show'] == conditionValue;//can be 1/true or 0/false
                            } else if ( field.type==='checkbox' ) {
                                thisConditionApplies = actualValue == conditionValue;//can be 1/true or 0/false
                            } else if ( field.type==='multicheckbox' ) {
                                //multicheckbox conditions
                                //loop through objects
                                thisConditionApplies = false;
                                let arrayValue = actualValue;
                                if ( arrayValue.length===0 ) {
                                    thisConditionApplies = false;
                                } else {
                                    for (const key of Object.keys(arrayValue)) {
                                        if ( !Array.isArray(conditionValue) ) conditionValue = [conditionValue];
                                        if ( conditionValue.includes(arrayValue[key])){
                                            thisConditionApplies = true;
                                            break;
                                        }
                                    }
                                }
                            } else if ( field.type==='radio' ) {
                                //as the regions field can be both radio and multicheckbox, an array is possible for a radio field
                                if ( Array.isArray(conditionValue) ) {
                                    thisConditionApplies = conditionValue.includes(actualValue);
                                } else {
                                    thisConditionApplies = conditionValue === actualValue;
                                }

                            } else {
                                if (conditionValue === true ) {
                                    thisConditionApplies = actualValue===1 || actualValue === "1" || actualValue === true;
                                } else if (conditionValue === false ) {
                                    thisConditionApplies = actualValue === 0 || actualValue === "0" || actualValue === false;
                                } else if (conditionValue.indexOf('EMPTY')!==-1) {
                                    thisConditionApplies = actualValue.length === 0;
                                } else {
                                    thisConditionApplies = String(actualValue).toLowerCase() === conditionValue.toLowerCase();
                                }
                            }
                        }
                    }
                    if ( invert ){
                        thisConditionApplies = !thisConditionApplies;
                    }
                    if ( relation === 'AND' ) {
                        conditionApplies = conditionApplies && thisConditionApplies;
                    } else {
                        conditionApplies = conditionApplies || thisConditionApplies;
                    }
                }

                if ( relation === 'AND' ) {
                    conditionApplies = conditionApplies && thisConditionApplies;
                } else {
                    conditionApplies = conditionApplies || thisConditionApplies;
                }
            }
        }
    }

    return conditionApplies ? 1 : 0;
}
