import {useCallback, useContext, useEffect, useMemo, useRef, useState} from "@wordpress/element";
import {__} from "@wordpress/i18n";
import {marked} from "marked";
import {ChatBotsContext} from "../../../../contexts/chatbots";
import {getCookie, showWidget} from "../../../../../helpers";
import useTheme from "../../../hooks/use-theme";
import useSoundNotification from "../../../hooks/use-sound-notification";
import ChatbotUtility from "../../../../../utilities/chatbot";
import {validateEmailInput} from "../../footer/inputs/email";

// Storage management constants
const STORAGE_KEY = 'lbaic.chatbots';
const PROMPTS_SHOWN_KEY = 'chatbot-icon-prompt-widget-items_have_been_shown';
const MESSAGES_SHOWN_KEY = 'chatbot-icon-message-widget-items_have_been_shown';
// Preview mode only: persist lead capture shown (submitted or dismissed) like messages/prompts
const LEAD_CAPTURE_PREVIEW_SHOWN_KEY = 'chatbot-icon-lead-capture-widget-items_have_been_shown';

// Module-level store to track notifications played in current page session
// This is separate from sessionStorage (which tracks dismissals)
const notificationsPlayedStore = {};

/**
 * Get chatbot identifier (uuid or 'default')
 * @param {object} chatbot Chatbot object
 * @return {string|null} Chatbot identifier
 */
const getChatbotId = (chatbot) => {
    return chatbot ? chatbot.uuid || 'default' : null;
};

/**
 * Get a storage key
 *
 * @param {string} userUuid User UUID
 * @return {string}
 */
const getStorageKey = (userUuid) => {
    return STORAGE_KEY + (userUuid ? '.' + userUuid : '');
};

/**
 * Get chatbots data from sessionStorage
 *
 * @param {string} userUuid User UUID
 * @return {object} Chatbots data object
 */
const getChatbotsStorageData = (userUuid) => {
    try {
        const storageData = localStorage.getItem(getStorageKey(userUuid)) || {};
        if (!storageData) {
            return {};
        }
        return JSON.parse(storageData);
    } catch (e) {
        return {};
    }
};

/**
 * Save chatbots data to sessionStorage
 *
 * @param {object} chatbotsData Chatbots data object
 * @param {string} userUuid User UUID
 */
const saveChatbotsStorageData = (chatbotsData, userUuid) => {
    try {
        localStorage.setItem(getStorageKey(userUuid), JSON.stringify(chatbotsData));
    } catch (e) {
        // Silently fail if sessionStorage is not available
    }
};

/**
 * Check if widgets have been shown for a chatbot
 * @param {string} chatbotId Chatbot identifier (uuid or 'default')
 * @param {string} widgetShownKey MESSAGES_SHOWN_KEY|PROMPTS_SHOWN_KEY
 * @param {string} userUuid User UUID
 * @return {boolean} True if widgets have been shown
 */
const haveWidgetBeenShown = (chatbotId, widgetShownKey, userUuid) => {
    const chatbotsData = getChatbotsStorageData(userUuid);
    const chatbotData = chatbotsData[chatbotId];
    return chatbotData?.[widgetShownKey] === true;
};

/**
 * Mark widgets as shown for a chatbot
 * @param {string} chatbotId Chatbot identifier (uuid or 'default')
 * @param {string} userUuid User UUID
 * @param {string} widgetShownKey MESSAGES_SHOWN_KEY|PROMPTS_SHOWN_KEY
 */
const markWidgetsAsShown = (chatbotId, widgetShownKey, userUuid) => {
    const chatbotsData = getChatbotsStorageData(userUuid);

    // Initialize chatbot data if it doesn't exist
    if (!chatbotsData[chatbotId]) {
        chatbotsData[chatbotId] = {};
    }

    // Mark widgets as shown
    chatbotsData[chatbotId][widgetShownKey] = true;

    // Save back to sessionStorage
    saveChatbotsStorageData(chatbotsData, userUuid);
};

