import {useEffect, useRef, useState, Fragment} from "@wordpress/element";
import {__} from "@wordpress/i18n";
import Input from "../../../../fields/input";
import Checkbox from "../../../../fields/checkbox";
import Tooltip from "../../../../fields/tooltip";
import {CreateConfig, DeleteConfig, UpdateConfig} from "../../../../../rest/configs";
import {getInitialData, getSettingsDataToUpdate} from "../../../../../helpers";
import {handleError, handleSuccess} from "../../../../../helpers/notifications";
import confirm from "../../../../../helpers/confirm";
import PopupContainer from "../../../container";
import {required, validate} from "../../../../../../validations";
import useUnsavedChangesWarning from "../../../../pages/components/unsaved-changes-warning";
import {AI_PROVIDER_API_KEY_FIELD_DESCRIPTION} from "./_data";
import Description from "../../../../sections/description";

export default function AddEditConfig({aiProviderId, added, updated, deleted, close, notifications, configData, showMakeDefault, layoutStyle}) {
    // Actions states
    const [isDataSet, setIsDataSet] = useState(false);
    const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
    const [saving, setSaving] = useState(false);
    const [deleting, setDeleting] = useState(false);
    // Config
    const config = {
        'title': useState(''),
        'description': useState(''),
        'default': useState(0),
    };
    // Config params
    const paramsFields = useRef(LimbChatbot.Hooks.applyFilters('lbaic.admin.page.settings.config.dataToCreate.fields', [
        {
            label: __("API key", 'limb-chatbot'),
            name: 'api_key',
            defaultValue: configData?.params?.api_key || '',
            type: 'input',
            validations: [required],
            description: aiProviderId in AI_PROVIDER_API_KEY_FIELD_DESCRIPTION ? AI_PROVIDER_API_KEY_FIELD_DESCRIPTION[aiProviderId] : null
        }
    ], aiProviderId));
    const params = {
        ...paramsFields.current.reduce((obj, item) => {
            if (item.skipSaving) {
                return obj;
            }
            obj[item.name] = useState(item.defaultValue);
            return obj;
        }, {})
    };
    // Errors
    const errors = {
        'title': useState(false),
        'description': useState(false),
        'default': useState(false),
    };
    const paramsErrors = {
        ...paramsFields.current.reduce((obj, item) => {
            if (item.skipSaving) {
                return obj;
            }
            obj[item.name] = useState(false);
            return obj;
        }, {})
    }
    // Initial data
    const initialData = useRef(getInitialData(config));
    const paramsInitialData = useRef(getInitialData(params));
    // Params fields info
    const [paramsFieldsInfo, setParamsFieldsInfo] = useState({});

    // Check unsaved changes before leaving the page
    useUnsavedChangesWarning(hasUnsavedChanges);

    useEffect(() => {
        // Default values
        if (configData?.title) {
            config['title'][1](configData.title);
        }
        if (configData?.description) {
            config['description'][1](configData.description);
        }
        if (configData?.default) {
            config['default'][1](configData.default);
        }
        // Params
        if (configData?.params) {
            for (const key of Object.keys(params)) {
                if (key in configData.params) {
                    params[key][1](configData.params[key]);
                }
            }
        }
        // Params fields info
        setParamsFieldsInfo(paramsFields.current.reduce((obj, item) => {
            obj[item.name] = {
                ...item
            };
            return obj;
        }, {}));

        setTimeout(() => {
            setIsDataSet(true);
        }, 200);
    }, [configData, aiProviderId]);

    useEffect(() => {
        if (isDataSet) {
            initialData.current = getInitialData(config);
            paramsInitialData.current = getInitialData(params);
        }
    }, [isDataSet]);

    useEffect(() => {
        if (isDataSet) {
            const dataToSave = getSettingsDataToUpdate(config, initialData.current);
            const paramsDataToSave = getSettingsDataToUpdate(params, paramsInitialData.current);
            setHasUnsavedChanges(dataToSave?.length > 0 || paramsDataToSave?.length > 0);
        }
    }, [isDataSet, config, params]);

    /**
     * Validate field
     *
     * @param {string} name Field name
     * @param {any} value Field value
     * @return {boolean}
     */
    const validateField = (name, value) => {
        try {
            let validations = [];

            switch (name) {
                case 'title':
                    validations = [required];
                    break;
                default:
                    break;
            }
            const res = validate(value, validations);
            // Update field errors state
            errors[name][1](!res.valid ? res.message : false);

            return res.valid;
        } catch (e) {
            handleError(e, notifications.set, {
                title: __("Field validation failed.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Something went wrong.", 'limb-chatbot'),
            });
            return false;
        }
    }

    /**
     * Validate field
     *
     * @param {string} name Field name
     * @param {any} value Field value
     * @return {boolean}
     */
    const validateParamField = (name, value) => {
        try {
            let validations = [];

            const fieldInfo = paramsFields.current.find(item => item.name === name);
            if (fieldInfo?.validations?.length) {
                validations = fieldInfo?.validations;
            }
            const res = validate(value, validations);
            // Update field errors state
            paramsErrors[name][1](!res.valid ? res.message : false);

            return res.valid;
        } catch (e) {
            handleError(e, notifications.set, {
                title: __("Field validation failed.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Something went wrong.", 'limb-chatbot'),
            });
            return false;
        }
    }

    /**
     * Save settings
     */
    const save = () => {
        if (saving || deleting || !hasUnsavedChanges) {
            return;
        }
        let hasError = false;
        // Validate main data
        for (const item of Object.keys(errors)) {
            if (!validateField(item, config[item][0])) {
                hasError = true;
            }
        }
        // Validate params
        for (const item of Object.keys(paramsErrors)) {
            if (!validateParamField(item, params[item][0])) {
                hasError = true;
            }
        }
        if (hasError) {
            return;
        }
        setSaving(true);
        const data = LimbChatbot.Hooks.applyFilters(`lbaic.admin.page.settings.config.dataTo${configData?.id ? 'Update' : 'Create'}`, getSettingsDataToUpdate(config, initialData.current), aiProviderId);
        const paramsData = LimbChatbot.Hooks.applyFilters(`lbaic.admin.page.settings.config.params.dataTo${configData?.id ? 'Update' : 'Create'}`, getSettingsDataToUpdate(params, paramsInitialData.current), aiProviderId);
        const reqData = {
            related_to: aiProviderId
        };
        // Main data
        if (data?.length) {
            for (const datum of data) {
                reqData[datum.key] = datum.value;
            }
        }
        // Params data
        if (paramsData.length) {
            reqData.params = {};
            for (const paramsDatum of paramsData) {
                reqData.params[paramsDatum.key] = paramsDatum.value;
            }
        }
        // Send Update or Create request
        const req = configData?.id ? UpdateConfig(LimbChatbot.rest.url, LimbChatbot.rest.nonce, configData.id, reqData) : CreateConfig(LimbChatbot.rest.url, LimbChatbot.rest.nonce, reqData);
        req.then(res => {
            if (res?.id) {
                if (configData?.id) {
                    updated(res);
                } else {
                    added(res);
                }
                handleSuccess(notifications.set, {
                    title: __("Data saved successfully.", 'limb-chatbot'),
                });
                close();
            } else {
                handleError({message: "Invalid response data."}, notifications.set, {
                    title: __("Failed to save API key.", 'limb-chatbot'),
                    description: __("Invalid response data received.", 'limb-chatbot') + ' ' + __("Please try again.", 'limb-chatbot'),
                });
            }
            setSaving(false);
        }).catch(e => {
            setSaving(false);
            handleError(e, notifications.set, {
                title: __("Failed to save changes.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please try again.", 'limb-chatbot'),
            });
        });
    }

    /**
     * Delete config
     */
    const deleteConfig = async () => {
        if (!deleting && !saving && configData?.id) {
            if (await confirm(__("Are you sure you want to delete the key?", 'limb-chatbot'))) {
                setDeleting(true);
                DeleteConfig(LimbChatbot.rest.url, LimbChatbot.rest.nonce, configData.id).then(res => {
                    setDeleting(false);
                    handleSuccess(notifications.set, {
                        title: __("Deletion completed successfully.", 'limb-chatbot'),
                    });
                    deleted(configData.id);
                    close();
                }).catch(e => {
                    setDeleting(false);
                    handleError(e, notifications.set, {
                        title: __("Failed to delete key.", 'limb-chatbot'),
                        description: e.message ? __(e.message, 'limb-chatbot') : __("Please try again.", 'limb-chatbot'),
                    });
                });
            }
        }
    }

    return <PopupContainer
        title={configData?.id ? __("Edit key", 'limb-chatbot') : __("Add new key", 'limb-chatbot')}
        description={__("Fill the following fields", 'limb-chatbot')}
        close={close}
        loading={saving || deleting}
        showLoadingContainer={!isDataSet}
        footer={<>
            <button
                className={`lbaic-settings-button lbaic-settings-button-center lbaic-settings-button-h-40 lbaic-settings-button-primary${saving || deleting || !hasUnsavedChanges ? ' lbaic-settings-button-disabled' : ''}`}
                onClick={save}>
                {saving &&
                    <svg
                        className='lbaic-settings-button-i lbaic-settings-loading-circle lbaic-settings-button-loading-circle'
                        xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                        <use href='#lbaic-settings-circle'/>
                    </svg>}
                <span className='lbaic-settings-button-label'>{__("Save", 'limb-chatbot')}</span>
            </button>
            <button
                className='lbaic-settings-button lbaic-settings-button-center lbaic-settings-button-h-40 lbaic-settings-button-secondary'
                onClick={close}>
                <span className='lbaic-settings-button-label'>{__("Cancel", 'limb-chatbot')}</span>
            </button>
        </>}
        layoutStyle={layoutStyle}
    >
        <div className='lbaic-settings-column'>
            <div className='lbaic-settings-column-in'>
                <Input
                    value={config['title'][0]}
                    setValue={config['title'][1]}
                    placeholder={__("Title", 'limb-chatbot')}
                    validate={(value) => validateField('title', value)}
                    errorMessage={errors['title'][0]}
                />
                <Description>{__("Enter a name to identify this API key", 'limb-chatbot')}</Description>
            </div>
        </div>
        <div className='lbaic-settings-column'>
            <div className='lbaic-settings-column-in'>
                <Input
                    value={config['description'][0]} setValue={config['description'][1]}
                    placeholder={__("Description", 'limb-chatbot')}
                    validate={(value) => validateField('description', value)}
                    errorMessage={errors['description'][0]}
                />
                <Description>{__("Optional description for this API key", 'limb-chatbot')}</Description>
            </div>
        </div>
        {Object.keys(paramsFieldsInfo).map((key) => (
            <div
                key={key}
                className='lbaic-settings-column'
            >
                <div className='lbaic-settings-column-in'>
                    {paramsFieldsInfo[key]?.type === 'render_content' && (
                        paramsFieldsInfo[key]?.content
                    )}
                    {paramsFieldsInfo[key]?.type === 'input' &&
                        <Input value={params[key][0]}
                               setValue={(value) => params[key][1](value)}
                               placeholder={paramsFieldsInfo[key].label}
                               validate={(value) => validateParamField(key, value)}
                               errorMessage={paramsErrors[key][0]}>
                            {paramsFieldsInfo[key]?.tooltip &&
                                <Tooltip label={paramsFieldsInfo[key].tooltip} position="top"/>}
                        </Input>}
                    {paramsFieldsInfo[key]?.type === 'checkbox' &&
                        <Checkbox label={paramsFieldsInfo[key].label}
                                  isChecked={params[key][0]}
                                  toggleValue={() => params[key][1](prevState => +!prevState)}
                                  validate={(value) => validateParamField(key, value)}
                                  errorMessage={paramsErrors[key][0]}/>}
                    {Boolean(paramsFieldsInfo[key]?.description) && (
                        paramsFieldsInfo[key].description
                    )}
                </div>
            </div>
        ))}
        {!!showMakeDefault &&
            <div className='lbaic-settings-column'>
                <div className='lbaic-settings-column-in'>
                    <Checkbox label={__("Make default", 'limb-chatbot')}
                              isChecked={config['default'][0]}
                              toggleValue={() => config['default'][1](+(!config['default'][0]))}
                              validate={(value) => validateField('default', value)}
                              errorMessage={errors['default'][0]}/>
                </div>
            </div>}
    </PopupContainer>
}