import {forwardRef, useEffect, useImperativeHandle, useRef, useState} from "@wordpress/element";
import {__} from "@wordpress/i18n";
import Dropdown from "../../dropdown";
import {GetConfig, GetConfigs, DeleteConfig} from "../../../../rest/configs";
import {handleError, handleSuccess} from "../../../../helpers/notifications";
import {getProviderFromConfigRelation} from "../../../../helpers";
import confirm from "../../../../helpers/confirm";
import AddEditConfig from "../../../popups/ai-providers/models/add-edit-config";

/**
 * Mask string
 *
 * @param {string} text Text
 * @return {string}
 */
export const maskString = (text) => {
    try {
        if (text?.length <= 6) {
            // If the string is too short, return as is
            return text;
        }

        const first3 = text.slice(0, 3);
        const last3 = text.slice(-3);
        const middle = '*'.repeat(text.length - 6);

        return first3 + middle + last3;
    } catch (e) {
        handleError(e);
        return '';
    }
}

function Config({
                    aiProviderId,
                    isDataFetched,
                    isDefaultsChecked = true,
                    configId,
                    setConfigId,
                    getReqParams,
                    configSelected,
                    setFetched,
                    fieldProps,
                    autoSelect = true,
                    disabled,
                    setLoading,
                    notifications,
                    ...props
                }, ref) {
    const [loadingConfigs, setLoadingConfigs] = useState(false);
    const [configs, setConfigs] = useState([]);
    const [selectedConfig, setSelectedConfig] = useState(null);
    const [pagination, setPagination] = useState({
        page: 1,
        perPage: 10,
        total: 0,
    });
    const configsFetchedRef = useRef(false);
    const [showEditConfigPopup, setShowEditConfigPopup] = useState(false);
    const [editConfigData, setEditConfigData] = useState(null);

    useImperativeHandle(ref, () => ({
        /**
         * New config added
         *
         * @param {object} newConfig New config
         */
        newConfigAdded(newConfig) {
            setConfigs(prev => [...prev, newConfig]);
            selected(newConfig);
        }
    }));

    const isDataReady = isDataFetched && isDefaultsChecked;
    const dropdownDisabled = disabled || !aiProviderId || loadingConfigs;

    useEffect(() => {
        if (isDataReady) {
            // Clear selected config if it doesn't belong to the new provider
            if (selectedConfig && aiProviderId) {
                const configProviderId = getProviderFromConfigRelation(selectedConfig.related_to);
                if (configProviderId !== aiProviderId) {
                    selected(null);
                }
            }
            // Update configs list
            getConfigs({
                page: 1,
                perPage: pagination.perPage
            }, true);
        }
    }, [isDataReady, aiProviderId]);

    useEffect(() => {
        if (!isDataReady) {
            return;
        }
        // Keep selected config object synced
        if (configId) {
            if (configs.length) {
                const config = configs.find(item => +item.id === +configId);
                if (config?.id) {
                    selected(config, config.id !== selectedConfig?.id);
                } else {
                    if (selectedConfig?.id) {
                        if (selectedConfig.id !== configId) {
                            getSelectedConfig(configId);
                        } else {
                            selected(selectedConfig, false);
                        }
                    } else {
                        getSelectedConfig(configId);
                    }
                }
            } else {
                if (configsFetchedRef.current) {
                    getSelectedConfig(configId);
                }
            }
        } else {
            selected(null)
        }
    }, [isDataReady, configId, configs]);

    /**
     * Get selected AI provider configs
     *
     * @param {object} params Params
     * @param {boolean} reset Reset the list of configs
     * @return {Promise<void>}
     */
    const getConfigs = async (params, reset = false) => {
        if (!aiProviderId) {
            setTimeout(() => {
                typeof setFetched === 'function' && setFetched(true);
            }, 200);
            return;
        }
        setLoadingConfigs(true);
        setLoading(prev => prev + 1);
        // Default params
        const reqParams = {
            related_to: aiProviderId,
            page: params?.page || 1,
            per_page: params?.perPage || 10,
            ...(getReqParams || {})
        };
        // Add search param
        if (params?.search) {
            reqParams.search = params.search;
            reqParams.search_fields = ['title'];
        }
        try {
            const res = await GetConfigs(LimbChatbot.rest.url, LimbChatbot.rest.nonce, reqParams);
            const newConfigs = [...(reset ? [] : configs), ...(res.items?.length ? res.items : [])];
            // Update state
            setConfigs([...newConfigs]);
            // Select config
            if (reset) {
                checkConfigsNewList(newConfigs);
            }
            // Pagination info
            setPagination(prevState => ({
                ...prevState,
                page: reqParams.page,
                total: res.total,
            }));

            configsFetchedRef.current = true;
        } catch (e) {
            handleError(e, notifications.set, {
                title: __("Failed to retrieve API keys data.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please check your connection and try again.", 'limb-chatbot'),
            });
        }
        setTimeout(() => {
            typeof setFetched === 'function' && setFetched(true);
            setLoading(prev => prev - 1);
        }, 200);
        setLoadingConfigs(false);
    }

    /**
     * Check new configs list
     *
     * @param {object[]} configs Configs
     */
    const checkConfigsNewList = (configs) => {
        if (autoSelect) {
            if (configs?.length) {
                // Find by current config id
                const config = configs.find(item => +item.id === +configId);
                if (!config?.id) {
                    const defaultConfig = configs.find(item => item.default);
                    if (defaultConfig?.id) {
                        selected(defaultConfig);
                    } else {
                        selected(configs[0]);
                    }
                } else {
                    // Verify the config belongs to current provider
                    const configProviderId = getProviderFromConfigRelation(config.related_to);
                    if (configProviderId !== aiProviderId) {
                        // Config doesn't belong to current provider, select default or first
                        const defaultConfig = configs.find(item => item.default);
                        if (defaultConfig?.id) {
                            selected(defaultConfig);
                        } else {
                            selected(configs[0]);
                        }
                    }
                }
            } else {
                // Nothing to autoselect
                selected(null);
            }
        } else {
            if (selectedConfig) {
                const configProviderId = getProviderFromConfigRelation(selectedConfig.related_to);
                // If the selected config is for a different AI provider, then clear the field
                if (configProviderId !== aiProviderId) {
                    selected(null);
                } else if (!configs?.length) {
                    // No configs available for this provider, clear selection
                    selected(null);
                }
            } else if (!configs?.length) {
                // No configs available, ensure selection is cleared
                selected(null);
            }
        }
    }

    /**
     * Get saved config
     *
     * @param {number} id Config id
     * @return {Promise<{id}|*|boolean>}
     */
    const getSelectedConfig = async (id) => {
        try {
            const res = await GetConfig(LimbChatbot.rest.url, LimbChatbot.rest.nonce, id, getReqParams || {});
            if (res?.id) {
                selected(res);
                return res;
            }
        } catch (e) {
            handleError(e, notifications.set, {
                title: __("Saved API key not found.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please check your connection and try again.", 'limb-chatbot'),
            });
        }
        return false;
    }

    /**
     * Config selected
     *
     * @param {object} config OpenAI config
     * @param {boolean} validate Validate the value
     */
    const selected = (config, validate = true) => {
        const newConfigId = config?.id || '';
        const hasSelectedConfig = !!selectedConfig;
        // Don't do anything if no real change detected (both are empty and no selectedConfig exists)
        // But allow clearing if we have a selectedConfig and are passing null
        if (!configId && !config?.id && !hasSelectedConfig) {
            return;
        }
        // Always update if:
        // 1. We're setting a new config (config?.id exists)
        // 2. We're clearing and have a selectedConfig (need to clear selectedConfig state)
        // 3. The configId is actually changing
        const isClearing = !config && hasSelectedConfig;
        const isSettingNew = !!config?.id;
        const isChanging = configId !== newConfigId;
        const shouldUpdate = isClearing || isSettingNew || isChanging;
        if (shouldUpdate) {
            setConfigId(newConfigId);
            setSelectedConfig(config);
            if (typeof configSelected === 'function') {
                configSelected(config);
            }
            if (validate && typeof fieldProps?.validate === 'function') {
                fieldProps?.validate(newConfigId);
            }
        }
    }

    /**
     * Handle option edit
     *
     * @param {object} option Option data
     */
    const handleOptionEdit = (option) => {
        const config = option.extra || option;
        if (!config?.id) {
            return;
        }
        // Always mask the API key when editing
        setEditConfigData({
            ...config,
            params: {
                ...(config.params || {}),
                api_key: maskString(config.params?.api_key)
            }
        });
        setShowEditConfigPopup(true);
    }

    /**
     * Config updated callback
     *
     * @param {object} updatedConfig Updated config
     */
    const configUpdated = (updatedConfig) => {
        // Update config in the list
        setConfigs(prev => prev.map(item => +item.id === +updatedConfig.id ? updatedConfig : item));
        
        // If updated config is currently selected, update selection
        if (+updatedConfig.id === +configId) {
            selected(updatedConfig);
        }
        
        setShowEditConfigPopup(false);
        setEditConfigData(null);
    }

    /**
     * Handle option delete
     *
     * @param {object} option Option data
     * @return {Promise<void>}
     */
    const handleOptionDelete = async (option) => {
        const config = option.extra || option;
        if (!config?.id) {
            return;
        }

        // Confirm deletion
        const confirmed = await confirm({
            title: __("Are you sure to delete the API key?", 'limb-chatbot'),
            description: __("All the data related to the key will be removed. If you need the data then please consider updating the key.", 'limb-chatbot'),
            style: {
                '--lbaic-settings-popup-max-width': '320px',
            },
        });

        if (!confirmed) {
            return;
        }

        setLoading(prev => prev + 1);
        try {
            await DeleteConfig(LimbChatbot.rest.url, LimbChatbot.rest.nonce, config.id);

            // Remove from configs list
            const updatedConfigs = configs.filter(item => +item.id !== +config.id);
            setConfigs(updatedConfigs);

            // If deleted config was selected, auto-select another one
            if (+config.id === +configId) {
                if (autoSelect && updatedConfigs.length > 0) {
                    // Find default config or first one
                    const defaultConfig = updatedConfigs.find(item => item.default);
                    const configToSelect = defaultConfig || updatedConfigs[0];
                    selected(configToSelect);
                } else {
                    // Clear selection if no autoselect or no configs left
                    selected(null);
                }
            }

            // Update pagination total
            setPagination(prevState => ({
                ...prevState,
                total: Math.max(0, prevState.total - 1),
            }));

            handleSuccess(notifications.set, {
                title: __("API key deleted successfully.", 'limb-chatbot'),
            });
        } catch (e) {
            handleError(e, notifications.set, {
                title: __("Failed to delete API key.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please check your connection and try again.", 'limb-chatbot'),
            });
        }
        setLoading(prev => prev - 1);
    }

    const options = configs.map(item => ({
        label: item.title,
        value: item.id,
        extra: item
    }));

    return (
        <>
            <Dropdown
                value={configId}
                setValue={(config) => selected(config)}
                defaultValue={selectedConfig}
                defaultIcon='key-vertical'
                options={options}
                placeholder={__("API key", 'limb-chatbot')}
                searchable
                search={async (params) => await getConfigs(params, true)}
                loadMore={async (params) => await getConfigs(params)}
                pagination={{
                    get: pagination,
                    set: setPagination,
                }}
                onOptionEdit={handleOptionEdit}
                onOptionDelete={handleOptionDelete}
                {...(fieldProps || {})}
                disabled={dropdownDisabled}
            />
            {showEditConfigPopup &&
                <AddEditConfig
                    aiProviderId={aiProviderId}
                    configData={editConfigData}
                    updated={configUpdated}
                    close={() => {
                        setShowEditConfigPopup(false);
                        setEditConfigData(null);
                    }}
                    notifications={notifications}
                />}
        </>
    );
}

export default forwardRef(Config);