import {forwardRef, useEffect, useImperativeHandle, useRef, useState, useCallback, useMemo} from "@wordpress/element";
import {__} from "@wordpress/i18n";
import Input from "../../../../../../fields/input";
import Textarea from "../../../../../../fields/textarea";
import {required, validate} from "../../../../../../../../validations";
import {handleError} from "../../../../../../../helpers/notifications";
import {GetSettings, UpdateSetting, UpdateSettings} from "../../../../../../../rest/settings";
import {
    getInitialData,
    getSettingsDataToUpdate,
    setupSettings
} from "../../../../../../../helpers";
import {generateUniqueId, showWidget} from "../../../../../../../../helpers";
import Emoji from "../../../../../../button/emoji";
import {ONBOARDING_KEY_PREFIX, updateOnboardingPositions} from "../../index";
import {DEFAULT_CHATBOT_SETTINGS_KEY_PREFIX} from "../../../../../../../data/chatbot/settings";
import Description from "../../../../../../sections/description";
import {COLORS, THEMES} from "../../../../../../../constants/chatbot/appearance";
import ColorPicker from "../../../../../../fields/color-picker";
import WPObjectsGroupsMultiSelect from "../../../../../../fields/specific-fields/wp-objects-groups-multi-select";
import Label from "../../../../../../sections/label";
import Tooltip from "../../../../../../fields/tooltip";

/**
 * Get initial message
 *
 * @param {object[]} widgets Widgets
 * @return {{text: string, indexes: {widget: number, content: number}}|false}
 */
const getInitialMessage = (widgets) => {
    const result = {
        initialMessage: '',
        initialMessagePlace: false,
    };
    if (!widgets?.length) {
        return result;
    }
    try {
        // Find a widget with new_chat location and type 'message'
        for (let i = 0; i < widgets.length; i++) {
            const widget = widgets[i];
            if (widget.published 
                && widget.type === 'message' 
                && showWidget(widget.locations, {screen: 'new_chat'})
                && widget.data?.content?.length) {
                // Find the text content
                for (let k = 0; k < widget.data.content.length; k++) {
                    const content = widget.data.content[k];
                    if (content?.type === 'text') {
                        // Initial text found
                        result.initialMessage = content.text?.value || '';
                        result.initialMessagePlace = {
                            widget: i,
                            content: k
                        };
                        return result;
                    }
                }
            }
        }
    } catch (e) {
        handleError(e);
    }
    return result;
}

/**
 * Update initial message
 *
 * @param {string} value Message
 * @param {object} foundWidgetPlace Widget location
 * @param {function} setter Widgets state manager
 */
const updateInitialMessage = (value, foundWidgetPlace, setter) => {
    try {
        if (!foundWidgetPlace) {
            // Create a new message widget
            setter(prevState => [
                ...(prevState || []),
                {
                    id: generateUniqueId(),
                    type: 'message',
                    published: true,
                    notify: false,
                    data: {
                        content: [
                            {
                                type: 'text',
                                text: {
                                    value: value
                                }
                            }
                        ]
                    },
                    locations: [
                        [
                            {
                                param: 'screen',
                                operator: '===',
                                value: 'new_chat'
                            }
                        ]
                    ],
                },
            ]);
        } else {
            // Update existing widget
            setter(prevState => prevState.map((widget, index) => {
                // Find the widget
                if (index === foundWidgetPlace.widget) {
                    return {
                        ...widget,
                        data: {
                            ...widget.data,
                            // Update the content
                            content: widget.data.content.map((contentItem, index) => {
                                if (index === foundWidgetPlace.content) {
                                    // Update the text
                                    return {
                                        ...contentItem,
                                        text: {
                                            ...contentItem.text,
                                            value: value
                                        },
                                    };
                                }
                                return contentItem;
                            })
                        },
                    };
                }
                return widget;
            }))
        }
    } catch (e) {
        handleError(e);
    }
}

const CHATBOT_PREVIEW_KEYS = ['greeting_text', 'greeting_tagline', 'widgets', 'theme', 'color'];

