/**
 * Elementor frontend handler for the Cookie Consent widget.
 *
 * Shows the consent notice (after an optional delay) until the visitor accepts or
 * rejects. The choice is remembered in localStorage — and, optionally, a browser
 * cookie so the server can read it — for a configurable number of days. Accept /
 * reject / preferences each dispatch a `captwiki:cookie-consent` (or
 * `captwiki:cookie-preferences`) DOM event so other scripts can react. Inside the
 * Elementor editor the notice renders inline for styling and never persists. One
 * controller per instance with editor-safe teardown.
 *
 * @package captain-widgets-kit
 * @since   1.7.0
 */

(function ($) {
    'use strict';

    /**
     * Per-instance handler — called by Elementor for every widget instance.
     *
     * @param {jQuery} $scope - The widget wrapper element.
     */
    var CaptwikiCookieHandler = function ($scope) {

        var root = $scope[0].querySelector('.captwiki-cookie');
        if (!root) return;

        if (root._cwkTeardown) {
            root._cwkTeardown();
        }

        var isEditMode = window.elementorFrontend && elementorFrontend.isEditMode && elementorFrontend.isEditMode();

        var config = {
            rememberDays: parseInt(root.getAttribute('data-remember-days'), 10) || 180,
            delay: parseInt(root.getAttribute('data-delay'), 10) || 0,
            storageKey: root.getAttribute('data-storage-key') || 'captwiki_cookie_consent',
            setCookie: root.getAttribute('data-set-cookie') === '1'
        };

        var handlers = [];
        var showTimer = null;

        /**
         * Add an event listener and remember it for later teardown.
         *
         * @param {Element} el   - Target element.
         * @param {string}  type - Event name.
         * @param {Function} fn  - Listener.
         */
        function on(el, type, fn) {
            if (!el) return;
            el.addEventListener(type, fn);
            handlers.push(function () { el.removeEventListener(type, fn); });
        }

        /* ── Storage helpers (guarded — private mode can throw) ── */

        /**
         * Has the visitor already chosen within the remembered window?
         *
         * @return {boolean} True if a stored, unexpired choice exists.
         */
        function hasChoice() {
            try {
                var raw = window.localStorage.getItem(config.storageKey);
                if (!raw) return false;
                var data = JSON.parse(raw);
                if (!data || !data.consent) return false;
                if (data.expires && data.expires < Date.now()) {
                    window.localStorage.removeItem(config.storageKey);
                    return false;
                }
                return true;
            } catch (e) {
                return false;
            }
        }

        /**
         * Persist the visitor's choice to localStorage and (optionally) a cookie.
         *
         * @param {string} consent - 'accepted' or 'rejected'.
         */
        function storeChoice(consent) {
            var maxAgeSeconds = config.rememberDays * 86400;
            try {
                window.localStorage.setItem(config.storageKey, JSON.stringify({
                    consent: consent,
                    expires: Date.now() + maxAgeSeconds * 1000
                }));
            } catch (e) { /* storage unavailable */ }

            if (config.setCookie) {
                try {
                    var secure = window.location.protocol === 'https:' ? '; Secure' : '';
                    document.cookie = encodeURIComponent(config.storageKey) + '=' + encodeURIComponent(consent) +
                        '; Max-Age=' + maxAgeSeconds + '; Path=/; SameSite=Lax' + secure;
                } catch (e) { /* cookie unavailable */ }
            }
        }

        /* ── Show / hide ───────────────────────────────────── */

        /**
         * Reveal the notice with its entrance transition.
         */
        function show() {
            root.classList.add('is-visible');
        }

        /**
         * Hide and (after the transition) remove the notice from layout.
         */
        function hide() {
            root.classList.remove('is-visible');
            root.classList.add('is-dismissed');
        }

        /**
         * Dispatch a bubbling DOM event so other scripts can react.
         *
         * @param {string} name   - Event name.
         * @param {Object} detail - Event detail payload.
         */
        function emit(name, detail) {
            try {
                root.dispatchEvent(new CustomEvent(name, { bubbles: true, detail: detail }));
            } catch (e) { /* CustomEvent unsupported — ignore */ }
        }

        /* ── Actions ───────────────────────────────────────── */

        /**
         * Record a choice, hide the notice and announce it.
         *
         * @param {string} consent - 'accepted' or 'rejected'.
         */
        function choose(consent) {
            if (!isEditMode) {
                storeChoice(consent);
            }
            hide();
            emit('captwiki:cookie-consent', { consent: consent, key: config.storageKey });
        }

        var actionButtons = Array.prototype.slice.call(root.querySelectorAll('[data-action]'));

        actionButtons.forEach(function (btn) {
            on(btn, 'click', function () {
                switch (btn.getAttribute('data-action')) {
                    case 'accept':
                        choose('accepted');
                        break;
                    case 'reject':
                        choose('rejected');
                        break;
                    case 'settings':
                        emit('captwiki:cookie-preferences', { key: config.storageKey });
                        break;
                }
            });
        });

        /* ── Init ──────────────────────────────────────────── */
        if (isEditMode) {
            // Always visible inline for styling; never persists.
            root.classList.add('is-preview', 'is-visible');
        } else if (hasChoice()) {
            root.classList.add('is-dismissed');
        } else if (config.delay > 0) {
            showTimer = window.setTimeout(show, config.delay * 1000);
        } else {
            // Defer one frame so the entrance transition runs.
            window.requestAnimationFrame(show);
        }

        /* ── Teardown ──────────────────────────────────────── */
        root._cwkTeardown = function () {
            handlers.forEach(function (off) { off(); });
            handlers = [];
            if (showTimer) {
                window.clearTimeout(showTimer);
                showTimer = null;
            }
            root._cwkTeardown = null;
        };
    };

    /* ── Register with Elementor frontend ───────────────────── */
    $(window).on('elementor/frontend/init', function () {
        elementorFrontend.hooks.addAction(
            'frontend/element_ready/captwiki-cookie-consent.default',
            CaptwikiCookieHandler
        );
    });

})(jQuery);
