import {useEffect, forwardRef, useImperativeHandle, useRef, useState, Fragment} from "@wordpress/element";
import {__} from "@wordpress/i18n";
import openAiImage from "../../../../../../../../../resources/admin/page/settings/open-ai.svg"
import geminiImage from "../../../../../../../../../resources/admin/page/settings/gemini.svg"
import deepSeekImage from "../../../../../../../../../resources/admin/page/settings/deep-seek.svg"
import openRouterImage from "../../../../../../../../../resources/admin/page/settings/open-router.svg"
import claudeImage from "../../../../../../../../../resources/admin/page/settings/claude.svg"
import grokImage from "../../../../../../../../../resources/admin/page/settings/grok.svg"
import Input from "../../../../../../fields/input";
import Checkbox from "../../../../../../fields/checkbox";
import Tooltip from "../../../../../../fields/tooltip";
import {required, validate} from "../../../../../../../../validations";
import {handleError} from "../../../../../../../helpers/notifications";
import {CreateConfig, UpdateConfig} from "../../../../../../../rest/configs";
import Notice from "../../../../../../sections/notice";
import {UpdateSettings} from "../../../../../../../rest/settings";
import {getProviderFromConfigRelation, getInitialData, getSettingsDataToUpdate} from "../../../../../../../helpers";
import {DEFAULT_CHATBOT_SETTINGS_KEY_PREFIX} from "../../../../../../../data/chatbot/settings";
import {SETTINGS_UTILITIES_KEY_PREFIX} from "../../../../../../../data/settings";
import {getAIProviderUtilities} from "../../../../../../../data/utilities";
import {GetUtilitySettings} from "../../../../../../../helpers/rest";
import {
    AI_PROVIDER_API_KEY_FIELD_DESCRIPTION
} from "../../../../../../popups/ai-providers/models/add-edit-config/_data";

