import {useEffect, useRef, useState} from "@wordpress/element";
import {__} from "@wordpress/i18n";
import Dropdown from "../../dropdown";
import {GetModel, GetModels} from "../../../../rest/models";
import {handleError} from "../../../../helpers/notifications";

export default function Model({aiProviderId, isAIProviderRequired, placeholder, isDataFetched, isDefaultsChecked = true, modelId, setModelId, endpoints, modelSelected, setFetched, fieldProps, aiProviderSelected, autoSelect = true, bestModelFor, disabled, setLoading, notifications}) {
    const [loadingModels, setLoadingModels] = useState(false);
    const [selectedModel, setSelectedModel] = useState(null);
    const [models, setModels] = useState([]);
    const [pagination, setPagination] = useState({
        page: 1,
        perPage: 10,
        total: 0,
    });
    const modelsFetchedRef = useRef(false);

    useEffect(() => {
        if (!isDefaultsChecked) {
            return;
        }
        // Keep selected model object synced
        if (modelId) {
            if (models.length) {
                const model = models.find(item => +item.id === +modelId);
                if (model?.id) {
                    if (model.id !== selectedModel?.id) {
                        selected(model);
                    } else {
                        // Nothing to do
                    }
                } else {
                    if (selectedModel?.id) {
                        if (selectedModel.id !== modelId) {
                            getSelectedModel(modelId);
                        } else {
                            // Nothing to do
                        }
                    } else {
                        getSelectedModel(modelId);
                    }
                }
            } else {
                if (modelsFetchedRef.current) {
                    getSelectedModel(modelId);
                }
            }
        } else {
            selected(null);
        }
    }, [isDefaultsChecked, modelId, models]);

    useEffect(() => {
        if (isDataFetched && isDefaultsChecked) {
            // Update models list
            getModels({
                page: 1,
                perPage: pagination.perPage
            }, true);
        }
    }, [isDataFetched, isDefaultsChecked, aiProviderId]);

    useEffect(() => {
        if (typeof aiProviderSelected === 'function') {
            if (selectedModel?.id) {
                if (selectedModel?.ai_provider_id) {
                    aiProviderSelected(selectedModel.ai_provider_id);
                }
            } else {
                aiProviderSelected(null);
            }
        }
    }, [selectedModel]);

    /**
     * Get selected AI models
     *
     * @param {object} params Params
     * @param {boolean} reset Reset the list of models
     * @return {Promise<void>}
     */
    const getModels = async (params, reset = false) => {
        if (isAIProviderRequired && !aiProviderId) {
            setModels([]);
            typeof setFetched === 'function' && setFetched(true);
            return;
        }
        setLoadingModels(true);
        setLoading(prev => prev + 1);
        // Default params
        const reqParams = {
            page: params?.page || 1,
            per_page: params?.perPage || 10,
        };
        if (endpoints?.length) {
            reqParams.endpoints = endpoints;
        }
        if (isAIProviderRequired) {
            reqParams.ai_provider_id = aiProviderId;
        }
        // Add search param
        if (params?.search) {
            reqParams.search = params.search;
            reqParams.search_fields = ['label'];
        }
        try {
            const res = await GetModels(LimbChatbot.rest.url, LimbChatbot.rest.nonce, reqParams);
            const newModels = [...(reset ? [] : models), ...(res.items?.length ? res.items : [])];
            // Update state
            setModels([...newModels]);
            // Select model
            if (reset) {
                checkModelsNewList(newModels, aiProviderId);
            }
            // Pagination info
            setPagination(prevState => ({
                ...prevState,
                page: reqParams.page,
                total: res.total,
            }));
        } catch (e) {
            handleError(e, notifications.set, {
                title: __("Failed to retrieve the AI models 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);
        setLoadingModels(false);
        modelsFetchedRef.current = true;
    }

    /**
     * Check new models list
     *
     * @param {object[]} models Models
     * @param {string} aiProvider AI provider
     */
    const checkModelsNewList = (models, aiProvider) => {
        if (autoSelect) {
            // Only auto-select if no model is saved
            // If a model is saved but not in the list, the useEffect will fetch it
            if (!modelId) {
                // Find autoselect item
                const autoSelectItem =
                    bestModelFor ?
                        models.find(item => item.name === bestModelFor) : models[0];
                if (autoSelectItem?.id) {
                    selected(autoSelectItem);
                } else {
                    // Nothing to autoselect
                    selected(null);
                }
            }
        } else {
            if (selectedModel) {
                // If the selected model is for a different AI provider, then clear the field
                if (selectedModel.ai_provider_id !== aiProvider) {
                    selected(null);
                }
            }
        }
    }

    /**
     * Get saved model
     *
     * @param {number} id AI model id
     * @return {Promise<void>}
     */
    const getSelectedModel = async (id) => {
        // Get the value
        try {
            const res = await GetModel(LimbChatbot.rest.url, LimbChatbot.rest.nonce, id);
            if (res?.id) {
                selected(res);
            }
        } catch (e) {
            handleError(e, notifications.set, {
                title: __("Saved AI model not found.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please check your connection and try again.", 'limb-chatbot'),
            });
        }
    }

    /**
     * Model selected
     *
     * @param {object|null} model Model
     * @param {boolean} validate Validate
     */
    const selected = (model, validate = true) => {
        const id = model?.id || '';
        setModelId(id);
        if (id) {
            setSelectedModel({
                ...model,
                icon: model.ai_provider_id
            });
        } else {
            setSelectedModel(null);
        }
        if (typeof modelSelected === 'function') {
            modelSelected(model);
        }
        if (validate && typeof fieldProps?.validate === 'function') {
            fieldProps?.validate(id);
        }
    }

    return <Dropdown value={modelId} setValue={(value) => selected(models.find(item => item.id === value))}
                     defaultValue={selectedModel}
                     options={models.map(item => ({
                         label: item.label,
                         value: item.id,
                         icon: item.ai_provider_id
                     }))}
                     placeholder={placeholder || __("AI model", 'limb-chatbot')}
                     searchable
                     search={async (params) => await getModels(params, true)}
                     loadMore={async (params) => await getModels(params)}
                     pagination={{
                         get: pagination,
                         set: setPagination,
                     }}
                     {...(fieldProps || {})}
                     disabled={disabled || loadingModels}/>
}