const Step2 = forwardRef(({setIsReady, setIsDataReady, setLoading, setHasUnsavedChanges, notifications, setChatbotPreview}, ref) => {
    const [isDataFetched, setIsDataFetched] = useState(false);
    const [showInFetched, setShowInFetched] = useState(false);
    const [hideInFetched, setHideInFetched] = useState(false);
    const settings = {
        'custom_instructions': useState(''),
        'default_custom_instructions': useState(''),
        'theme': useState('system'),
        'color': useState('#458cf5'),
        'custom_colors': useState([]),
        'greeting_text': useState(''),
        'greeting_tagline': useState(''),
        'widgets': useState([]),
        'show_in': useState(['all']),
        'hide_in': useState([]),
    };
    const errors = {
        'custom_instructions': useState(false),
        'theme': useState(false),
        'greeting_text': useState(false),
        'greeting_tagline': useState(false),
        'initial_message': useState(false),
        'show_in': useState(false),
        'hide_in': useState(false),
    };
    const initialData = useRef(getInitialData(settings));

    // Widgets related - memoize to avoid recalculation on every render
    const {initialMessage, initialMessagePlace} = useMemo(
        () => getInitialMessage(settings['widgets'][0]),
        [settings['widgets'][0]]
    );

    // Memoize default color values to avoid recalculation
    const firstDefaultColor = useMemo(() => COLORS.length > 0 ? COLORS[0] : null, []);
    const lastDefaultColor = useMemo(() => COLORS.length > 1 ? COLORS[COLORS.length - 1] : null, []);

    /**
     * Handle color click (both default and custom)
     */
    const handleColorClick = useCallback((colorValue) => {
        settings['color'][1](colorValue);
    }, [settings['color'][1]]);

    /**
     * Handle custom color delete
     */
    const handleCustomColorDelete = useCallback((colorValue, isActive) => {
        // Remove the custom color (only one is allowed)
        settings['custom_colors'][1]([]);
        // Reset chatbot color if this was the active color
        if (isActive) {
            settings['color'][1](COLORS[0].value);
        }
    }, [settings['custom_colors'][1], settings['color'][1]]);

    /**
     * Render color value component
     *
     * @param {object} item Color value object
     * @param {number} i Index
     * @param {boolean} custom Is the color value custom or not
     * @return {JSX.Element}
     */
    const renderColorValue = useCallback((item, i, custom = false) => {
        const isActive = settings['color'][0] === item.value;

        return <div key={i} className='lbaic-settings-cb-a-row-in'>
            <button
                className={`lbaic-settings-radio-m lbaic-settings-radio-m-color lbaic-settings-radio-m-h-40 lbaic-settings-radio-m-pi-8${isActive ? ' active' : ''}`}
                onClick={() => handleColorClick(item.value)}>
                {custom &&
                    <button className="lbaic-settings-cb-a-icon"
                            onClick={(e) => {
                                e.stopPropagation();
                                handleCustomColorDelete(item.value, isActive);
                            }}>
                        <svg className='lbaic-settings-cb-a-i'
                             xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                            <use href='#lbaic-settings-close'/>
                        </svg>
                    </button>}
                <i className='lbaic-settings-radio-m-color-in'
                   style={{'--lbaic-settings-radio-m-color-bg': item.value}}/>
            </button>
        </div>
    }, [settings['color'][0], handleColorClick, handleCustomColorDelete]);

    /**
     * Color picked from picker
     *
     * @param {Event} e Event
     */
    const onColorPick = useCallback((e) => {
        const newColor = e.target.value;

        // Update main color
        settings['color'][1](newColor);

        // Update custom colors - always ensure first element exists with the new color
        settings['custom_colors'][1]([{
            value: newColor,
        }]);
    }, [settings['color'][1], settings['custom_colors'][1]]);

    /**
     * Color picker opened (no-op for onboarding)
     */
    const onOpenColorPicker = useCallback(() => {
        // No action needed in onboarding step
    }, []);

    // Update chatbot preview when settings change
    useEffect(() => {
        if (isDataFetched) {
            if (typeof setChatbotPreview === 'function') {
                // Update state
                setChatbotPreview(prevState => ({
                    ...prevState,
                    utility: {
                        ...prevState.utility,
                        ...CHATBOT_PREVIEW_KEYS.reduce((acc, key) => ({
                            ...acc,
                            [key]: settings[key][0],
                        }), {})
                    },
                    _updateTimestamp: Date.now()
                }));
            }
        }
    }, [
        isDataFetched,
        settings['greeting_text'][0],
        settings['greeting_tagline'][0],
        settings['widgets'][0],
        settings['theme'][0],
        settings['color'][0],
    ]);

    useImperativeHandle(ref, () => ({
        discardChanges() {
            // Reset all settings to initial values
            Object.keys(settings).forEach(key => {
                settings[key][1](initialData.current[key]);
            });

            // Clear all errors
            Object.keys(errors).forEach(key => {
                errors[key][1](false);
            });
        },
        async beforeNextStep() {
            // Save data
            try {
                if (!isTheStepValid()) {
                    // Set step state 0 - not done yet
                    await UpdateSetting(LimbChatbot.rest.url, LimbChatbot.rest.nonce, ONBOARDING_KEY_PREFIX + 'step_2.status', 0);
                    return false;
                }
                const dataToUpdate = getSettingsDataToUpdate(settings, initialData.current, DEFAULT_CHATBOT_SETTINGS_KEY_PREFIX)
                if (dataToUpdate.length) {
                    const res = await UpdateSettings(LimbChatbot.rest.url, LimbChatbot.rest.nonce, dataToUpdate);
                }
                // Set step state 1 - done
                await UpdateSetting(LimbChatbot.rest.url, LimbChatbot.rest.nonce, ONBOARDING_KEY_PREFIX + 'step_2.status', 1);

                return true;
            } catch (e) {
                handleError(e, notifications.set, {
                    title: __("Failed to save changes.", 'limb-chatbot'),
                    description: e.message ? __(e.message, 'limb-chatbot') : __("Please try again.", 'limb-chatbot'),
                });
            }
            return false;
        },
    }));

    useEffect(() => {
        resetTheStepStatus();
        fetchData();
    }, []);

    useEffect(() => {
        setIsReady(settings['custom_instructions'][0].trim().length > 0);
    }, [settings['custom_instructions'][0]]);

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

        // Use getSettingsDataToUpdate to check if there are any changes
        const dataToUpdate = getSettingsDataToUpdate(settings, initialData.current, DEFAULT_CHATBOT_SETTINGS_KEY_PREFIX);
        const hasChanges = dataToUpdate.length > 0;

        setHasUnsavedChanges(hasChanges);
    }, [
        isDataFetched,
        setHasUnsavedChanges,
        settings['custom_instructions'][0],
        settings['theme'][0],
        settings['color'][0],
        settings['custom_colors'][0],
        settings['greeting_text'][0],
        settings['greeting_tagline'][0],
        settings['widgets'][0],
        settings['show_in'][0],
        settings['hide_in'][0],
    ]);

    // Handle show_in and hide_in conditional logic
    useEffect(() => {
        const showInValue = settings['show_in'][0];
        const hasAllValue = showInValue?.includes('all');

        // If show_in doesn't have 'all', clear hide_in field
        if (!hasAllValue && showInValue?.length > 0) {
            settings['hide_in'][1]([]);
        }
    }, [settings['show_in'][0]]);

    // ResizeObserver to watch for height changes in .lbaic-settings-popup-body
    useEffect(() => {
        if (!isDataFetched) {
            return;
        }

        const popupBodyElement = document.querySelector('.lbaic-settings-popup-body');
        if (!popupBodyElement) {
            return;
        }

        const resizeObserver = new ResizeObserver(() => {
            // Recalculate chatbot position when popup body height changes
            window.requestAnimationFrame(() => {
                updateOnboardingPositions();
            });
        });

        resizeObserver.observe(popupBodyElement);

        return () => {
            resizeObserver.disconnect();
        };
    }, [isDataFetched]);

    /**
     * Get saved settings
     */
    const fetchData = async () => {
        setLoading(prev => prev + 1);
        try {
            const res = await GetSettings(LimbChatbot.rest.url, LimbChatbot.rest.nonce, {
                key: Object.keys(settings).map(key => DEFAULT_CHATBOT_SETTINGS_KEY_PREFIX + key),
            });
            setupSettings(settings, false, DEFAULT_CHATBOT_SETTINGS_KEY_PREFIX, res, initialData);
        } catch (e) {
            handleError(e, notifications.set, {
                title: __("Failed to retrieve data.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please check your connection and try again.", 'limb-chatbot'),
            });
        }
        setTimeout(() => {
            setIsDataFetched(true);
            setLoading(prev => prev - 1);
            setIsDataReady(true);
        }, 200);
    }

    /**
     * Reset the step status
     *
     * @return {Promise<void>}
     */
    const resetTheStepStatus = async () => {
        try {
            await UpdateSetting(LimbChatbot.rest.url, LimbChatbot.rest.nonce, ONBOARDING_KEY_PREFIX + 'step_2.status', 0);
        } catch (e) {
            handleError(e);
        }
    }

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

            switch (name) {
                case 'custom_instructions':
                    validations = [required];
                    break;
                default:
                    return true;
            }
            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;
        }
    }

    /**
     * Is the step valid
     *
     * @return {boolean}
     */
    const isTheStepValid = () => {
        let hasError = false;
        const data = {
            'custom_instructions': settings['custom_instructions'][0],
            'greeting_text': settings['greeting_text'][0],
            'greeting_tagline': settings['greeting_tagline'][0],
            'initial_message': initialMessage,
        };
        for (const item of Object.keys(data)) {
            if (!validateField(item, data[item])) {
                hasError = true;
            }
        }

        return !hasError;
    }

    return isDataFetched && <>
        <div className="lbaic-settings-column">
            <div className="lbaic-settings-column-in">
                <Textarea
                    value={settings['custom_instructions'][0]}
                    setValue={settings['custom_instructions'][1]}
                    placeholder={__("Custom instructions", 'limb-chatbot')}
                    validate={(value) => validateField('custom_instructions', value)}
                    errorMessage={errors['custom_instructions'][0]}
                    textareaProps={{rows: 5}}
                    placeholderChildren={
                        settings['custom_instructions'][0] !== settings['default_custom_instructions'][0] && (
                            <div className="lbaic-settings-textarea-placeholder-actions">
                                <button
                                    className="lbaic-settings-textarea-placeholder-action"
                                    type="button"
                                    onClick={() => settings['custom_instructions'][1](settings['default_custom_instructions'][0])}
                                >{__("Reset to default", 'limb-chatbot')}</button>
                            </div>
                        )
                    }
                />
                <Description>{__("Provide specific instructions for how the chatbot should behave and respond.", 'limb-chatbot')}</Description>
            </div>
        </div>
        <div className="lbaic-settings-column">
            <div className="lbaic-settings-column-in">
                <Input
                    value={settings['greeting_text'][0]}
                    setValue={settings['greeting_text'][1]}
                    placeholder={__("Greeting text", 'limb-chatbot')}
                    actions={[
                        {
                            component: Emoji,
                            props: {
                                chosen: obj => settings['greeting_text'][1](prevState => prevState + obj.emoji),
                            }
                        }
                    ]}
                    validate={(value) => validateField('greeting_text', value)}
                    errorMessage={errors['greeting_text'][0]}
                />
                <Description>{__("The main welcome message on the AI agent’s homepage", 'limb-chatbot')}</Description>
            </div>
            <div className="lbaic-settings-column-in">
                <Input
                    value={settings['greeting_tagline'][0]}
                    setValue={settings['greeting_tagline'][1]}
                    placeholder={__("Greeting tagline", 'limb-chatbot')}
                    actions={[
                        {
                            component: Emoji,
                            props: {
                                chosen: obj => settings['greeting_tagline'][1](prevState => prevState + obj.emoji),
                            }
                        }
                    ]}
                    validate={(value) => validateField('greeting_tagline', value)}
                    errorMessage={errors['greeting_tagline'][0]}
                />
                <Description>{__("A brief tagline shown with the greeting on the homepage", 'limb-chatbot')}</Description>
            </div>
        </div>
        <div className="lbaic-settings-column">
            <div className="lbaic-settings-column-in">
                <Input
                    value={initialMessage}
                    setValue={value => updateInitialMessage(value, initialMessagePlace, settings['widgets'][1])}
                    actions={[
                        {
                            component: Emoji,
                            props: {
                                chosen: obj => updateInitialMessage(initialMessage + obj.emoji, initialMessagePlace, settings['widgets'][1])
                            },
                        }
                    ]}
                    placeholder={__("Initial message", 'limb-chatbot')}
                    validate={(value) => validateField('initial_message', value)}
                    errorMessage={errors['initial_message'][0]}
                />
                <Description>{__("The AI’s first message in new conversations.", 'limb-chatbot')} <a href="https://wpaichatbot.com/documentation/getting-started/publishing-your-first-chatbot/#step-2-define-your-ai-agent" target="_blank">{__("Learn more", 'limb-chatbot')}</a></Description>
            </div>
        </div>
        <div className="lbaic-settings-column">
            <div className="lbaic-settings-column-in">
                <div className="lbaic-settings-cb-a-column-header">
                    <Label>{__("Theme", 'limb-chatbot')}</Label>
                </div>
                <div className="lbaic-settings-cb-a-column-body">
                    <div className='lbaic-settings-cb-a-row'>
                        {THEMES.map(item =>
                            <div key={item.value}
                                 className='lbaic-settings-cb-a-row-in lbaic-settings-cb-a-row-gap-8'>
                                <Tooltip
                                    label={item.label}
                                    width={90}
                                >
                                    <button
                                        className={`lbaic-settings-radio-m lbaic-settings-radio-m-h-40 lbaic-settings-radio-m-pi-16${settings['theme'][0] === item.value ? ' active' : ''}`}
                                        onClick={() => settings['theme'][1](item.value)}>
                                        <svg className='lbaic-settings-radio-m-i' xmlns='http://www.w3.org/2000/svg'
                                             fill='none' viewBox='0 0 24 24'>
                                            <use href={`#lbaic-settings-theme-${item.icon}`}/>
                                        </svg>
                                    </button>
                                </Tooltip>
                            </div>)}
                    </div>
                </div>
                <Description>{__("Choose your chatbot's light/dark mode preference", 'limb-chatbot')}</Description>
            </div>
            <div className="lbaic-settings-column-in">
                <div className="lbaic-settings-cb-a-column-header">
                    <Label>{__("Color", 'limb-chatbot')}</Label>
                </div>
                <div className="lbaic-settings-cb-a-column-body">
                    <div className='lbaic-settings-cb-a-row'>
                        <div className='lbaic-settings-cb-a-row-in'>
                            <ColorPicker
                                value={settings['color'][0]}
                                pickerOpened={onOpenColorPicker}
                                colorPicked={onColorPick}
                                positionAbove={true}
                            />
                        </div>
                        {firstDefaultColor && renderColorValue(firstDefaultColor, 0)}
                        {lastDefaultColor && renderColorValue(lastDefaultColor, COLORS.length - 1)}
                        {settings['custom_colors'][0].map((item, i) => renderColorValue(item, i, true))}
                    </div>
                </div>
                <Description>{__("Customize your chatbot's primary color to match your brand", 'limb-chatbot')}</Description>
            </div>
        </div>
        <div className="lbaic-settings-column">
            <div className="lbaic-settings-column-in">
                <WPObjectsGroupsMultiSelect
                    value={settings['show_in'][0]}
                    setValue={settings['show_in'][1]}
                    placeholder={__("Show in location", 'limb-chatbot')}
                    errorMessage={errors['show_in'][0]}
                    validate={value => validateField('show_in', value)}
                    addNotification={notifications.set}
                    isDataReady={isDataFetched}
                    setFetched={setShowInFetched}
                />
                <Description>{__("Select the WordPress public locations where your chatbot will be shown", 'limb-chatbot')}</Description>
            </div>
            <div className="lbaic-settings-column-in">
                {isDataFetched && settings['show_in'][0]?.includes('all') && (
                    <>
                        <WPObjectsGroupsMultiSelect
                            value={settings['hide_in'][0]}
                            setValue={settings['hide_in'][1]}
                            placeholder={__("Hide in location", 'limb-chatbot')}
                            errorMessage={errors['hide_in'][0]}
                            validate={value => validateField('hide_in', value)}
                            addNotification={notifications.set}
                            showAllOption={false}
                            isDataReady={isDataFetched}
                            setFetched={setHideInFetched}
                        />
                        <Description>{__("Select locations where the chatbot will be hidden", 'limb-chatbot')}</Description>
                    </>
                )}
            </div>
        </div>
    </>
});

export default Step2;