/**
 * AIEO Attribution — thin global client.
 *
 * Listens for clicks on stock WooCommerce add-to-cart buttons (archive
 * loop, single-product, upsells/related/crosssells) and fires a
 * fire-and-forget beacon to `aieo_attribution_log`, plus a GA4 dataLayer
 * push. The side-cart surfaces (RV, favorites, QV, may-also-like) have
 * their own inline instrumentation inside aieo-side-cart.js; this file
 * does NOT double-fire for those — we skip any button that carries
 * a `data-aieo-source` attribute (our own buttons always do).
 *
 * Architectural notes:
 *  - Single delegated click listener on document — zero per-button cost.
 *  - `navigator.sendBeacon` when available; XHR keepalive fallback.
 *  - No jQuery dependency: runs standalone, no blocking code.
 *  - Never blocks the actual add-to-cart click — this runs alongside
 *    WC's own click handler, never interferes.
 */
(function () {
    'use strict';

    var CFG = window.aieoAttribution || {};
    if (!CFG.enabled) return;

    var AJAX_URL = CFG.ajaxUrl || (window.ajaxurl || '/wp-admin/admin-ajax.php');

    // --- Source inference ------------------------------------------------

    function closestByClass($el, className) {
        for (var n = $el; n && n !== document; n = n.parentNode) {
            if (n.classList && n.classList.contains(className)) return n;
        }
        return null;
    }

    /**
     * Derive the attribution source from page + button context.
     * Returns a whitelisted key from AIEO_Attribution::VALID_SOURCES.
     */
    function inferSource(btn) {
        // Tolstoy attribution: ONLY when the clicked add-to-cart
        // button is for the SAME product the Tolstoy link pointed
        // to. Without this guard, a visitor who clicks a Tolstoy
        // carousel link to product B and then adds an upsell/related
        // recommendation FROM product B's page to cart would have
        // that upsell mis-attributed to Tolstoy. Comparing the
        // button's product id (readProductId) to the pending entry's
        // dst_product_id keeps the credit on the correct surface:
        //   button is B → tolstoy gets credit (correct)
        //   button is C (upsell on B) → fall through to DOM walk →
        //     product_upsells gets credit (correct)
        // `dst_product_id` is captured at click time by
        // aieo-tolstoy-attribution.js from the `?p=NNN` query param
        // and survives the WP `?p=NNN`→pretty-permalink 301.
        if (window.aieoTolstoyPending && window.aieoTolstoyPending.source === 'tolstoy') {
            var pendingPid = parseInt(window.aieoTolstoyPending.dst_product_id, 10) || 0;
            var clickedPid = readProductId(btn);
            if (pendingPid && clickedPid && pendingPid === clickedPid) {
                return 'tolstoy';
            }
            // Mismatch — leave window.aieoTolstoyPending intact in
            // case the visitor also clicks the actual destination's
            // Add-to-Cart later on the same page view. The pending
            // entry is naturally cleared on its first matching use
            // (further down this file when the payload is built) or
            // on next page load (TTL).
        }
        // Walk up the DOM, checking `data-section` FIRST. The
        // single-product page lazy-hydrates all five recommendation
        // blocks into `<section class="aieo-recommendations"
        // data-section="<type>">` — the data-section attr is the
        // only reliable post-hydration disambiguator. Placeholder
        // IDs + standard WC classes are kept as fallbacks.
        for (var n = btn; n && n !== document; n = n.parentNode) {
            if (!n) continue;
            if (n.getAttribute) {
                var ds = n.getAttribute('data-section');
                if (ds) {
                    if (ds === 'same_needs')      return 'product_needs';
                    if (ds === 'upsells')         return 'product_upsells';
                    if (ds === 'crosssells')      return 'product_crosssells';
                    if (ds === 'recently_viewed') return 'product_recently_viewed';
                    if (ds === 'related')         return 'product_related';
                }
            }
            if (n.id) {
                if (n.id === 'aieo-same-needs-placeholder' || n.id === 'aieo-same-needs')       return 'product_needs';
                if (n.id === 'aieo-up-sells-placeholder' || n.id === 'aieo-up-sells')           return 'product_upsells';
                if (n.id === 'aieo-cross-sells-placeholder' || n.id === 'aieo-cross-sells')     return 'product_crosssells';
                if (n.id === 'aieo-recently-viewed-placeholder' || n.id === 'aieo-recently-viewed') return 'product_recently_viewed';
                if (n.id === 'aieo-related-placeholder')                                        return 'product_related';
            }
            if (n.classList) {
                if (n.classList.contains('same-needs'))     return 'product_needs';
                if (n.classList.contains('upsells'))        return 'product_upsells';
                if (n.classList.contains('cross-sells'))    return 'product_crosssells';
                if (n.classList.contains('related'))        return 'product_related';
                if (n.classList.contains('aieo-recommendations')) return 'product_related';
            }
        }

        var bc = document.body ? document.body.classList : null;
        if (!bc) return 'unknown';
        if (bc.contains('single-product'))                 return 'single_product';
        // Blog-post detection (Tier 2B). Matches the WP body classes
        // emitted on single posts and the canonical `<article class="post">`
        // marker that block themes use. Must be checked AFTER
        // 'single-product' so a product page never gets misclassified.
        if (
            bc.contains('single-post')
            || bc.contains('blog')
            || (document.querySelector && document.querySelector('article.post'))
        ) return 'blog_post';
        if (bc.contains('home') || bc.contains('front-page')) return 'home';
        if (
            bc.contains('post-type-archive-product')
            || bc.contains('archive')
            || bc.contains('woocommerce-shop')
            || bc.contains('tax-product_cat')
            || bc.contains('tax-product_brand')
            || bc.contains('tax-product_tag')
        ) return 'archive';
        if (bc.contains('woocommerce-cart'))     return 'cart';
        if (bc.contains('woocommerce-checkout')) return 'checkout';
        return 'unknown';
    }

    /**
     * Extract the post_id from a standard `postid-NNN` body class
     * emitted by WordPress on single posts. Returns 0 if not found.
     */
    function resolveBlogPostId() {
        if (!document.body || !document.body.classList) return 0;
        var classes = document.body.classList;
        for (var i = 0; i < classes.length; i++) {
            var m = /^postid-(\d+)$/.exec(classes[i]);
            if (m) return parseInt(m[1], 10) || 0;
        }
        return 0;
    }

    /**
     * Try to resolve source_pid — the "anchor" product that surfaced
     * this recommendation. For product_upsells / related / crosssells
     * the anchor is the single-product page's main product. For
     * `tolstoy`, the anchor is the SOURCE product (the product whose
     * page hosted the Tolstoy widget on the previous tab) — carried
     * across tabs via window.aieoTolstoyPending.
     */
    function resolveSourcePid(source) {
        if (source === 'tolstoy' && window.aieoTolstoyPending) {
            return parseInt(window.aieoTolstoyPending.source_pid, 10) || 0;
        }
        if (
            source === 'product_upsells'
            || source === 'product_related'
            || source === 'product_crosssells'
            || source === 'product_needs'
        ) {
            var mainForm = document.querySelector('form.cart [name="add-to-cart"]');
            if (mainForm && mainForm.value) return parseInt(mainForm.value, 10) || 0;
            var pidHolder = document.querySelector('.product[id^="product-"]');
            if (pidHolder) {
                var m = /product-(\d+)/.exec(pidHolder.id);
                if (m) return parseInt(m[1], 10) || 0;
            }
        }
        return 0;
    }

    // --- Helpers ---------------------------------------------------------

    function uuidV4() {
        // crypto.randomUUID is available in all modern browsers; fallback
        // to a Math.random shim for older ones.
        if (window.crypto && typeof window.crypto.randomUUID === 'function') {
            try { return window.crypto.randomUUID(); } catch (e) { /* fallthrough */ }
        }
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = Math.random() * 16 | 0;
            var v = c === 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }

    function readProductId(btn) {
        var pid = btn.getAttribute('data-product_id') || btn.getAttribute('data-product-id');
        if (pid) { return parseInt(pid, 10) || 0; }
        // Single-product fallback — WC's `.single_add_to_cart_button`
        // doesn't carry the id itself; it sits inside a form.cart.
        var form = btn.closest ? btn.closest('form.cart') : null;
        if (form) {
            var hidden = form.querySelector('[name="add-to-cart"]');
            if (hidden && hidden.value) { return parseInt(hidden.value, 10) || 0; }
        }
        return 0;
    }

    function readVariationId(btn) {
        var vid = btn.getAttribute('data-variation_id') || btn.getAttribute('data-variation-id');
        if (vid) { return parseInt(vid, 10) || 0; }
        var form = btn.closest ? btn.closest('form.cart') : null;
        if (form) {
            var hidden = form.querySelector('input[name="variation_id"]');
            if (hidden && hidden.value) { return parseInt(hidden.value, 10) || 0; }
        }
        return 0;
    }

    function readQuantity(btn) {
        var qty = btn.getAttribute('data-quantity');
        if (qty) { return parseInt(qty, 10) || 1; }
        var form = btn.closest ? btn.closest('form.cart') : null;
        if (form) {
            var qtyInput = form.querySelector('input.qty, input[name="quantity"]');
            if (qtyInput && qtyInput.value) { return parseInt(qtyInput.value, 10) || 1; }
        }
        return 1;
    }

    // --- Beacon ----------------------------------------------------------

    function sendBeaconForm(body) {
        // `navigator.sendBeacon` needs a Blob or FormData; URL-encoded
        // form data is the most compact AJAX-safe payload for
        // admin-ajax.php's parser.
        if (navigator.sendBeacon) {
            try {
                var fd = new FormData();
                Object.keys(body).forEach(function (k) { fd.append(k, body[k]); });
                navigator.sendBeacon(AJAX_URL, fd);
                return true;
            } catch (e) { /* fall through to XHR */ }
        }
        // Fallback — fire-and-forget XHR with `keepalive` for pages
        // that are about to navigate away.
        try {
            var xhr = new XMLHttpRequest();
            xhr.open('POST', AJAX_URL, true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
            if ('keepalive' in xhr) { xhr.keepalive = true; }
            var parts = [];
            Object.keys(body).forEach(function (k) {
                parts.push(encodeURIComponent(k) + '=' + encodeURIComponent(body[k]));
            });
            xhr.send(parts.join('&'));
            return true;
        } catch (e) {
            return false;
        }
    }

    function pushGa4(args) {
        // Operator deferred GA4 (e.g. Google Site Kit) — push nothing.
        // `=== false` so an older localize (no flag) keeps the prior behaviour.
        if (CFG.ga4_enabled === false) { return; }
        // Standard GA4 add_to_cart shape plus AIEO custom dimensions.
        // Mirrors the format used by aieo-side-cart.js so both paths
        // land in the same event stream.
        try {
            window.dataLayer = window.dataLayer || [];
            window.dataLayer.push({ ecommerce: null });
            var evt = {
                event: 'add_to_cart',
                ecommerce: {
                    currency: CFG.currency || 'EUR',
                    value: args.unit_price * args.quantity,
                    items: [{
                        item_id: String(args.product_id),
                        quantity: args.quantity,
                        price: args.unit_price,
                        item_list_id: args.source,
                        item_list_name: args.source,
                    }],
                },
                aieo_source: args.source,
                aieo_source_pid: args.source_pid,
                aieo_event_id: args.event_id,
            };
            window.dataLayer.push(evt);
            if (typeof window.gtag === 'function') {
                window.gtag('event', 'add_to_cart', evt.ecommerce);
            }
        } catch (e) { /* swallow — GA failures never block attribution */ }
    }

    // --- Click listener --------------------------------------------------

    function onClick(e) {
        var t = e.target;
        if (!t) return;
        // Walk up to a real button element, since clicks often land
        // on inner <span> / <svg>.
        var btn = null;
        for (var n = t; n && n !== document; n = n.parentNode) {
            if (!n.classList) continue;
            if (
                n.classList.contains('add_to_cart_button')
                || n.classList.contains('single_add_to_cart_button')
            ) { btn = n; break; }
        }
        if (!btn) return;

        // Side-cart surfaces carry their own `data-aieo-source` and
        // are handled inline by aieo-side-cart.js — skip here to
        // avoid double-firing.
        if (btn.hasAttribute('data-aieo-source')) return;

        var product_id = readProductId(btn);
        if (!product_id) return;

        var source     = inferSource(btn);
        var source_pid = resolveSourcePid(source);
        var event_id   = uuidV4();

        var payload = {
            action:       'aieo_attribution_log',
            event_id:     event_id,
            product_id:   product_id,
            variation_id: readVariationId(btn),
            quantity:     readQuantity(btn),
            source:       source,
            source_pid:   source_pid,
        };

        // Archive-surface adds carry the term context in source_meta
        // so the admin tab can answer "added from which category".
        if (source === 'archive' && CFG.archive && CFG.archive.term_id) {
            payload.source_meta = JSON.stringify({
                archive_term_id:   CFG.archive.term_id,
                archive_term_tax:  CFG.archive.tax,
                archive_term_slug: CFG.archive.slug,
                archive_term_name: CFG.archive.name,
            });
        }

        // Blog-post-surface adds carry the originating post so we can
        // build a per-post conversion table (Tier 2B). post_id is
        // best-effort — `postid-NNN` body class is WP-standard; if a
        // theme strips body classes, only the URL is recorded.
        if (source === 'blog_post') {
            var meta = { post_url: window.location.href };
            var post_id = resolveBlogPostId();
            if (post_id > 0) { meta.post_id = post_id; }
            payload.source_meta = JSON.stringify(meta);
        }

        // Tolstoy-surface adds carry the slot + widget + clicked-video
        // context captured at the source-tab click. The pending entry
        // was promoted to `window.aieoTolstoyPending` on this tab's
        // page load by aieo-tolstoy-attribution.js. Once we consume it
        // here, clear the global so a SECOND add-to-cart on this same
        // page (a re-add, or a different product) doesn't get
        // mis-attributed to the same Tolstoy click.
        if (source === 'tolstoy' && window.aieoTolstoyPending && window.aieoTolstoyPending.source_meta) {
            payload.source_meta = JSON.stringify(window.aieoTolstoyPending.source_meta);
            try { delete window.aieoTolstoyPending; } catch (e) { window.aieoTolstoyPending = null; }
        }

        sendBeaconForm(payload);

        // GA4 signal — price unknown at click-time for stock buttons,
        // leave as 0 and let the thankyou/backfill reconcile later.
        pushGa4({
            product_id: product_id,
            quantity:   payload.quantity,
            unit_price: 0,
            source:     source,
            source_pid: source_pid,
            event_id:   event_id,
        });
    }

    // Use `capture: true` so we run BEFORE WC's own handler — the
    // beacon fires even if WC's click triggers a full-page nav for
    // non-AJAX buttons.
    document.addEventListener('click', onClick, true);
})();