export default function ChatbotIconWidgets({leadCaptureActionRef, capturedState, isInPreviewMode, onLeadCaptureBlockChange}) {
    const chatbotsContext = useContext(ChatBotsContext);
    const chatbot = chatbotsContext.chatbots[chatbotsContext.activeChatbotIndex];
    const [dismissedWidgets, setDismissedWidgets] = useState([]);
    const [dismissedPrompts, setDismissedPrompts] = useState(false);
    const [showTypingIndicator, setShowTypingIndicator] = useState(false);
    const [showWidgets, setShowWidgets] = useState(false);
    const [widgetsInitiallyShown, setWidgetsInitiallyShown] = useState(false);
    const visibleWidgetsLengthRef = useRef(0);
    const [leadCaptureDismissed, setLeadCaptureDismissed] = useState(false);
    const [forceShowLeadCaptureAgain, setForceShowLeadCaptureAgain] = useState(false);
    const leadCaptureGroupRef = useRef(null);

    const chatbotId = useMemo(() => getChatbotId(chatbot), [chatbot]);

    const userUuid = getCookie('lbaic_chatbot_user_uuid') || '';

    // Sound notification hook (chatbot is not opened, so isOnChatScreen is false)
    const playNotificationSound = useSoundNotification(chatbot.uuid, chatbot.utility?.sound, false);

    const [messagesShown, setMessagesShown] = useState(() => chatbotId ? haveWidgetBeenShown(chatbotId, MESSAGES_SHOWN_KEY, userUuid) : false);
    const [promptsShown, setPromptsShown] = useState(() => chatbotId ? haveWidgetBeenShown(chatbotId, PROMPTS_SHOWN_KEY, userUuid) : false);
    // Lead capture: non-preview = API _captured_state + local; preview = also use local storage (like messages/prompts)
    const [leadCaptureSubmittedLocal, setLeadCaptureSubmittedLocal] = useState(false);
    const [leadCaptureShownFromStorage, setLeadCaptureShownFromStorage] = useState(false);
    const leadCaptureSubmitted = Boolean(capturedState) || leadCaptureSubmittedLocal;

    const chatbotIconWidgets = useMemo(() => {
        if (!chatbot?.utility?.widgets?.length) {
            return [];
        }
        return chatbot.utility.widgets.filter(widget =>
            widget.published
            && (widget.type === 'message' || widget.type === 'prompt' || widget.type === 'lead_capture')
            && showWidget(widget.locations, {screen: 'chatbot_icon'})
            && !dismissedWidgets.includes(widget.id)
        );
    }, [chatbot?.utility?.widgets, dismissedWidgets]);

    const visibleWidgets = useMemo(() => {
        return chatbotIconWidgets.filter(widget => {
            if (widget.type === 'message' && messagesShown) {
                return false;
            }
            if (widget.type === 'prompt' && (promptsShown || dismissedPrompts)) {
                return false;
            }
            if (widget.type === 'lead_capture') {
                if (isInPreviewMode) {
                    if (leadCaptureShownFromStorage) {
                        return false;
                    }
                } else if (leadCaptureSubmitted) {
                    return false;
                } else if (leadCaptureDismissed && !forceShowLeadCaptureAgain) {
                    return false;
                }

            }
            return true;
        });
    }, [chatbotIconWidgets, messagesShown, promptsShown, dismissedPrompts, leadCaptureSubmitted, leadCaptureDismissed, forceShowLeadCaptureAgain, isInPreviewMode, leadCaptureShownFromStorage]);

    // Preview only: read lead capture shown state from storage (like messages/prompts)
    useEffect(() => {
        if (!isInPreviewMode || !chatbotId) return;
        setLeadCaptureShownFromStorage(haveWidgetBeenShown(chatbotId, LEAD_CAPTURE_PREVIEW_SHOWN_KEY, userUuid));
    }, [isInPreviewMode, chatbotId, userUuid]);

    const leadCaptureRequired = useMemo(() => {
        const lc = chatbotIconWidgets.find(w => w.type === 'lead_capture');
        return Boolean(lc?.data?.required);
    }, [chatbotIconWidgets]);

    const messageWidgetsToNotify = useMemo(() => {
        return visibleWidgets.filter(widget => widget.type === 'message' && widget.notify);
    }, [visibleWidgets]);

    const handleDismissMessage = () => {
        const allMessageWidgets = (chatbot?.utility?.widgets || [])
            .filter(widget =>
                widget.published
                && widget.type === 'message'
                && showWidget(widget.locations, {screen: 'chatbot_icon'})
            );

        const allMessageWidgetIds = allMessageWidgets.map(widget => widget.id);

        if (allMessageWidgetIds.length > 0) {
            setDismissedWidgets(prev => [...new Set([...prev, ...allMessageWidgetIds])]);
        }

        if (chatbotId && !messagesShown) {
            markWidgetsAsShown(chatbotId, MESSAGES_SHOWN_KEY, userUuid);
        }

        // Decrement document title count and unseen messages badge
        if (notificationsPlayedStore[chatbotId]?.count > 0) {
            playNotificationSound(-notificationsPlayedStore[chatbotId].count)
            notificationsPlayedStore[chatbotId].count = 0;
        }
    };

    const handleDismissAllPrompts = () => {
        setDismissedPrompts(true);
        if (chatbotId && !promptsShown) {
            markWidgetsAsShown(chatbotId, PROMPTS_SHOWN_KEY, userUuid);
        }
    };

    const handleDismissLeadCapture = () => {
        if (isInPreviewMode && chatbotId) {
            markWidgetsAsShown(chatbotId, LEAD_CAPTURE_PREVIEW_SHOWN_KEY, userUuid);
            setLeadCaptureShownFromStorage(true);
        }
        setLeadCaptureDismissed(true);
    };

    const handleLeadCaptureSubmitted = () => {
        // Unblock immediately so openChatbot (called after this) does not block
        onLeadCaptureBlockChange?.(false);
        if (leadCaptureActionRef?.current) {
            leadCaptureActionRef.current.blockOpen = false;
        }
        if (isInPreviewMode && chatbotId) {
            markWidgetsAsShown(chatbotId, LEAD_CAPTURE_PREVIEW_SHOWN_KEY, userUuid);
            setLeadCaptureShownFromStorage(true);
        }
        setLeadCaptureSubmittedLocal(true);
    };

    const groupedWidgets = useMemo(() => {
        const groups = [];
        let currentGroup = null;

        visibleWidgets.forEach((widget, index) => {
            if (!currentGroup || currentGroup.type !== widget.type) {
                currentGroup = {
                    type: widget.type,
                    widgets: [widget],
                    startIndex: index
                };
                groups.push(currentGroup);
            } else {
                currentGroup.widgets.push(widget);
            }
        });

        return groups;
    }, [visibleWidgets]);

    // Animation duration per group type based on item count (matches SCSS: message 0.4s + 0.08s stagger, prompt 0.5s + 0.25s stagger)
    const getGroupDuration = (type, count = 1) => {
        const n = Math.max(1, count);
        if (type === 'message') return (n - 1) * 0.03 + 0.2;
        if (type === 'prompt') return (n - 1) * 0.35 + 0.2;
        if (type === 'lead_capture') return 0.2;
        return (n - 1) * 0.2 + 0.5;
    };

    // Cumulative end time (seconds) before each group – lead_capture uses this as animationDelay
    const leadCaptureDelayByGroupIndex = useMemo(() => {
        const delays = [];
        let cumulative = 0;
        groupedWidgets.forEach((group, i) => {
            delays[i] = cumulative;
            cumulative += getGroupDuration(group.type, group.widgets?.length ?? 1);
        });
        return delays;
    }, [groupedWidgets]);

    const firstMessageId = visibleWidgets.find(widget => widget.type === 'message')?.id || null;
    const firstPromptId = visibleWidgets.find(widget => widget.type === 'prompt')?.id || null;
    const hasMessageWidgets = visibleWidgets.some(widget => widget.type === 'message');

    // Get the maximum appear_after delay from message widgets
    const messageWidgetDelay = useMemo(() => {
        const messageWidgets = visibleWidgets.filter(widget => widget.type === 'message');
        if (!messageWidgets.length) return 0;
        const delays = messageWidgets.map(widget => (widget.appear_after ?? 0) * 1000);
        return Math.max(...delays);
    }, [visibleWidgets]);

    useEffect(() => {
        visibleWidgetsLengthRef.current = visibleWidgets.length;
    }, [visibleWidgets.length]);

    useEffect(() => {
        if (widgetsInitiallyShown) {
            setShowTypingIndicator(false);
            setShowWidgets(visibleWidgets.length > 0);
            return;
        }

        if (!visibleWidgets.length) {
            setShowTypingIndicator(false);
            setShowWidgets(false);
            return;
        }

        if (hasMessageWidgets) {
            // Wait for the specified delay before showing typing indicator
            const initialDelayTimer = setTimeout(() => {
                setShowTypingIndicator(true);
                setShowWidgets(false);

                // After typing indicator, show widgets
                const typingTimer = setTimeout(() => {
                    if (visibleWidgetsLengthRef.current > 0) {
                        setShowTypingIndicator(false);
                        setShowWidgets(true);
                        setWidgetsInitiallyShown(true);
                    }
                }, 2000);

                return () => clearTimeout(typingTimer);
            }, messageWidgetDelay);

            return () => clearTimeout(initialDelayTimer);
        } else {
            setShowTypingIndicator(false);
            setShowWidgets(true);
            setWidgetsInitiallyShown(true);
        }
    }, [visibleWidgets.length, hasMessageWidgets, messageWidgetDelay, widgetsInitiallyShown]);

    // Play notification sound for message widgets with notify: true
    // Use module-level store to track notifications in current page session
    // SessionStorage (messagesShown) is only updated on dismiss
    useEffect(() => {
        // Reset notifs played state for new messages
        if (messageWidgetsToNotify.length !== notificationsPlayedStore[chatbotId]?.count) {
            notificationsPlayedStore[chatbotId] = {
                ...(notificationsPlayedStore[chatbotId] || {}),
                played: false,
            }
        }

        // Only play if:
        // 1. Widgets are being shown
        // 2. Haven't played notification for this chatbot in current page session
        // 3. Messages haven't been dismissed (messagesShown from sessionStorage)
        if (
            showWidgets
            && !notificationsPlayedStore[chatbotId]?.played
            && !messagesShown
        ) {
            if (messageWidgetsToNotify.length > 0) {
                playNotificationSound(messageWidgetsToNotify.length - (notificationsPlayedStore[chatbotId]?.count || 0));
                // Mark as notified in the module store (persists during page session only)
                notificationsPlayedStore[chatbotId] = {
                    ...(notificationsPlayedStore[chatbotId] || {}),
                    count: messageWidgetsToNotify.length,
                    played: true,
                };
            }
        }
    }, [showWidgets, messageWidgetsToNotify.length, messagesShown, playNotificationSound, chatbotId]);

    // Update lead capture action ref for parent (Chatbots) to block open / shake / show again
    useEffect(() => {
        const hasVisibleLeadCapture = showWidgets && visibleWidgets.some(w => w.type === 'lead_capture');
        const leadCaptureBlockOpen = leadCaptureRequired && !leadCaptureSubmitted && !(isInPreviewMode && leadCaptureShownFromStorage);
        onLeadCaptureBlockChange?.(leadCaptureBlockOpen);
        if (!leadCaptureActionRef) {
            return;
        }
        leadCaptureActionRef.current = {
            blockOpen: leadCaptureBlockOpen,
            isVisible: hasVisibleLeadCapture,
            shake: () => {
                const el = leadCaptureGroupRef.current;
                if (!el) {
                    return;
                }
                const shakeClass = 'lbaic-chatbot-icon-lead-capture-shake';
                el.classList.remove(shakeClass);
                el.offsetHeight; // force reflow so adding the class restarts the animation
                el.classList.add(shakeClass);
                window.setTimeout(() => el.classList.remove(shakeClass), 500);
            },
            showAgain: () => {
                setForceShowLeadCaptureAgain(true);
                setLeadCaptureDismissed(false);
            },
        };
    }, [leadCaptureActionRef, leadCaptureRequired, leadCaptureSubmitted, leadCaptureShownFromStorage, isInPreviewMode, showWidgets, visibleWidgets, onLeadCaptureBlockChange]);

    // Listen for the session storage cleared action to reset the widget state
    useEffect(() => {
        const handleStorageCleared = () => {
            // Reset all widget state to allow widgets to be shown again (messages, prompts, lead capture)
            setDismissedWidgets([]);
            setDismissedPrompts(false);
            setMessagesShown(false);
            setPromptsShown(false);
            setLeadCaptureDismissed(false);
            setForceShowLeadCaptureAgain(false);
            setLeadCaptureShownFromStorage(false);
            setLeadCaptureSubmittedLocal(false);
        };

        LimbChatbot?.Hooks?.addAction('lbaic.chatbot.icon.widgets.storage.cleared', 'lbaic/chatbot-icon-widgets/storage-cleared', handleStorageCleared);

        return () => {
            LimbChatbot?.Hooks?.removeAction('lbaic.chatbot.icon.widgets.storage.cleared', 'lbaic/chatbot-icon-widgets/storage-cleared');
        };
    }, [chatbotId, chatbot?.utility?.widgets, chatbot?.uuid]);

    const theme = useTheme(chatbot.utility?.theme || 'system');

    if (!visibleWidgets.length) {
        return null;
    }

    return (
        <div className={`lbaic-chatbot-icon-widgets lbaic-${theme}`}>
            {showTypingIndicator && (
                <div className="lbaic-chatbot-icon-typing-indicator lbaic-fade-in-up">
                    {chatbot?.utility?.avatar && (
                        <div className="lbaic-chatbot-icon-typing-avatar">
                            <svg xmlns='http://www.w3.org/2000/svg' fill='none'>
                                <use href={`#lbaic-chatbot-avatar-${chatbot.utility.avatar}`}/>
                            </svg>
                        </div>
                    )}
                    <div className="lbaic-chatbot-icon-typing-indicator-content">
                        <div className="lbaic-chatbot-icon-typing-indicator-dot"></div>
                        <div className="lbaic-chatbot-icon-typing-indicator-dot"></div>
                        <div className="lbaic-chatbot-icon-typing-indicator-dot"></div>
                    </div>
                </div>
            )}

            {showWidgets && groupedWidgets.map((group, groupIndex) => (
                <div
                    key={`group-${group.type}-${group.widgets[0]?.id ?? groupIndex}`}
                    className={`lbaic-chatbot-icon-widget-group lbaic-chatbot-icon-widget-group-${group.type} lbaic-fade-in-up`}
                >
                    {group.type === 'message' && (
                        <div className="lbaic-chatbot-icon-message-group">
                            {group.widgets.map((widget, widgetIndex) => (
                                <MessageWidget
                                    key={widget.id}
                                    widget={widget}
                                    index={group.startIndex + widgetIndex}
                                    inGroupIndex={widgetIndex}
                                    showDismiss={widget.id === firstMessageId}
                                    onDismiss={handleDismissMessage}
                                />
                            ))}
                        </div>
                    )}
                    {group.type === 'prompt' && (
                        <div className="lbaic-chatbot-icon-prompt-group">
                            {group.widgets.map((widget, widgetIndex) => (
                                <PromptWidget
                                    key={widget.id}
                                    widget={widget}
                                    index={group.startIndex + widgetIndex}
                                    showDismiss={widget.id === firstPromptId}
                                    onDismissAll={handleDismissAllPrompts}
                                    chatbotUuid={chatbot.uuid}
                                />
                            ))}
                        </div>
                    )}
                    {group.type === 'lead_capture' && (
                        <div ref={leadCaptureGroupRef} className="lbaic-chatbot-icon-lead-capture-group">
                            {group.widgets.map((widget) => (
                                <LeadCaptureWidget
                                    key={widget.id}
                                    widget={widget}
                                    chatbot={chatbot}
                                    onDismiss={handleDismissLeadCapture}
                                    onSubmitted={handleLeadCaptureSubmitted}
                                    isInPreviewMode={isInPreviewMode}
                                    animationDelay={leadCaptureDelayByGroupIndex[groupIndex] ?? 0}
                                />
                            ))}
                        </div>
                    )}
                </div>
            ))}
        </div>
    );
}

