/**
 * AIEO Tolstoy attribution — cross-tab carrier.
 *
 * The Tolstoy video carousel embeds links that open in a NEW TAB
 * (`target="_blank"`). That breaks same-tab attribution: by the time
 * the visitor clicks Add-to-Cart on the destination product page, the
 * destination tab has no DOM/session knowledge of which carousel /
 * which source product surfaced it.
 *
 * Bridge mechanism:
 *
 *   1. Click recorder (runs on the SOURCE tab — the page hosting the
 *      Tolstoy widget). Capture-phase delegated click listener on any
 *      `<a>` inside `[data-aieo-reco^="slot_"]` that also contains a
 *      `<tolstoy-widget>` element. Writes an entry to localStorage
 *      with `{ source: 'tolstoy', source_pid, source_meta, dst_path,
 *      ts }` BEFORE the navigation fires.
 *
 *   2. Consumer (runs on every page load). Reads localStorage; if a
 *      recent entry's `dst_path` matches the current `location.pathname`,
 *      lifts it into `window.aieoTolstoyPending` and one-shot deletes
 *      it from localStorage so a refresh or a sibling tab doesn't
 *      double-consume.
 *
 *   3. The existing `aieo-attribution-client.js` checks
 *      `window.aieoTolstoyPending` in `inferSource()` /
 *      `resolveSourcePid()` and attaches `source_meta` to the
 *      add-to-cart beacon. Result: an Add-to-Cart on Product B that
 *      originated from a Tolstoy click on Product A's page records
 *      a `wp_aieo_addcart_events` row with `source='tolstoy'`,
 *      `source_pid=A`, `source_meta={slot_id, tolstoy_widget_id,
 *      clicked_url, slot_name}`.
 *
 *   localStorage key:  `aieo_tolstoy_attribution`
 *   TTL:              30 minutes (`Date.now() - entry.ts > TTL_MS`)
 *
 * Loaded only on `is_singular('product')` from
 * AIEO_DMM_Custom_Slots::enqueue_tolstoy_scripts(). Both source AND
 * destination pages are product pages, so this single enqueue covers
 * both halves of the bridge.
 */