const Step1 = forwardRef(({config, setIsReady, setIsDataReady, setHasUnsavedChanges, notifications}, ref) => {
    const settings = {
        'ai_provider_id': useState(''),
    };
    const errors = {
        'ai_provider_id': useState(false),
    };
    const [apiKeyError, setApiKeyError] = useState(false);

    const initialData = useRef(getInitialData(settings));
    const initialParamsState = useRef(null);

    // Get params fields from hook for the selected provider, excluding Slack fields
    const getParamsFields = (providerId) => {
        const allFields = LimbChatbot.Hooks.applyFilters('lbaic.admin.page.settings.config.dataToCreate.fields', [
            {
                label: __("API key", 'limb-chatbot'),
                name: 'api_key',
                defaultValue: config?.params?.api_key || '',
                type: 'input',
                validations: [required],
                description: providerId in AI_PROVIDER_API_KEY_FIELD_DESCRIPTION ? AI_PROVIDER_API_KEY_FIELD_DESCRIPTION[providerId] : null
            }
        ], providerId);

        // Filter out Slack fields (bot_token, signing_secret, app_token)
        return allFields.filter(field =>
            !['bot_token', 'signing_secret', 'app_token'].includes(field.name)
        );
    };

    // Params state - use a single state object for all params
    const [paramsState, setParamsState] = useState({});
    const [paramsErrorsState, setParamsErrorsState] = useState({});
    const [paramsFieldsInfo, setParamsFieldsInfo] = useState({});

    useImperativeHandle(ref, () => ({
        discardChanges() {
            // Reset ai_provider_id to initial value
            const initialProvider = initialData.current.ai_provider_id || '';
            settings['ai_provider_id'][1](initialProvider);

            // Reset params state to initial values
            if (initialParamsState.current !== null) {
                setParamsState({...initialParamsState.current});
            } else {
                setParamsState({});
            }

            // Clear errors
            errors['ai_provider_id'][1](false);
            setParamsErrorsState({});
            setApiKeyError(false);
        },
        async getData() {
            // Validate main fields
            if (!validateField('ai_provider_id', settings['ai_provider_id'][0])) {
                return false;
            }

            // Get current params fields for validation
            const currentFields = getParamsFields(settings['ai_provider_id'][0]);

            // Validate all params fields
            let hasParamError = false;
            for (const field of currentFields) {
                if (!validateParamField(field.name, paramsState[field.name])) {
                    hasParamError = true;
                }
            }
            if (hasParamError) {
                return false;
            }
            const data = getSettingsDataToUpdate(settings, initialData.current);

            // Build params object from current fields only
            const paramsData = {};
            for (const field of currentFields) {
                if (field.name in paramsState) {
                    paramsData[field.name] = paramsState[field.name];
                }
            }

            // Check if params have changed
            let paramsChanged = !config?.id;
            if (config?.id) {
                for (const key of Object.keys(paramsData)) {
                    if (config.params?.[key] !== paramsData[key]) {
                        paramsChanged = true;
                        break;
                    }
                }
                // Also check if any params were removed (exist in config but not in paramsData)
                if (config.params) {
                    for (const key of Object.keys(config.params)) {
                        // Skip Slack fields
                        if (!['bot_token', 'signing_secret', 'app_token'].includes(key) && !(key in paramsData)) {
                            paramsChanged = true;
                            break;
                        }
                    }
                }
            }

            if (!data.length && !paramsChanged) {
                return {
                    config: config,
                };
            }
            try {
                let res;
                if (config?.id) {
                    // Check if provider has changed from the original config
                    const originalProvider = getProviderFromConfigRelation(config.related_to);
                    const newProvider = settings['ai_provider_id'][0];

                    if (originalProvider !== newProvider) {
                        // Provider changed, create a new config
                        res = await CreateConfig(LimbChatbot.rest.url, LimbChatbot.rest.nonce, {
                            title: __('Main', 'limb-chatbot'),
                            description: __('Created for onboarding.', 'limb-chatbot'),
                            related_to: settings['ai_provider_id'][0],
                            params: paramsData,
                        });
                        if (res?.id) {
                            // Save onboarding config in settings
                            await saveConfigSetting(res);
                        }
                    } else {
                        // Provider unchanged, update the existing config
                        res = await UpdateConfig(LimbChatbot.rest.url, LimbChatbot.rest.nonce, config.id, {
                            related_to: settings['ai_provider_id'][0],
                            params: paramsData,
                        });
                    }
                } else {
                    if (data.find(item => item.key === 'ai_provider_id')) {
                        // Create a new config
                        res = await CreateConfig(LimbChatbot.rest.url, LimbChatbot.rest.nonce, {
                            title: __('Main', 'limb-chatbot'),
                            description: __('Created for onboarding.', 'limb-chatbot'),
                            related_to: settings['ai_provider_id'][0],
                            params: paramsData,
                        });
                        if (res?.id) {
                            // Save onboarding config in settings
                            await saveConfigSetting(res);
                        }
                    }
                }
                // Check response
                if (res?.id) {
                    return {
                        config: res,
                    };
                } else {
                    setApiKeyError(__("Failed to save the API key.", 'limb-chatbot') + ' ' + __("Please check and try again.", 'limb-chatbot'));
                }
            } catch (e) {
                handleError(e);
                setApiKeyError(__("Failed to save the API key.", 'limb-chatbot') + ' ' + (e.message ? __(e.message, 'limb-chatbot') : __("Please check and try again.", 'limb-chatbot')));
            }
            return false;
        },
    }));

    useEffect(() => {
        // Here nothing to check, config comes already
        setIsDataReady(true);
    }, []);

    useEffect(() => {
        // Update params fields info when provider changes
        if (settings['ai_provider_id'][0]) {
            const newFields = getParamsFields(settings['ai_provider_id'][0]);

            // Update params fields info
            setParamsFieldsInfo(newFields.reduce((obj, item) => {
                obj[item.name] = {
                    ...item
                };
                return obj;
            }, {}));

            // Initialize/update params state
            setParamsState(prev => {
                const newState = {...prev};
                for (const field of newFields) {
                    if (!(field.name in newState)) {
                        // Initialize with default or config value
                        newState[field.name] = config?.params?.[field.name] ?? field.defaultValue;
                    } else if (config?.params?.[field.name] !== undefined && config.params[field.name] !== newState[field.name]) {
                        // Update from config if different
                        newState[field.name] = config.params[field.name];
                    }
                }
                return newState;
            });

            // Reset errors when provider changes
            setParamsErrorsState({});
        }
    }, [settings['ai_provider_id'][0]]);

    useEffect(() => {
        if (config?.id) {
            const aiProviderId = getProviderFromConfigRelation(config.related_to) || '';
            // Update settings state
            settings['ai_provider_id'][1](aiProviderId);
            // Initial data
            initialData.current.ai_provider_id = aiProviderId;

            // Update params from config
            if (config.params) {
                const fields = getParamsFields(aiProviderId);
                const newParamsState = {};
                for (const field of fields) {
                    if (field.name in config.params) {
                        newParamsState[field.name] = config.params[field.name];
                    } else {
                        newParamsState[field.name] = field.defaultValue;
                    }
                }
                // Store initial params state (only once when config is first loaded)
                if (initialParamsState.current === null) {
                    initialParamsState.current = {...newParamsState};
                }
                setParamsState(newParamsState);
            } else {
                // No config params, initialize with empty state
                if (initialParamsState.current === null) {
                    initialParamsState.current = {};
                }
            }
        }
    }, [config]);

    useEffect(() => {
        // Reset API key error when provider changes
        setApiKeyError(false);
        // Reset params errors when provider changes
        setParamsErrorsState({});
    }, [settings['ai_provider_id'][0]]);

    useEffect(() => {
        // Check if provider is selected and api_key is filled
        const apiKey = paramsState['api_key'] || '';
        setIsReady(
            Boolean(settings['ai_provider_id'][0] && apiKey.trim().length > 0)
        );
    }, [settings['ai_provider_id'][0], paramsState['api_key']]);

    // Check for unsaved changes
    useEffect(() => {
        if (!setHasUnsavedChanges) {
            return;
        }

        // Check if ai_provider_id changed
        const providerChanged = settings['ai_provider_id'][0] !== (initialData.current.ai_provider_id || '');

        // Check if params changed
        let paramsChanged = false;
        if (initialParamsState.current !== null) {
            const currentParamsKeys = Object.keys(paramsState);
            const initialParamsKeys = Object.keys(initialParamsState.current);

            // Check if keys are different
            if (currentParamsKeys.length !== initialParamsKeys.length) {
                paramsChanged = true;
            } else {
                // Check if any value changed
                for (const key of currentParamsKeys) {
                    if (paramsState[key] !== initialParamsState.current[key]) {
                        paramsChanged = true;
                        break;
                    }
                }
            }
        } else {
            // If initialParamsState is null (no config on first load), check if paramsState has any non-empty values
            // This means user selected a provider and entered values
            const hasNonEmptyValues = Object.keys(paramsState).length > 0 &&
                Object.values(paramsState).some(val => {
                    if (typeof val === 'string') {
                        return val.trim().length > 0;
                    }
                    return val !== '' && val !== null && val !== undefined;
                });
            paramsChanged = hasNonEmptyValues;
        }

        setHasUnsavedChanges(providerChanged || paramsChanged);
    }, [settings['ai_provider_id'][0], paramsState, setHasUnsavedChanges]);

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

            switch (name) {
                case 'ai_provider_id':
                    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 param field
     *
     * @param {string} name Field name
     * @param {any} value Field value
     * @return {boolean}
     */
    const validateParamField = (name, value) => {
        try {
            let validations = [];

            const currentFields = getParamsFields(settings['ai_provider_id'][0]);
            const fieldInfo = currentFields.find(item => item.name === name);
            if (fieldInfo?.validations?.length) {
                validations = fieldInfo.validations;
            }
            const res = validate(value, validations);
            // Update field errors state
            setParamsErrorsState(prev => ({
                ...prev,
                [name]: !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 config setting
     *
     * @param {object} config Config
     * @return {Promise<void>}
     */
    const saveConfigSetting = async (config) => {
        try {
            const aiProviderId = getProviderFromConfigRelation(config.related_to);
            const res = await GetUtilitySettings(aiProviderId, 'chatbot', [
                {
                    key: 'ai_model_id',
                    defaultValue: '',
                }
            ]);
            // Get available utilities for the AI provider
            const utilities = getAIProviderUtilities(aiProviderId);
            const hasEmbeddingSupport = utilities.includes('embedding');

            // Get embedding utility default AI model ID only if provider supports embedding
            let embeddingRes = null;
            if (hasEmbeddingSupport) {
                embeddingRes = await GetUtilitySettings(aiProviderId, 'embedding', [
                    {
                        key: 'ai_model_id',
                        defaultValue: '',
                    }
                ]);
            }

            // Build settings to update
            const settingsToUpdate = [
                // Default chatbot AI settings
                {
                    key: DEFAULT_CHATBOT_SETTINGS_KEY_PREFIX + 'ai_provider_id',
                    value: aiProviderId,
                },
                {
                    key: DEFAULT_CHATBOT_SETTINGS_KEY_PREFIX + 'config_id',
                    value: config.id,
                },
                {
                    key: DEFAULT_CHATBOT_SETTINGS_KEY_PREFIX + 'ai_model_id',
                    value: res.ai_model_id,
                },
                // Utilities default AI provider ID
                ...utilities.map(item => ({
                    key: SETTINGS_UTILITIES_KEY_PREFIX + item + '.default.ai_provider_id',
                    value: aiProviderId,
                })),
                // Utilities ai provider config ID
                ...utilities.map(item => ({
                    key: `lbaic.ai_providers.${aiProviderId}.utilities.` + item + '.config_id',
                    value: config.id,
                }))
            ];

            // Only add KB settings if provider supports embedding
            if (hasEmbeddingSupport && embeddingRes) {
                settingsToUpdate.push(
                    // Knowledge base settings
                    {
                        key: SETTINGS_UTILITIES_KEY_PREFIX + 'chatbot.kb_config_id',
                        value: config.id,
                    },
                    {
                        key: SETTINGS_UTILITIES_KEY_PREFIX + 'chatbot.kb_ai_model_id',
                        value: embeddingRes.ai_model_id || '',
                    }
                );
            }

            // Update settings
            await UpdateSettings(LimbChatbot.rest.url, LimbChatbot.rest.nonce, settingsToUpdate);
        } catch (e) {
            handleError(e);
        }
    }

    return <>
        <div className="lbaic-settings-mp-popup-body">
            <div
                className={`lbaic-settings-mp-popup-body-in${settings['ai_provider_id'][0] === 'open-ai' ? ' active' : ''}`}
                onClick={() => settings['ai_provider_id'][1]('open-ai')}>
                <div className="lbaic-settings-mp-popup-body-item">
                    <img className='lbaic-settings-mp-popup-pic' src={openAiImage} alt="open-ai"/>
                </div>
                <span className="lbaic-settings-mp-popup-body-label">{__("OpenAI", 'limb-chatbot')}</span>
            </div>
            <div
                className={`lbaic-settings-mp-popup-body-in${settings['ai_provider_id'][0] === 'gemini' ? ' active' : ''}`}
                onClick={() => settings['ai_provider_id'][1]('gemini')}>
                <div className="lbaic-settings-mp-popup-body-item">
                    <img className='lbaic-settings-mp-popup-pic' src={geminiImage} alt="gemini"/>
                </div>
                <span className="lbaic-settings-mp-popup-body-label">{__("Google Gemini", 'limb-chatbot')}</span>
            </div>
            <div
                className={`lbaic-settings-mp-popup-body-in${settings['ai_provider_id'][0] === 'claude' ? ' active' : ''}`}
                onClick={() => settings['ai_provider_id'][1]('claude')}>
                <div className="lbaic-settings-mp-popup-body-item">
                    <img className='lbaic-settings-mp-popup-pic' src={claudeImage} alt="claude"/>
                </div>
                <span className="lbaic-settings-mp-popup-body-label">{__("Claude", 'limb-chatbot')}</span>
            </div>
            <div
                className={`lbaic-settings-mp-popup-body-in${settings['ai_provider_id'][0] === 'grok' ? ' active' : ''}`}
                onClick={() => settings['ai_provider_id'][1]('grok')}>
                <div className="lbaic-settings-mp-popup-body-item">
                    <img className='lbaic-settings-mp-popup-pic' src={grokImage} alt="grok"/>
                </div>
                <span className="lbaic-settings-mp-popup-body-label">{__("Grok", 'limb-chatbot')}</span>
            </div>
            <div
                className={`lbaic-settings-mp-popup-body-in${settings['ai_provider_id'][0] === 'open-router' ? ' active' : ''}`}
                onClick={() => settings['ai_provider_id'][1]('open-router')}>
                <div className="lbaic-settings-mp-popup-body-item">
                    <img className='lbaic-settings-mp-popup-pic' src={openRouterImage} alt="open-router"/>
                </div>
                <span className="lbaic-settings-mp-popup-body-label">{__("Open-router", 'limb-chatbot')}</span>
            </div>
            <div
                className={`lbaic-settings-mp-popup-body-in${settings['ai_provider_id'][0] === 'deep-seek' ? ' active' : ''}`}
                onClick={() => settings['ai_provider_id'][1]('deep-seek')}>
                <div className="lbaic-settings-mp-popup-body-item">
                    <img className='lbaic-settings-mp-popup-pic' src={deepSeekImage} alt="deep-seek"/>
                </div>
                <span className="lbaic-settings-mp-popup-body-label">{__("DeepSeek", 'limb-chatbot')}</span>
            </div>
        </div>
        {settings['ai_provider_id'][0] && (
            <div className="lbaic-settings-mp-popup-content">
                {Object.keys(paramsFieldsInfo).map((key) => {
                    if (paramsFieldsInfo[key]?.hide) {
                        return null;
                    }

                    return (
                        <Fragment key={key}>
                            <div className="lbaic-settings-column">
                                <div className="lbaic-settings-column-in">
                                    {paramsFieldsInfo[key]?.type === 'input' &&
                                        <Input value={paramsState[key] || ''}
                                               setValue={(value) => setParamsState(prev => ({...prev, [key]: value}))}
                                               placeholder={paramsFieldsInfo[key].label}
                                               validate={(value) => validateParamField(key, value)}
                                               errorMessage={paramsErrorsState[key]}>
                                            {paramsFieldsInfo[key]?.tooltip &&
                                                <Tooltip label={paramsFieldsInfo[key].tooltip} position="top"/>}
                                        </Input>}
                                    {paramsFieldsInfo[key]?.type === 'checkbox' &&
                                        <Checkbox label={paramsFieldsInfo[key].label}
                                                  isChecked={paramsState[key] || 0}
                                                  toggleValue={() => setParamsState(prev => ({
                                                      ...prev,
                                                      [key]: +!prev[key]
                                                  }))}
                                                  validate={(value) => validateParamField(key, value)}
                                                  errorMessage={paramsErrorsState[key]}/>}
                                    {Boolean(paramsFieldsInfo[key]?.description) && (
                                        paramsFieldsInfo[key]?.description
                                    )}
                                </div>
                            </div>
                        </Fragment>
                    );
                })}
                {apiKeyError && <Notice type='error' desc={apiKeyError}/>}
            </div>
        )}
    </>
});

export default Step1;