// Message widget component
function MessageWidget({widget, index, inGroupIndex, showDismiss, onDismiss}) {
    const chatbotsContext = useContext(ChatBotsContext);
    const chatbot = chatbotsContext.chatbots[chatbotsContext.activeChatbotIndex];

    const handleClick = useCallback(() => {
        LimbChatbot?.Hooks?.doAction('lbaic.chatbot.open', chatbot?.uuid, 'chat');
        if (onDismiss) {
            onDismiss();
        }
    }, [chatbot?.uuid]);

    const handleDismiss = (e) => {
        e.stopPropagation();
        if (onDismiss) {
            onDismiss();
        }
    };

    if (!widget.data?.content?.[0]?.text?.value) {
        return null;
    }

    return (
        <div className={`lbaic-chatbot-icon-message-widget lbaic-chatbot-icon-widget-item-${index}`}>
            <div className="lbaic-chatbot-icon-message-widget-content">
                {!inGroupIndex && (
                    <div className="lbaic-chatbot-icon-message-widget-avatar">
                        {chatbot?.utility?.avatar && (
                            <svg xmlns='http://www.w3.org/2000/svg' fill='none'>
                                <use href={`#lbaic-chatbot-avatar-${chatbot.utility.avatar}`}/>
                            </svg>
                        )}
                    </div>
                )}
                <div className="lbaic-chatbot-icon-message-widget-bubble" onClick={handleClick}>
                    <span className="lbaic-chatbot-icon-message-widget-text">
                        {widget.data?.content?.[0]?.text?.value}
                    </span>
                </div>
                {showDismiss && (
                    <button
                        onClick={handleDismiss}
                        className="lbaic-chatbot-icon-widget-dismiss"
                        aria-label={__("Dismiss message", 'limb-chatbot')}
                    >
                        <svg
                             xmlns='http://www.w3.org/2000/svg'
                             viewBox='0 0 24 24'
                             fill="none"
                             stroke="currentColor"
                        >
                            <use href="#lbaic-dismiss"/>
                        </svg>
                    </button>
                )}
            </div>
        </div>
    );
}

