/**
 * Conversion Busters
 *
 * Single state machine for cart-triggered and mini CB flows.
 * Keeps admin settings/AJAX endpoints, but removes layered patch logic.
 */
(function ($) {
    'use strict';


    var F = window.NiwaAIFrontend || {};

    var STATES = {
        IDLE: 'idle',
        AWAITING_REPLY: 'awaiting_confirm',
        RESOLVING_REPLY: 'resolving_confirm',
        AWAITING_EMAIL: 'awaiting_email',
        GENERATING: 'creating_coupon',
        COMPLETED: 'coupon_sent',
        DECLINED: 'offered'
    };

    function storageBaseKey() {
        try {
            if (window.Niwa && window.Niwa.Utils && window.Niwa.Utils.STORAGE_KEY) {
                return window.Niwa.Utils.STORAGE_KEY;
            }
            if (window.NiwaAI && window.NiwaAI.storage_key) {
                return window.NiwaAI.storage_key;
            }
        } catch (e) { /* ignore */ }
        return 'niwa_ai_chat_history_v1';
    }

    function runtimeKey() {
        return storageBaseKey() + '_cb_runtime';
    }

    function defaultRuntime() {
        return {
            active: false,
            state: STATES.IDLE,
            mode: 'cart',
            offerMessage: '',
            offerSource: 'ConversionBuster',
			offerToken: '',
            couponCode: '',
            couponMessage: '',
            replyMessage: '',
            updatedAt: 0
        };
    }

    function readRuntime() {
        try {
            var raw = localStorage.getItem(runtimeKey());
            if (!raw) return defaultRuntime();
            var parsed = JSON.parse(raw);
            if (!parsed || typeof parsed !== 'object') return defaultRuntime();
            return Object.assign(defaultRuntime(), parsed);
        } catch (e) {
            return defaultRuntime();
        }
    }

    function syncLegacyFlags(runtime) {
        runtime = runtime || defaultRuntime();
        try {
            if (runtime.state && runtime.state !== STATES.IDLE) {
                sessionStorage.setItem('niwa_ai_cb_state', runtime.state);
            } else {
                sessionStorage.removeItem('niwa_ai_cb_state');
            }

            if (runtime.state === STATES.AWAITING_REPLY || runtime.state === STATES.RESOLVING_REPLY) {
                sessionStorage.setItem('niwa_ai_cb_confirm_pending', '1');
            } else {
                sessionStorage.removeItem('niwa_ai_cb_confirm_pending');
            }

            if (runtime.active) {
                sessionStorage.setItem('niwa_ai_cb_active', '1');
            } else {
                sessionStorage.removeItem('niwa_ai_cb_active');
            }

            if (runtime.mode === 'mini' && runtime.active) {
                sessionStorage.setItem('niwa_ai_cb_mini_active', '1');
            } else {
                sessionStorage.removeItem('niwa_ai_cb_mini_active');
            }
        } catch (e) { /* ignore */ }
    }

    function writeRuntime(next) {
        var runtime = Object.assign(defaultRuntime(), next || {});
        runtime.updatedAt = Date.now();
        try {
            localStorage.setItem(runtimeKey(), JSON.stringify(runtime));
        } catch (e) { /* ignore */ }
        syncLegacyFlags(runtime);
        return runtime;
    }

    function patchRuntime(patch) {
        return writeRuntime(Object.assign(readRuntime(), patch || {}));
    }

    function resetRuntime(state) {
        var runtime = defaultRuntime();
        if (state) {
            runtime.state = state;
        }
        return writeRuntime(runtime);
    }

    function isBlockingState(state) {
        return state === STATES.AWAITING_REPLY
            || state === STATES.RESOLVING_REPLY
            || state === STATES.AWAITING_EMAIL
            || state === STATES.GENERATING;
    }

    function getBlockingFlowState() {
        var runtime = readRuntime();
        return isBlockingState(runtime.state) ? runtime.state : '';
    }

    function hasBlockingFlow() {
        return !!getBlockingFlowState();
    }

    function getStoredEmail() {
        var email = '';
        try {
            email = sessionStorage.getItem('niwa_validated_email') || '';
        } catch (e) { /* ignore */ }

        if (!email && F.state && F.state.chatState && F.state.chatState.userEmail) {
            email = String(F.state.chatState.userEmail || '');
        }

        return email ? String(email).trim() : '';
    }

    function persistEmail(email) {
        email = String(email || '').trim();
        if (!email) return;

        try {
            sessionStorage.setItem('niwa_validated_email', email);
            sessionStorage.setItem('niwa_ai_email_collected', '1');
        } catch (e) { /* ignore */ }

        if (F.state) {
            F.state.chatState = F.state.chatState || {};
            F.state.chatState.userEmail = email;
            F.state.chatState.emailValidated = true;
            F.state.chatState.awaitingEmail = false;
            F.state.chatState.cbWaitingForCoupon = false;
            F.state.chatState.cbReadyForReset = false;
            if (typeof F.state.saveChatState === 'function') {
                F.state.saveChatState();
            }
        }
    }

    function persistPhone(phone) {
        phone = String(phone || '').trim();
        if (!phone || !F.state) return;

        F.state.chatState = F.state.chatState || {};
        F.state.chatState.userPhone = phone;
        F.state.chatState.phoneValidated = true;
        F.state.chatState.awaitingPhone = false;
        if (typeof F.state.saveChatState === 'function') {
            F.state.saveChatState();
        }
    }

    function setPostFlowMute(val) {
        if (F.state && typeof F.state.setPostFlowMute === 'function') {
            F.state.setPostFlowMute(!!val);
        }
    }

    function openChat() {
        if (window.Niwa && window.Niwa.MorphController) {
            window.Niwa.MorphController.open();
        } else if (window.niwaAI && typeof window.niwaAI.openChat === 'function') {
            window.niwaAI.openChat();
        }
    }

    function scrollBottom() {
        if (F.animations && typeof F.animations.scrollBottom === 'function') {
            setTimeout(F.animations.scrollBottom, 0);
            setTimeout(F.animations.scrollBottom, 160);
        }
    }

    function getRenderer() {
        return window.Niwa && window.Niwa.MessageRenderer ? window.Niwa.MessageRenderer : null;
    }

    function getHistory() {
        var renderer = getRenderer();
        if (renderer && typeof renderer.getHistory === 'function') {
            return renderer.getHistory() || [];
        }

        try {
            var raw = localStorage.getItem(storageBaseKey());
            if (!raw) return [];
            var parsed = JSON.parse(raw);
            return Array.isArray(parsed) ? parsed : [];
        } catch (e) {
            return [];
        }
    }

    function hasTextInHistory(role, text) {
        text = String(text || '').trim();
        if (!text) return false;

        var history = getHistory();
        for (var i = 0; i < history.length; i++) {
            var entry = history[i];
            if (!entry || (entry.type && entry.type !== 'text')) continue;
            if ((entry.role || 'bot') !== role) continue;
            if (String(entry.content || entry.text || '').trim() === text) {
                return true;
            }
        }
        return false;
    }

    function saveHistoryNow() {
        var renderer = getRenderer();
        if (renderer && typeof renderer.saveHistory === 'function') {
            renderer.saveHistory();
        }
    }

    function backendSessionId() {
        return typeof F.getSessionId === 'function' ? F.getSessionId() : '';
    }

    function appendBackendHistory(entries) {
        if (!Array.isArray(entries) || !entries.length) return;
        if (typeof NiwaCB === 'undefined' || !NiwaCB || !NiwaCB.ajax_url || !NiwaCB.nonce) return;

        $.post(NiwaCB.ajax_url, {
            action: 'niwa_ai_cb_append_history',
            nonce: NiwaCB.nonce,
            session: backendSessionId(),
            entries: JSON.stringify(entries)
        });
    }

    function pruneBackendHistory(entries) {
        if (!Array.isArray(entries) || !entries.length) return;
        if (typeof NiwaCB === 'undefined' || !NiwaCB || !NiwaCB.ajax_url || !NiwaCB.nonce) return;

        $.post(NiwaCB.ajax_url, {
            action: 'niwa_ai_cb_prune_history',
            nonce: NiwaCB.nonce,
            session: backendSessionId(),
            entries: JSON.stringify(entries)
        });
    }

    function normalizeHistoryText(text) {
        return String(text || '').replace(/\s+/g, ' ').trim();
    }

    var PRUNE_KEY_SEPARATOR = '::';

    function addPruneEntry(entries, role, text) {
        text = normalizeHistoryText(text);
        if (!text) return;
        for (var i = 0; i < entries.length; i++) {
            if (entries[i].role === role && entries[i].text === text) return;
        }
        entries.push({ role: role, text: text });
    }

    function collectCartFlowPruneEntries(runtime) {
        var entries = [];
        runtime = runtime || readRuntime();

        addPruneEntry(entries, 'bot', runtime.offerMessage || '');
        addPruneEntry(entries, 'bot', getLocalizedMessage('email_prompt_message'));
        addPruneEntry(entries, 'bot', getLocalizedMessage('clarify_message'));
        addPruneEntry(entries, 'user', runtime.replyMessage || '');

        return entries;
    }

    function buildPruneMap(entries) {
        if (!Array.isArray(entries) || !entries.length) return 0;

        var removeMap = {};
        for (var i = 0; i < entries.length; i++) {
            removeMap[entries[i].role + PRUNE_KEY_SEPARATOR + normalizeHistoryText(entries[i].text)] = true;
        }

        return removeMap;
    }

    function removeLocalHistoryByEntries(entries) {
        var removeMap = buildPruneMap(entries);
        if (!removeMap) return 0;

        var history = getHistory();
        if (!Array.isArray(history) || !history.length) return 0;

        var filtered = [];
        var removed = 0;
        for (var j = 0; j < history.length; j++) {
            var entry = history[j] || {};
            var role = entry.role || 'bot';
            var text = normalizeHistoryText(entry.content || entry.text || '');
            if (removeMap[role + PRUNE_KEY_SEPARATOR + text]) {
                removed++;
                continue;
            }
            filtered.push(entry);
        }

        if (removed > 0) {
            try {
                localStorage.setItem(storageBaseKey(), JSON.stringify(filtered));
            } catch (e) { /* ignore */ }
        }

        return removed;
    }

    function removeLocalDomByEntries(entries) {
        var removeMap = buildPruneMap(entries);
        if (!removeMap) return 0;

        var selector = [
            '[data-source="ConversionBuster"]',
            '[data-source="ConversionBusterUser"]'
        ].join(',');
        var nodes = document.querySelectorAll(selector);
        var removed = 0;

        for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i];
            var role = node.classList && node.classList.contains('niwa-message--user') ? 'user' : 'bot';
            var textEl = node.querySelector ? node.querySelector('.niwa-message__text') : null;
            var text = normalizeHistoryText((textEl && textEl.textContent) || node.textContent || '');
            if (!removeMap[role + PRUNE_KEY_SEPARATOR + text]) continue;
            if (node.parentNode) {
                node.parentNode.removeChild(node);
                removed++;
            }
        }

        return removed;
    }

    function removeLocalCartFlowHistory(runtime, entries) {
        var renderer = getRenderer();
        if (renderer && typeof renderer.removeBySource === 'function') {
            return (renderer.removeBySource('ConversionBuster', { entries: entries }) || 0)
                + (renderer.removeBySource('ConversionBusterUser', { entries: entries }) || 0);
        }

        var removed = removeLocalDomByEntries(entries);
        removed += removeLocalHistoryByEntries(entries);

        return removed;
    }

    function rememberCouponMessage(message, couponCode) {
        try {
            sessionStorage.setItem('niwa_ai_last_coupon', JSON.stringify({
                message: String(message || ''),
                couponCode: String(couponCode || '')
            }));
        } catch (e) { /* ignore */ }
    }

    function addBotMessage(text, opts) {
        text = String(text || '').trim();
        if (!text) return;

        opts = opts || {};

        var renderer = getRenderer();
        openChat();

        if (renderer) {
            renderer.addMessage(text, 'bot', {
                source: opts.source || 'ConversionBuster',
                forceNewGroup: true,
                couponCode: opts.couponCode || null
            });
            saveHistoryNow();
        } else if (window.niwaAI && typeof window.niwaAI.addBotMessage === 'function') {
            window.niwaAI.addBotMessage(text, opts);
        }

        if (opts.couponCode) {
            rememberCouponMessage(text, opts.couponCode);
        }

        appendBackendHistory([{ role: 'bot', text: text }]);
        scrollBottom();
    }

    function addUserMessage(text) {
        text = String(text || '').trim();
        if (!text) return;

        var renderer = getRenderer();
        if (renderer) {
            if (typeof renderer.clearSuggestions === 'function') {
                renderer.clearSuggestions();
            }
            renderer.addMessage(text, 'user', {
                source: 'ConversionBusterUser',
                forceNewGroup: true
            });
            saveHistoryNow();
        }

        appendBackendHistory([{ role: 'user', text: text }]);
        scrollBottom();
    }

    function fallbackOfferMessage() {
        var lang = ((typeof NiwaCB !== 'undefined' && NiwaCB.language) || 'en').toLowerCase();
        if (lang.indexOf('de') === 0) {
            return 'Ich habe einen Rabatt fuer Sie gefunden. Wenn Sie ihn wollen, bestaetigen Sie einfach im Chat.';
        }
        if (lang.indexOf('it') === 0) {
            return 'Ho trovato uno sconto per voi. Se lo volete, confermatelo semplicemente in chat.';
        }
        if (lang.indexOf('ru') === 0) {
            return 'Ya nashla dlya vas skidku. Esli hotite, prosto podtverdite eto v chate.';
        }
        if (lang.indexOf('hr') === 0) {
            return 'Pronasla sam popust za vas. Ako ga zelite, samo potvrdite u chatu.';
        }
        if (lang.indexOf('sr') === 0 || lang.indexOf('bs') === 0) {
            return 'Nasla sam popust za vas. Ako ga zelite, samo potvrdite u chatu.';
        }
        return 'I found a discount for you. If you want it, just confirm in chat.';
    }

    function getLocalizedMessage(key) {
        if (key === 'email_prompt_message' && typeof NiwaCB !== 'undefined' && NiwaCB && NiwaCB.email_hunt_prompt) {
            return String(NiwaCB.email_hunt_prompt || '').trim();
        }

        if (key === 'clarify_message' && typeof NiwaCB !== 'undefined' && NiwaCB && NiwaCB.clarify_message) {
            return String(NiwaCB.clarify_message || '').trim();
        }

        var lang = ((typeof NiwaCB !== 'undefined' && NiwaCB.language) || 'en').toLowerCase();
        var pack = {
            understand_message: {
                sr: 'Razumem. Ako se predomislite, samo mi recite da zelite kod.',
                hr: 'Razumijem. Ako se predomislite, samo mi recite da zelite kod.',
                bs: 'Razumem. Ako se predomislite, samo mi recite da zelite kod.',
                en: 'Understood. If you change your mind, just tell me you want the code.',
                de: 'Verstanden. Wenn Sie es sich anders ueberlegen, sagen Sie einfach, dass Sie den Code wollen.',
                it: 'Capisco. Se cambiate idea, ditemi semplicemente che volete il codice.',
                ru: 'Ponimayu. Esli peredumaete, prosto skazhite, chto hotite kod.'
            },
            clarify_message: {
                sr: 'Ako zelite kod za popust, samo potvrdite i generisacu ga.',
                hr: 'Ako zelite kod za popust, samo potvrdite i generirat cu ga.',
                bs: 'Ako zelite kod za popust, samo potvrdite i generisacu ga.',
                en: 'If you want the discount code, just confirm and I will generate it.',
                de: 'Wenn Sie den Rabattcode wollen, bestaetigen Sie to einfach und ich generiere ihn.',
                it: 'Se volete il codice sconto, confermatelo e lo generero.',
                ru: 'Esli vy hotite kod skidki, prosto podtverdite eto i ya ego sozdam.'
            },
            email_prompt_message: {
                sr: 'Upisite vas email da bih mogla kreirati kod.',
                hr: 'Upisite vas email da bih mogla kreirati kod.',
                bs: 'Upisite vas email da bih mogla kreirati kod.',
                en: 'Please enter your email so I can create your code.',
                de: 'Bitte geben Sie Ihre E-Mail ein, damit ich Ihren Code erstellen kann.',
                it: 'Inserite la vostra email cosi posso creare il vostro codice.',
                ru: 'Vvedite vash email, chtoby ya mogla sozdat kod.'
            }
        };

        var langKey = lang.split(/[_-]/)[0];
        return (pack[key] && (pack[key][langKey] || pack[key].en)) || '';
    }

    function isEmailHuntEnabled() {
        return !!(typeof NiwaCB !== 'undefined' && NiwaCB && NiwaCB.email_hunt_enabled);
    }

    function isEmailRequired() {
        return isEmailHuntEnabled();
    }

    /**
     * Returns true when "Lock chat behind email" (Email Start Chat Cacher) is
     * active AND the visitor has not yet provided a validated email this session.
     */
    function _isChatLockedByESCC() {
        if (!window.NiwaAI || !window.NiwaAI.email_start_chat_cacher_enabled) return false;
        try {
            if (sessionStorage.getItem('niwa_validated_email')) return false;
        } catch (e) { /* ignore */ }
        return true;
    }

    function ensureOfferVisible() {
        var runtime = readRuntime();
        if (!runtime.offerMessage || !isBlockingState(runtime.state)) return;
        if (!hasTextInHistory('bot', runtime.offerMessage)) {
            addBotMessage(runtime.offerMessage, { source: runtime.offerSource || 'ConversionBuster' });
        }
    }

    function showEmailCapture() {
        if (F.forms && typeof F.forms.showEmailFormOnly === 'function') {
            setTimeout(function () {
                F.forms.showEmailFormOnly();
            }, 100);
        }
    }

    function startOffer(message, opts) {
        message = String(message || '').trim();
        if (!message) {
            message = fallbackOfferMessage();
        }

        opts = opts || {};


        // ESCC interaction rules.
        if (_isChatLockedByESCC()) {
            if (!isEmailHuntEnabled()) {
                // Rule 2: chat is locked by ESCC and CB Email Hunt is off —
                // CB cannot trigger until the visitor unlocks via ESCC email form.
                return false;
            }
            // Rule 1: chat is locked but CB Email Hunt is on — CB proceeds
            // because its email hunt will capture the email (which also unlocks
            // ESCC). Clear the ESCC unlock form from the input first so CB Email
            // Hunt can take over the input area. Also remove the ESCC unlock
            // prompt from chat history/DOM — CB is about to present its own
            // offer message and showing both would confuse the visitor.
            var _cbRenderer = getRenderer();
            if (_cbRenderer && typeof _cbRenderer.removeBySource === 'function') {
                _cbRenderer.removeBySource('escc_unlock');
            }
            if (F.forms && typeof F.forms.showStandardForm === 'function') {
                F.forms.showStandardForm();
            }
        }

        var runtime = readRuntime();
        if (isBlockingState(runtime.state) && runtime.offerMessage && runtime.offerMessage === message) {
            openChat();
            ensureOfferVisible();
            return true;
        }

        runtime = writeRuntime({
            active: true,
            state: STATES.AWAITING_REPLY,
            mode: opts.mode === 'mini' ? 'mini' : 'cart',
            offerMessage: message,
            offerSource: opts.source || 'ConversionBuster',
			offerToken: opts.offerToken || '',
            couponCode: '',
            couponMessage: '',
            replyMessage: ''
        });

        if (cartMonitor && typeof cartMonitor.setCbActive === 'function') {
            cartMonitor.setCbActive(true);
        }

        setPostFlowMute(true);
        openChat();

        if (!hasTextInHistory('bot', message)) {
            addBotMessage(message, { source: runtime.offerSource });
        }

        return true;
    }

    function startMiniFlow(message) {
        return startOffer(message, {
            mode: 'mini',
            source: 'ObjectionSlayer3'
        });
    }

    function finishFlow(state, patch) {
        patch = patch || {};
        patch.active = false;
        patch.state = state || STATES.IDLE;
        patch.mode = 'cart';

        writeRuntime(Object.assign(readRuntime(), patch));
        if (F.state) {
            F.state.chatState = F.state.chatState || {};
            F.state.chatState.awaitingEmail = false;
            F.state.chatState.cbWaitingForCoupon = false;
            F.state.chatState.cbReadyForReset = (state === STATES.COMPLETED);
            if (typeof F.state.saveChatState === 'function') {
                F.state.saveChatState();
            }
        }
        if (cartMonitor && typeof cartMonitor.setCbActive === 'function') {
            cartMonitor.setCbActive(false);
        }
        setPostFlowMute(false);
    }

    function cancelCartFlow(reason) {
        var runtime = readRuntime();
        if (runtime.mode !== 'cart' || !isBlockingState(runtime.state)) {
            return false;
        }

        var entries = collectCartFlowPruneEntries(runtime);
        removeLocalCartFlowHistory(runtime, entries);
        pruneBackendHistory(entries);

        finishFlow(STATES.IDLE, {
            offerMessage: '',
            couponCode: '',
            couponMessage: '',
            replyMessage: '',
            cancelReason: reason || 'cart_ineligible'
        });

        if (cartMonitor && typeof cartMonitor.setSuppressed === 'function') {
            cartMonitor.setSuppressed(false);
        }

        return true;
    }

    function isCartEligibilityFailure(data) {
        data = data && typeof data === 'object' ? data : {};
        var reason = String(data.reason || data.error || '').toLowerCase();
        var message = String(data.message || '').toLowerCase();

        return reason === 'cart_unavailable'
            || reason === 'cart_threshold_not_met'
            || reason === 'cart_minimum_not_met'
            || message.indexOf('cart threshold not met') !== -1
            || message.indexOf('minimum amount not met') !== -1;
    }

    function detectReplyIntent(text) {
        if (typeof NiwaCB === 'undefined' || !NiwaCB || !NiwaCB.ajax_url || !NiwaCB.nonce) {
            return $.Deferred().reject().promise();
        }

        return $.post(NiwaCB.ajax_url, {
            action: 'niwa_ai_cb_detect_response',
            nonce: NiwaCB.nonce,
			message: text,
			mode: readRuntime().mode === 'mini' ? 'mini' : 'cart'
        });
    }

    function promptForEmail() {
        writeRuntime(Object.assign(readRuntime(), {
            active: true,
            state: STATES.AWAITING_EMAIL
        }));
        if (F.state) {
            F.state.chatState = F.state.chatState || {};
            F.state.chatState.awaitingEmail = true;
            F.state.chatState.cbWaitingForCoupon = true;
            F.state.chatState.cbReadyForReset = false;
            if (typeof F.state.saveChatState === 'function') {
                F.state.saveChatState();
            }
        }
        addBotMessage(getLocalizedMessage('email_prompt_message'));
        showEmailCapture();
    }

    function createCoupon(email) {
        var runtime = writeRuntime(Object.assign(readRuntime(), {
            active: true,
            state: STATES.GENERATING
        }));
        if (F.state) {
            F.state.chatState = F.state.chatState || {};
            F.state.chatState.awaitingEmail = false;
            F.state.chatState.cbWaitingForCoupon = true;
            if (typeof F.state.saveChatState === 'function') {
                F.state.saveChatState();
            }
        }

        $.post(NiwaCB.ajax_url, {
            action: 'niwa_ai_cb_create_coupon',
            nonce: NiwaCB.nonce,
            email: email || '',
            session: backendSessionId(),
			is_cb_mini: runtime.mode === 'mini' ? '1' : '0',
			offer_token: runtime.offerToken || ''
        }).done(function (resp) {
            if (resp && resp.success && resp.data) {
                var couponMessage = String(resp.data.message || 'Your discount code has been generated.');
                var couponCode = String(resp.data.coupon_code || '');

                addBotMessage(couponMessage, {
                    source: 'ConversionBuster',
                    couponCode: couponCode
                });

                finishFlow(STATES.COMPLETED, {
                    couponCode: couponCode,
                    couponMessage: couponMessage
                });

                if (cartMonitor && typeof cartMonitor.setCooldown === 'function') {
                    cartMonitor.setCooldown();
                }
                return;
            }

            if (resp && resp.data && isCartEligibilityFailure(resp.data)) {
                cancelCartFlow('cart_ineligible');
                return;
            }

            var failMsg = resp && resp.data
                ? String(resp.data.message || resp.data.error || 'Could not generate the code.')
                : 'Could not generate the code.';

            addBotMessage(failMsg);
            writeRuntime(Object.assign(readRuntime(), {
                active: true,
                state: STATES.AWAITING_REPLY
            }));
        }).fail(function (xhr) {
            var failMsg = 'An error occurred. Please try again.';
            try {
                var body = xhr && xhr.responseJSON ? xhr.responseJSON : null;
                if (!body && xhr && typeof xhr.responseText === 'string') {
                    body = JSON.parse(xhr.responseText);
                }
                if (body && body.data) {
                    failMsg = body.data.message || body.data.error || failMsg;
                }
            } catch (e) { /* ignore */ }

            if (body && body.data && isCartEligibilityFailure(body.data)) {
                cancelCartFlow('cart_ineligible');
                return;
            }

            addBotMessage(failMsg);
            writeRuntime(Object.assign(readRuntime(), {
                active: true,
                state: STATES.AWAITING_REPLY
            }));
        });
    }

    function handleReplyIntent(intent) {
        if (intent === 'yes') {
            if (isEmailRequired()) {
                promptForEmail();
                return;
            }
            var email = getStoredEmail();
            createCoupon(email);
            return;
        }

        if (intent === 'no') {
            addBotMessage(getLocalizedMessage('understand_message'));
            finishFlow(STATES.DECLINED);
            if (cartMonitor && typeof cartMonitor.setCooldown === 'function') {
                cartMonitor.setCooldown();
            }
            return;
        }

        addBotMessage(getLocalizedMessage('clarify_message'));
        writeRuntime(Object.assign(readRuntime(), {
            active: true,
            state: STATES.AWAITING_REPLY
        }));
    }

    function handleUserReply(text, opts) {
        text = String(text || '').trim();
        if (!text) return false;

        opts = opts || {};

        var runtime = readRuntime();
        var state = opts.forcedFlowState || runtime.state;

        if (state === STATES.GENERATING || state === STATES.RESOLVING_REPLY) {
            return true;
        }

        if (state === STATES.AWAITING_EMAIL) {
            showEmailCapture();
            return true;
        }

        if (state !== STATES.AWAITING_REPLY) {
            return false;
        }

        if (runtime.mode === 'cart'
            && cartMonitor
            && typeof cartMonitor.isLastCartIneligible === 'function'
            && cartMonitor.isLastCartIneligible()) {
            cancelCartFlow('cart_ineligible_before_reply');
            return false;
        }

        if (opts.renderUserMessage !== false) {
            addUserMessage(text);
        }

        writeRuntime(Object.assign(runtime, {
            active: true,
            state: STATES.RESOLVING_REPLY,
            replyMessage: text
        }));

        detectReplyIntent(text).done(function (resp) {
            var data = resp && resp.success && resp.data ? resp.data : null;
            var intent = data && typeof data.intent === 'string' ? String(data.intent).toLowerCase() : 'unclear';
			if (data && data.offer_token) {
				patchRuntime({ offerToken: String(data.offer_token) });
			}
            handleReplyIntent(intent);
        }).fail(function () {
            handleReplyIntent('unclear');
        });

        return true;
    }

    function onEmailCaptured(email) {
        persistEmail(email);

        var runtime = readRuntime();
        if (runtime.state !== STATES.AWAITING_EMAIL) {
            return false;
        }

        createCoupon(email);
        return true;
    }

    function onPhoneCaptured() {
        persistPhone(arguments.length ? arguments[0] : '');
        return false;
    }

    var cartMonitor = {
        initialized: false,
        cooldownUntil: 0,
        isProcessing: false,
        hasShown: false,
        lastCartStatus: null,

        getCbActive: function () {
            return !!readRuntime().active;
        },

        setCbActive: function (val) {
            patchRuntime({ active: !!val });
        },

        setSuppressed: function (val) {
            try {
                if (val) {
                    sessionStorage.setItem('niwa_ai_cb_suppress', '1');
                } else {
                    sessionStorage.removeItem('niwa_ai_cb_suppress');
                }
            } catch (e) { /* ignore */ }
        },

        isSuppressed: function () {
            try {
                return sessionStorage.getItem('niwa_ai_cb_suppress') === '1';
            } catch (e) {
                return false;
            }
        },

        rememberCartStatus: function (data) {
            this.lastCartStatus = data && typeof data === 'object' ? data : null;
        },

        isCartStatusIneligible: function (data) {
            data = data && typeof data === 'object' ? data : {};

            var threshold = parseInt(data.threshold || 0, 10);
            var cartCount = parseInt(data.cart_count || 0, 10);
            var minimumAmount = parseFloat(data.minimum_amount || 0);
            var cartTotal = parseFloat(data.cart_total || 0);

            if (threshold > 0 && cartCount < threshold) return true;
            if (minimumAmount > 0 && cartTotal < minimumAmount) return true;
            if (data.should_trigger === false && threshold <= 0) return true;

            return false;
        },

        isLastCartIneligible: function () {
            return this.lastCartStatus ? this.isCartStatusIneligible(this.lastCartStatus) : false;
        },

        setCooldown: function () {
            this.cooldownUntil = Date.now() + ((NiwaCB && NiwaCB.session_cooldown) || (30 * 60 * 1000));
            try {
                sessionStorage.setItem('niwa_ai_cb_cooldown_until', String(this.cooldownUntil));
            } catch (e) { /* ignore */ }
        },

        loadCooldown: function () {
            try {
                var raw = parseInt(sessionStorage.getItem('niwa_ai_cb_cooldown_until') || '0', 10);
                if (!isNaN(raw)) {
                    this.cooldownUntil = raw;
                }
            } catch (e) { /* ignore */ }
        },

        init: function () {
            if (this.initialized) return;
            this.initialized = true;

            if (typeof NiwaCB === 'undefined' || !NiwaCB || !NiwaCB.enabled || !NiwaCB.wc_active) {
                return;
            }

            this.loadCooldown();
            this.resume();
            this.bindEvents();
            this.startMonitoring();
        },

        resume: function () {
            var runtime = readRuntime();
            syncLegacyFlags(runtime);

            if (!isBlockingState(runtime.state)) {
                return;
            }

            // ESCC interaction rules — mirror the guard in startOffer().
            if (_isChatLockedByESCC()) {
                if (!isEmailHuntEnabled()) {
                    // Rule 2: ESCC is blocking and CB Email Hunt is off.
                    // Reset any stale blocking state so checkCart() remains
                    // responsive once conditions change (CBEH enabled or ESCC unlocked).
                    finishFlow(STATES.IDLE);
                    return;
                }
                // Rule 1: ESCC is blocking but CB Email Hunt is on —
                // clear the ESCC form so CB can take over, then open the chat.
                // Also clear the ESCC unlock prompt from history/DOM so it
                // doesn't linger alongside the resumed CB offer.
                var _resumeRenderer = getRenderer();
                if (_resumeRenderer && typeof _resumeRenderer.removeBySource === 'function') {
                    _resumeRenderer.removeBySource('escc_unlock');
                }
                if (F.forms && typeof F.forms.showStandardForm === 'function') {
                    F.forms.showStandardForm();
                }
                openChat();

                // If CB was in the email-collection phase, decide whether to
                // keep it or reset:
                //   - If the CB email prompt message IS already in the chat
                //     history, the visitor legitimately entered CBEH email
                //     collection in the current flow (said "yes" → CB asked
                //     for email). They reloaded or navigated without typing
                //     it in. We must keep AWAITING_EMAIL and re-show the
                //     email form — otherwise their in-progress flow vanishes.
                //   - If the CB email prompt message is NOT in history, the
                //     AWAITING_EMAIL state is stale (carried over from a
                //     previous session via localStorage). Reset to
                //     AWAITING_REPLY so the visitor re-engages with the offer
                //     instead of seeing a bare email form that looks like
                //     the ESCC unlock form they were just freed from.
                if (runtime.state === STATES.AWAITING_EMAIL
                    && !hasTextInHistory('bot', getLocalizedMessage('email_prompt_message'))) {
                    writeRuntime(Object.assign(readRuntime(), { state: STATES.AWAITING_REPLY }));
                    runtime = readRuntime();
                }
            }

            this.setCbActive(true);
            setPostFlowMute(true);

            if (runtime.state === STATES.AWAITING_EMAIL) {
                if (!hasTextInHistory('bot', getLocalizedMessage('email_prompt_message'))) {
                    addBotMessage(getLocalizedMessage('email_prompt_message'));
                }
                showEmailCapture();
                return;
            }

            ensureOfferVisible();
        },

        bindEvents: function () {
            var self = this;

            $(document.body).on('added_to_cart wc_fragment_refresh updated_wc_div wc_fragments_refreshed removed_from_cart wc_cart_emptied', function () {
                self.checkSoon(350);
            });

            $(document).on('click', '.add_to_cart_button, .single_add_to_cart_button', function () {
                self.checkSoon(1200);
            });

            $(document).ajaxComplete(function (event, xhr, settings) {
                var url = settings && settings.url ? settings.url : '';
                var data = settings && typeof settings.data === 'string' ? settings.data : '';
                if (url.indexOf('wc-ajax=add_to_cart') !== -1 || data.indexOf('action=niwa_ai_add_to_cart') !== -1) {
                    self.checkSoon(700);
                }
            });

            if (window.MutationObserver) {
                var morphEl = document.getElementById('niwaMorph') || document.getElementById('niwaMorphLeft') || document.getElementById('niwaCenter');
                if (morphEl) {
                    // Only suppress CB when the flyout transitions from OPEN -> CLOSED
                    // (i.e. the visitor closed it manually). Previously the guard fired
                    // for any class change while the flyout wasn't open, so unrelated
                    // class mutations from themes, resize handlers, or WooCommerce
                    // fragment refreshes would freeze CB before it could ever trigger.
                    this.flyoutObserver = new MutationObserver(function (mutations) {
                        for (var i = 0; i < mutations.length; i++) {
                            var m = mutations[i];
                            if (m.type !== 'attributes' || m.attributeName !== 'class') continue;
                            var oldClass = m.oldValue || '';
                            var newClass = m.target.className || '';
                            var wasOpen = oldClass.indexOf('niwa-morph--open') !== -1 || oldClass.indexOf('niwa-center--open') !== -1;
                            var isOpen = newClass.indexOf('niwa-morph--open') !== -1 || newClass.indexOf('niwa-center--open') !== -1;
                            if (wasOpen && !isOpen) {
                                self.setSuppressed(true);
                            }
                        }
                    });
                    this.flyoutObserver.observe(morphEl, {
                        attributes: true,
                        attributeOldValue: true,
                        attributeFilter: ['class']
                    });
                }
            }
        },

        startMonitoring: function () {
            var self = this;
            this.checkSoon(0);
            this.intervalId = setInterval(function () {
                self.checkSoon(0);
            }, 2000);
        },

        checkSoon: function (delay) {
            var self = this;
            clearTimeout(this.debounceTimer);
            this.debounceTimer = setTimeout(function () {
                self.checkCart();
            }, typeof delay === 'number' ? delay : 300);
        },

        checkCart: function () {
            if (this.isProcessing) return;

            var runtime = readRuntime();
            var blockingFlow = isBlockingState(runtime.state);
            var blockingCartFlow = blockingFlow && runtime.mode === 'cart';

            if (this.isSuppressed() && !blockingCartFlow) return;
            if (Date.now() < this.cooldownUntil && !blockingCartFlow) return;
            if (blockingFlow && !blockingCartFlow) return;

            this.isProcessing = true;

            $.ajax({
                url: NiwaCB.ajax_url,
                type: 'POST',
                data: {
                    action: 'niwa_ai_cb_check_cart',
                    nonce: NiwaCB.nonce
                }
            }).done(function (response) {
                var data = response && response.success && response.data ? response.data : null;
                cartMonitor.rememberCartStatus(data);

                if (blockingCartFlow) {
                    if (!data || cartMonitor.isCartStatusIneligible(data)) {
                        cancelCartFlow('cart_ineligible');
                    }
                    return;
                }

                if (data && data.should_trigger) {
                    cartMonitor.hasShown = true;
                    cartMonitor.setSuppressed(false);
                    startOffer(String(data.offer_text || data.message || '').trim(), {
                        mode: 'cart',
						source: 'ConversionBuster',
						offerToken: String(data.offer_token || '')
                    });
                }
            }).always(function () {
                cartMonitor.isProcessing = false;
            });
        }
    };

    $(document).ready(function () {
        cartMonitor.init();
    });

    $(document).on('za:user:message', function (e, text) {
        handleUserReply(text);
    });

    $(window).on('load', function () {
        setTimeout(function () {
            cartMonitor.resume();
        }, 400);
    });

    var flowApi = {
        state: readRuntime().state,
        set: function (state) {
            flowApi.state = state;
            if (state === STATES.IDLE) {
                resetRuntime(STATES.IDLE);
                return;
            }
            patchRuntime({ state: state });
        },
        get: function () {
            flowApi.state = readRuntime().state;
            return flowApi.state;
        }
    };

    F.cb = F.cb || {};
    F.cb.flow = flowApi;
    F.cb.cartMonitor = cartMonitor;
    F.cb.handleUserReply = handleUserReply;
    F.cb.renderHandledUserMessage = addUserMessage;
    F.cb.hasBlockingFlow = hasBlockingFlow;
    F.cb.getBlockingFlowState = getBlockingFlowState;
    F.cb.isEmailHuntActive = function () {
        return isEmailHuntEnabled() && readRuntime().state === STATES.AWAITING_EMAIL;
    };
    F.cb.startMiniFlow = startMiniFlow;
    F.cb.onEmailCaptured = onEmailCaptured;
    F.cb.onPhoneCaptured = onPhoneCaptured;

    window.addEventListener('beforeunload', function () {
        if (cartMonitor.flyoutObserver) {
            cartMonitor.flyoutObserver.disconnect();
        }
        if (cartMonitor.intervalId) {
            clearInterval(cartMonitor.intervalId);
        }
    });
})(jQuery);