(function () {
    'use strict';

    var STORAGE_KEY = 'aieo_tolstoy_attribution';
    var TTL_MS      = 30 * 60 * 1000;

    /**
     * Resolve the CURRENT page's WooCommerce product id. Tries three
     * sources in order of reliability:
     *   1. WC add-to-cart hidden input — `<input name="add-to-cart" value="NNN">`
     *      inside `form.cart`. Most reliable; survives theme overrides.
     *   2. <body class="postid-NNN"> — WP-standard body class on
     *      singular pages. Survives most theme + caching layers.
     *   3. `<div class="product"...>` Hentry id attribute `post-NNN`.
     * Returns 0 if none of these are findable.
     */
    function resolveCurrentProductId() {
        var hidden = document.querySelector('form.cart [name="add-to-cart"]');
        if (hidden && hidden.value) {
            var v = parseInt(hidden.value, 10);
            if (v > 0) { return v; }
        }
        var bc = document.body && document.body.classList;
        if (bc) {
            for (var i = 0; i < bc.length; i++) {
                var m = /^postid-(\d+)$/.exec(bc[i]);
                if (m) { return parseInt(m[1], 10) || 0; }
            }
        }
        var prod = document.querySelector('.product[id^="product-"]');
        if (prod) {
            var pid = prod.getAttribute('id') || '';
            var mm = /^product-(\d+)$/.exec(pid);
            if (mm) { return parseInt(mm[1], 10) || 0; }
        }
        return 0;
    }

    /**
     * Promote a pending localStorage entry to window.aieoTolstoyPending
     * IF (a) it exists, (b) it's within TTL, (c) the entry's
     * `dst_product_id` matches the current page's product id.
     *
     * Why product_id, not pathname: WordPress 301-redirects `?p=NNN`
     * URLs to the canonical pretty permalink. The Tolstoy widget
     * emits hrefs as `https://example.com/?p=NNN`, so the destination
     * tab loads `/product/category/foo` instead of `/` — a pathname
     * match would never fire and attribution would be silently lost.
     * Product id is stable across the redirect.
     *
     * One-shot: deletes from localStorage on success so a refresh or
     * a second tab doesn't re-consume the same record.
     */
    function consumePendingOnLoad() {
        try {
            var raw = localStorage.getItem(STORAGE_KEY);
            if (!raw) { return; }
            var entry = JSON.parse(raw);
            if (!entry || typeof entry !== 'object' || !entry.ts) {
                localStorage.removeItem(STORAGE_KEY);
                return;
            }
            if (Date.now() - entry.ts > TTL_MS) {
                localStorage.removeItem(STORAGE_KEY);
                return;
            }

            // Match by product_id when both sides have it. Tolstoy
            // hrefs carry `?p=NNN` so we capture it at click time;
            // the destination page exposes it via body.postid-NNN /
            // form.cart / .product[id^="product-"].
            var currentPid = resolveCurrentProductId();
            var entryPid   = parseInt(entry.dst_product_id, 10) || 0;

            if (entryPid && currentPid) {
                if (entryPid !== currentPid) {
                    // Leave the entry in localStorage — another tab
                    // navigating to product entryPid can still claim it.
                    return;
                }
            } else if (entry.dst_path && entry.dst_path !== window.location.pathname) {
                // Legacy fallback only when no product_id was resolvable
                // at click time (rare — would happen for Tolstoy links
                // without a `?p=` query param AND without an embedded
                // product slug we could parse).
                return;
            }
            // else: no product_id on either side → consume best-effort.

            localStorage.removeItem(STORAGE_KEY);
            window.aieoTolstoyPending = {
                source:         entry.source,
                source_pid:     entry.source_pid,
                source_meta:    entry.source_meta,
                // Propagated so aieo-attribution-client's inferSource()
                // can verify the clicked add-to-cart button is for the
                // SAME product the Tolstoy link pointed at. Without
                // this, every add-to-cart on the destination page —
                // including upsell/related cards for OTHER products —
                // would inherit the Tolstoy source. See the matching
                // guard in aieo-attribution-client.js::inferSource().
                dst_product_id: parseInt(entry.dst_product_id, 10) || 0,
                ts:             entry.ts
            };
        } catch (e) {
            // Storage access can throw in private-mode browsers. The
            // attribution graceful-degrades to 'unknown' — same as
            // any other surface lookup miss.
        }
    }

    /**
     * Delegated capture-phase click listener. Fires BEFORE the browser
     * navigation kicks off (or before Tolstoy's own click handler can
     * call preventDefault), so the localStorage write is guaranteed
     * to happen even if the link is intercepted downstream.
     */
    function attachClickRecorder() {
        document.addEventListener('click', function (e) {
            var target = e.target;
            if (!target || !target.closest) { return; }

            var link = target.closest('a[href]');
            if (!link) { return; }

            // Only intercept clicks INSIDE a Tolstoy-bearing slot. The
            // `[data-aieo-reco^="slot_"]` selector is the join key on
            // every custom-slot wrapper; the <tolstoy-widget> descendant
            // check rules out HTML/code slots that happen to contain
            // links — those don't get auto-attributed (operators who
            // want HTML-slot attribution can add their own data-aieo-*
            // attributes per click).
            var slot = link.closest('[data-aieo-reco^="slot_"]');
            if (!slot) { return; }
            if (!slot.querySelector('tolstoy-widget')) { return; }

            var href = link.getAttribute('href');
            if (!href) { return; }

            // Same-origin only. Tolstoy widgets configured by the
            // operator should always point at on-site products, but
            // sanity-check anyway to avoid cross-site attribution
            // leakage if a video embed slips an outbound link in.
            var dst;
            try {
                dst = new URL(href, window.location.origin);
            } catch (er) {
                return;
            }
            if (dst.origin !== window.location.origin) { return; }

            // Extract the destination product id BEFORE the navigation.
            // Tolstoy hrefs are emitted as `?p=NNN` (WP "ugly" permalink),
            // so the query param is the cleanest source. WordPress then
            // 301-redirects that URL to the canonical pretty permalink
            // (`/product/cat/slug/`) — but the redirect's destination
            // pathname has no `?p=NNN` and can't be derived from the
            // pre-redirect URL. Capturing the id at click time and
            // matching the destination tab's product id is what makes
            // this work across the redirect.
            var dstProductId = 0;
            var pParam = dst.searchParams.get('p');
            if (pParam) {
                dstProductId = parseInt(pParam, 10) || 0;
            }
            // Fallback: pretty-permalink links MAY embed a numeric id
            // somewhere (e.g. `/product/.../p/12345`) but the common
            // shape is slug-only — leave dstProductId = 0 in that case.
            // The consumer will fall back to dst_path matching.

            // source_pid = the product whose page hosts this widget —
            // same convention as product_upsells / product_related /
            // product_crosssells in the main attribution client.
            var sourcePid = 0;
            var hidden = document.querySelector('form.cart [name="add-to-cart"]');
            if (hidden && hidden.value) {
                sourcePid = parseInt(hidden.value, 10) || 0;
            }
            if (!sourcePid) {
                // Fallback: <body class="postid-NNN single-product"> on
                // legacy WC + classic themes.
                var bc = document.body && document.body.classList;
                if (bc) {
                    for (var i = 0; i < bc.length; i++) {
                        var m = /^postid-(\d+)$/.exec(bc[i]);
                        if (m) { sourcePid = parseInt(m[1], 10) || 0; break; }
                    }
                }
            }

            // Slot metadata for the report. Operators investigating
            // "which Tolstoy carousel drives the most cart-adds"
            // pivot on tolstoy_widget_id; "which clicked video drove
            // this purchase" pivots on clicked_url.
            var meta = {
                slot_id:     slot.getAttribute('data-aieo-reco') || '',
                clicked_url: dst.href
            };
            var widgetEl = slot.querySelector('tolstoy-widget');
            if (widgetEl) {
                var wid = widgetEl.getAttribute('data-tolstoy-widget-id');
                if (wid) { meta.tolstoy_widget_id = wid; }
            }

            try {
                localStorage.setItem(STORAGE_KEY, JSON.stringify({
                    source:         'tolstoy',
                    source_pid:     sourcePid,
                    source_meta:    meta,
                    dst_product_id: dstProductId, // primary match key — stable across WP's ?p=NNN → pretty-permalink 301
                    dst_path:       dst.pathname, // legacy fallback when ?p= is absent
                    ts:             Date.now()
                }));
            } catch (er) {
                // Private mode / quota — graceful no-op.
            }
        }, true);
    }

    // Run the consumer ASAP — `inferSource()` in
    // aieo-attribution-client.js fires on add-to-cart click, which
    // comes after DOMContentLoaded, but checking immediately covers
    // the path where the destination page also hosts a Tolstoy widget
    // and the visitor's NEXT click happens before DCL.
    consumePendingOnLoad();

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', attachClickRecorder);
    } else {
        attachClickRecorder();
    }
})();