// Prompt widget component
function PromptWidget({widget, index, showDismiss, onDismissAll, chatbotUuid}) {
    const handleClick = (promptText) => {
        if (typeof LimbChatbot !== 'undefined' && LimbChatbot?.Hooks) {
            LimbChatbot.Hooks.applyFiltersAsync('lbaic.chatbot.sendMessage', null, promptText, chatbotUuid);
        }
    };

    const handleDismissAll = (e) => {
        e.stopPropagation();
        if (onDismissAll) {
            onDismissAll();
        }
    };

    if (!widget.data?.content?.[0]?.text?.value) {
        return null;
    }

    // Use Message field if available, otherwise fallback to Content for backward compatibility
    const messageToSend = widget.data?.message || widget.data?.content?.[0]?.text?.value || '';
    // Use Content as label
    const labelText = widget.data?.content?.[0]?.text?.value || '';

    return (
        <div className={`lbaic-chatbot-icon-prompt-widget lbaic-chatbot-icon-widget-item-${index}`}>
            <div className="lbaic-chatbot-icon-prompt-widget-content">
                {showDismiss && (
                    <button
                        onClick={handleDismissAll}
                        className="lbaic-chatbot-icon-widget-dismiss"
                        aria-label={__("Dismiss all prompts", 'limb-chatbot')}
                    >
                        <svg
                            xmlns='http://www.w3.org/2000/svg'
                            viewBox='0 0 24 24'
                            fill="none"
                            stroke="currentColor"
                        >
                            <use href="#lbaic-dismiss"/>
                        </svg>
                    </button>
                )}
                <div className="lbaic-chatbot-icon-prompt-widget-button"
                     onClick={() => handleClick(messageToSend)}>
                    <span className="lbaic-chatbot-icon-prompt-widget-button-text">{labelText}</span>
                </div>
            </div>
        </div>
    );
}

