<template>
  <div class="marko-web-p1-events-track-engagement" style="display: none;" />
</template>

<script>
/**
 * Fires a single `Engaged View` p1event on the first genuine human interaction of a
 * pageview, then unbinds. A positive "humanity" signal used to qualify page views
 * as real — see websites/ENGAGEMENT-TRACKING-SCOPE.md. The same signal is also
 * mirrored to GTM's `dataLayer` as a `verified-view` event so GA4 can report on it.
 *
 * Primary trigger is a viewport-normalized scroll distance; a small set of
 * high-intent OR-triggers (link/button click, menu open, form-field focus, media
 * play, optional text selection) catch users who act before scrolling. Ad clicks
 * are intentionally NOT a trigger — creatives render in cross-origin (safe)frames
 * and ad clicks are already tracked, bot-filtered, by the ad systems.
 */

// Recognizes a menu/nav toggle vs. a plain link/button (affects the label only).
const MENU_SELECTOR = [
  '[aria-expanded]',
  '[data-toggle="collapse"]',
  '[data-toggle="dropdown"]',
  '[class*="menu-toggle"]',
  '[class*="nav-toggle"]',
  '[class*="hamburger"]',
  '.navbar-toggler',
].join(',');

// Same-origin ad containers to ignore (fallback render path only; cross-origin ad
// iframes never bubble a click to the page).
const AD_SELECTOR = '[data-unit-form],[id^="mfads-req-"]';

export default {
  props: {
    category: { type: String, default: 'Website' },
    entity: { type: Object, required: true },
    // An event label (e.g. 'Sponsored' on sponsored content), so reporting splits
    // engagement the same way it splits Views. The trigger type is recorded
    // separately in props.eventName.
    label: { type: String, default: null },
    floorPx: { type: Number, default: 500 },
    viewportFraction: { type: Number, default: 0.75 },
    enableTextSelect: { type: Boolean, default: false },
  },

  data() {
    return {
      didFire: false,
      // A genuine user gesture must precede a scroll for it to count.
      userGestured: false,
      distanceThreshold: 0,
      // registered [target, type, handler, options] tuples for clean teardown
      listeners: [],
    };
  },

  mounted() {
    this.distanceThreshold = Math.max(
      this.floorPx,
      this.viewportFraction * (window.innerHeight || 0),
    );

    // Guards the scroll trigger against layout shift, anchor jumps, and
    // programmatic scroll — only a real gesture arms it.
    const markGesture = () => { this.userGestured = true; };
    this.on(window, 'wheel', markGesture, { passive: true });
    this.on(window, 'touchmove', markGesture, { passive: true });
    this.on(window, 'keydown', markGesture);

    // Scroll past the viewport-normalized distance threshold.
    this.on(window, 'scroll', this.onScroll, { passive: true });

    // Any on-page link/button click (menu vs. link classified by selector).
    this.on(document, 'click', this.onClick, true);

    // Form-field focus — search box, newsletter, comment (covers Enter-key submit).
    this.on(document, 'focusin', this.onFocusIn, true);

    // Media play — native <video>/<audio> and same-origin players (does not bubble).
    this.on(document, 'play', this.onPlay, true);

    // Optional, off by default — strong signal but higher false-positive risk.
    if (this.enableTextSelect) {
      this.on(document, 'selectionchange', this.onSelectionChange);
    }
  },

  beforeDestroy() {
    this.teardown();
  },

  methods: {
    /**
     * Register a listener and track it for teardown.
     */
    on(target, type, handler, options) {
      target.addEventListener(type, handler, options);
      this.listeners.push([target, type, handler, options]);
    },

    /**
     * Remove all registered listeners.
     */
    teardown() {
      this.listeners.forEach(([target, type, handler, options]) => {
        target.removeEventListener(type, handler, options);
      });
      this.listeners = [];
    },

    onScroll() {
      if (!this.userGestured) return;
      const scrolled = window.scrollY || window.pageYOffset || 0;
      if (scrolled >= this.distanceThreshold) this.fire('scroll');
    },

    onClick(e) {
      const el = e.target && e.target.closest ? e.target.closest('a, button') : null;
      if (!el) return;
      // Ignore same-origin ad containers — ad clicks are not an engagement signal.
      if (el.closest(AD_SELECTOR)) return;
      this.fire(el.closest(MENU_SELECTOR) ? 'menu-open' : 'link-click');
    },

    onFocusIn(e) {
      const el = e.target;
      if (!el || !el.matches) return;
      if (el.matches('input:not([type="hidden"]), textarea, select')) this.fire('form-input');
    },

    onPlay() {
      this.fire('media-play');
    },

    onSelectionChange() {
      const sel = window.getSelection && window.getSelection();
      if (sel && String(sel).trim().length > 0) this.fire('text-select');
    },

    /**
     * Fire the single Engaged View event for this pageview, then unbind.
     *
     * `category` and `entity` are passed in as props by the mounting View tracker
     * (e.g. track-content passes the content entity), so the event is attributed to
     * that page's entity. Exactly one tracker is mounted per pageview → exactly one
     * Engaged View event. `eventName` is the qualifying trigger type, recorded on
     * `props.eventName`; the event `label` (e.g. 'Sponsored') comes from the prop.
     *
     * The same signal is also pushed to GTM's `dataLayer` as a `verified-view`
     * event so it flows through to GA4. The push follows the fleet's dataLayer
     * convention (a top-level `event` key the GTM trigger matches on, plus a nested
     * `p1-events` namespace object that GTM Data Layer Variables read, mirroring the
     * `identity-x` shape). It is pushed independently of the p1events lib so GA4
     * still receives the verified view even if that lib is blocked/failed to load.
     */
    fire(eventName) {
      if (this.didFire) return;
      this.didFire = true;
      this.teardown();

      const { category, label, entity } = this;

      // Primary signal: the Engaged View p1event.
      if (window.p1events) {
        window.p1events('track', {
          category,
          entity,
          action: 'Engaged View',
          ...(label && { label }),
          props: { eventName },
        });
      }

      // Mirror to GTM's dataLayer as `verified-view` for GA4 reporting.
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: 'verified-view',
        'p1-events': {
          event: 'verified-view',
          category,
          action: 'Engaged View',
          ...(label && { label }),
          eventName,
          entityId: entity && entity.id,
          entityNs: entity && entity.ns,
          entityName: entity && entity.name,
        },
      });
    },
  },
};
</script>