// Lead capture widget component (near chatbot icon – Figma ChatbotLeadCaptureWidget)
function LeadCaptureWidget({widget, chatbot, onDismiss, onSubmitted, isInPreviewMode, animationDelay = 0}) {
    const visibleFields = widget?.data?.fields || [];
    const heading = widget?.data?.heading || __("Let's get started", 'limb-chatbot');
    const description = widget?.data?.description || __("Please share a few details to begin chatting", 'limb-chatbot');
    const submitButtonText = widget?.data?.submit_button_text || __("Start Chat", 'limb-chatbot');

    const chatbotUtility = useMemo(() => {
        return chatbot?.uuid ? new ChatbotUtility(chatbot.uuid, '') : null;
    }, [chatbot?.uuid]);

    const [formData, setFormData] = useState(() => {
        const initial = {};
        visibleFields.forEach(field => {
            initial[field.name] = '';
        });
        return initial;
    });
    const [errors, setErrors] = useState({});
    const [isSubmitting, setIsSubmitting] = useState(false);
    const [captureError, setCaptureError] = useState('');

    const handleInputChange = useCallback((name, value, field) => {
        setFormData(prev => ({...prev, [name]: value}));
        if (field.type === 'email') {
            setErrors(prev => ({...prev, [name]: value.trim() ? !validateEmailInput(value) : field.required}));
        } else if (field.required) {
            setErrors(prev => ({...prev, [name]: !value.trim()}));
        }
    }, []);

    const validateForm = useCallback(() => {
        const newErrors = {};
        visibleFields.forEach(field => {
            const value = formData[field.name] || '';
            if (field.required && !value.trim()) {
                newErrors[field.name] = true;
            } else if (field.type === 'email' && value.trim() && !validateEmailInput(value.trim())) {
                newErrors[field.name] = true;
            }
        });
        setErrors(newErrors);
        return Object.keys(newErrors).length === 0;
    }, [visibleFields, formData]);

    const handleSubmit = useCallback(async (e) => {
        e.preventDefault();
        if (!validateForm()) return;
        setCaptureError('');
        setIsSubmitting(true);
        try {
            if (!isInPreviewMode) {
                const res = await chatbotUtility?.widgetCallback(widget.id, formData);
                if (!res?.success) {
                    setCaptureError(res?.data?.message || __("Something went wrong. Please try again.", 'limb-chatbot'));
                    setIsSubmitting(false);
                    return;
                }
                if (typeof LimbChatbot !== 'undefined' && LimbChatbot?.Hooks) {
                    LimbChatbot.Hooks.doAction('limb.chatbot.lead_captured', res);
                }
            }
            onSubmitted?.();
            LimbChatbot?.Hooks?.doAction('lbaic.chatbot.open', chatbot?.uuid, 'chat');
        } catch (err) {
            setCaptureError(__("Something went wrong. Please try again.", 'limb-chatbot'));
        } finally {
            setIsSubmitting(false);
        }
    }, [validateForm, chatbotUtility, widget?.id, formData, onSubmitted, chatbot?.uuid, isInPreviewMode]);

    const handleDismiss = (e) => {
        e.stopPropagation();
        onDismiss();
    };

    if (!widget?.data) {
        return null;
    }

    const hasRequiredEmpty = visibleFields.some(f => f.required && !(formData[f.name] || '').trim());
    const hasErrors = Object.values(errors).some(Boolean);
    const isButtonDisabled = isSubmitting || hasRequiredEmpty || hasErrors;

    return (
        <div
            className="lbaic-chatbot-icon-lead-capture lbaic-chatbot-icon-widget-group-lead-capture-form"
            style={animationDelay > 0 ? {animationDelay: `${animationDelay}s`} : undefined}
        >
            <div className="lbaic-chatbot-icon-lead-capture-container">
                <div className="lbaic-chatbot-icon-lead-capture-header">
                    <h3 className="lbaic-chatbot-icon-lead-capture-title">{heading}</h3>
                    <p className="lbaic-chatbot-icon-lead-capture-description" dangerouslySetInnerHTML={{__html: marked.parse(description)}}/>
                </div>
                <form className="lbaic-chatbot-icon-lead-capture-form" onSubmit={handleSubmit}>
                    <div className="lbaic-chatbot-icon-lead-capture-fields">
                        {visibleFields.map((field, index) => {
                            const value = formData[field.name] || '';
                            const hasError = errors[field.name];
                            const id = `lbaic-chatbot-icon-lead-capture-${widget.id}-${field.name}`;
                            return (
                                <div key={index} className="lbaic-chatbot-icon-lead-capture-field">
                                    <label htmlFor={id} className="lbaic-chatbot-icon-lead-capture-label">
                                        {field.label || (field.type === 'email' ? __("Email", 'limb-chatbot') : __("Name", 'limb-chatbot'))}
                                        {field.required &&
                                            <sup className="lbaic-chatbot-icon-lead-capture-required"> *</sup>}
                                    </label>
                                    <input
                                        id={id}
                                        type={field.type === 'email' ? 'email' : 'text'}
                                        className={`lbaic-chatbot-icon-lead-capture-input${hasError ? ' lbaic-chatbot-icon-lead-capture-input-invalid' : ''}`}
                                        placeholder={field.placeholder || ''}
                                        value={value}
                                        onChange={(e) => handleInputChange(field.name, e.target.value, field)}
                                        disabled={isSubmitting}
                                        autoComplete="off"
                                    />
                                </div>
                            );
                        })}
                    </div>
                    <button
                        type="submit"
                        className="lbaic-chatbot-icon-lead-capture-submit"
                        disabled={isButtonDisabled}
                    >
                        {isSubmitting ? __("Sending…", 'limb-chatbot') : submitButtonText}
                    </button>
                    {captureError && (
                        <div className="lbaic-chatbot-icon-lead-capture-error">
                            <span className="lbaic-chatbot-icon-lead-capture-error-text">{captureError}</span>
                        </div>
                    )}
                </form>
            </div>
            <button
                type="button"
                onClick={handleDismiss}
                className="lbaic-chatbot-icon-widget-dismiss"
                aria-label={__("Dismiss form", 'limb-chatbot')}
            >
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor">
                    <use href="#lbaic-dismiss"/>
                </svg>
            </button>
        </div>
    );
}