/**
 * VisitorLens — Visitor Tracker
 * Collects browser-side visitor data and POSTs to WP REST API.
 * Requires vlensTracker localized object from WordPress.
 * Requires vsr-gate.js (window.vlensGateCheck) for consent + URL exclusion.
 */
(function () {
  'use strict';

  if (typeof vlensTracker === 'undefined') return;

  var gate = window.vlensGateCheck;

  // If URL is excluded, never run
  if (gate && gate.isUrlExcluded && gate.isUrlExcluded()) return;

  var ENDPOINT   = vlensTracker.endpoint;
  var NONCE      = vlensTracker.nonce;

  /* ── Persistent visitor cookie (vlens_vid) ────────────────────────────────── */
  var COOKIE_NAME = 'vlens_vid';
  var COOKIE_DAYS = 365;

  function genUuid() {
    // Crypto-quality 32-char hex string
    if (window.crypto && window.crypto.getRandomValues) {
      var buf = new Uint8Array(16);
      window.crypto.getRandomValues(buf);
      return Array.from(buf, function(b) { return ('0' + b.toString(16)).slice(-2); }).join('');
    }
    // Fallback: Math.random
    return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
      var r = Math.random() * 16 | 0;
      return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
  }

  function getCookie(name) {
    var match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
    return match ? decodeURIComponent(match[1]) : null;
  }

  function setCookie(name, value, days) {
    var expires = new Date(Date.now() + days * 864e5).toUTCString();
    document.cookie = name + '=' + encodeURIComponent(value)
      + '; expires=' + expires
      + '; path=/; SameSite=Lax';
  }

  function getVisitorCookieId() {
    // Prefer localStorage for speed, fall back to cookie
    var id;
    try { id = localStorage.getItem(COOKIE_NAME); } catch(e) {}
    if (!id) id = getCookie(COOKIE_NAME);
    if (!id || !/^[0-9a-f]{32}$/.test(id)) {
      id = genUuid();
      try { localStorage.setItem(COOKIE_NAME, id); } catch(e) {}
      setCookie(COOKIE_NAME, id, COOKIE_DAYS);
    } else {
      // Refresh cookie expiry on each visit
      setCookie(COOKIE_NAME, id, COOKIE_DAYS);
    }
    return id;
  }

  var VISITOR_COOKIE_ID = getVisitorCookieId();
  var BOT_PATTERN = /bot|crawl|spider|slurp|mediapartners|facebookexternalhit|twitterbot|linkedinbot|curl|wget|python/i;

  if (BOT_PATTERN.test(navigator.userAgent)) return;
  if (typeof fetch === 'undefined') return;

  // Skip if inside replay iframe
  if (new URLSearchParams(window.location.search).get('vs_replay') === '1') return;

  function getDeviceType() {
    var ua = navigator.userAgent;
    if (/tablet|ipad|playbook|silk|(android(?!.*mobile))/i.test(ua)) return 'tablet';
    if (/mobile|iphone|ipod|blackberry|iemobile|opera mini|android.*mobile/i.test(ua)) return 'mobile';
    return 'desktop';
  }

  function getUtm() {
    var p = new URLSearchParams(window.location.search);
    return {
      utm_source:   p.get('utm_source')   || '',
      utm_medium:   p.get('utm_medium')   || '',
      utm_campaign: p.get('utm_campaign') || '',
      utm_term:     p.get('utm_term')     || '',
      utm_content:  p.get('utm_content')  || '',
    };
  }

  function buildPayload() {
    var screen = window.screen || {};
    return {
      page_uri:      window.location.pathname + window.location.search,
      page_title:    document.title || '',
      referrer:      document.referrer || '',
      language:      navigator.language || '',
      timezone:      Intl.DateTimeFormat().resolvedOptions().timeZone || '',
      device_type:   getDeviceType(),
      screen_width:  screen.width  || null,
      screen_height: screen.height || null,
      cookie_id:     VISITOR_COOKIE_ID,
      ...getUtm(),
    };
  }

  function getLoadTime() {
    try {
      var nav = performance.getEntriesByType('navigation');
      if (nav && nav.length) {
        var n = nav[0];
        return {
          load_time_ms:    Math.round(n.loadEventEnd > 0 ? n.loadEventEnd - n.startTime : 0),
          dom_ready_ms:    Math.round(n.domContentLoadedEventEnd > 0 ? n.domContentLoadedEventEnd - n.startTime : 0),
        };
      }
      var t = performance.timing;
      if (t && t.loadEventEnd > 0) {
        return {
          load_time_ms: t.loadEventEnd - t.navigationStart,
          dom_ready_ms: t.domContentLoadedEventEnd - t.navigationStart,
        };
      }
    } catch (e) {}
    return { load_time_ms: 0, dom_ready_ms: 0 };
  }

  async function track(includePerf, isRetry) {
    // Re-check consent before every track call (SPA navigations etc.)
    if (gate && gate.hasConsent && !gate.hasConsent()) return;

    var payload = buildPayload();
    if (includePerf) {
      var perf = getLoadTime();
      payload.load_time_ms = perf.load_time_ms;
    }
    try {
      var resp = await fetch(ENDPOINT, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-WP-Nonce':   NONCE,
        },
        body: JSON.stringify(payload),
        keepalive: true,
      });
      if (resp.ok) {
        var data = await resp.json();
        if (data.visitor_id) {
          try { sessionStorage.setItem('vs_visitor_id', data.visitor_id); } catch(e) {}
        }
        if (data.online !== undefined) {
          document.dispatchEvent(new CustomEvent('vs:tracked', { detail: data }));
        }
      } else if (resp.status === 429 && !isRetry) {
        // Rate-limit window is short-lived — one bounded retry instead of losing the pageview.
        setTimeout(function () { track(includePerf, true); }, 3000);
      }
    } catch (err) {
      // Fail silently
    }
  }

  // SPA support
  var _push    = history.pushState.bind(history);
  var _replace = history.replaceState.bind(history);
  history.pushState = function () { _push.apply(history, arguments); setTimeout(track, 0); };
  history.replaceState = function () { _replace.apply(history, arguments); setTimeout(track, 0); };
  window.addEventListener('popstate', function () { setTimeout(track, 0); });

  // Initial page load — wait for load event so performance timing is available
  function initialTrack() {
    if (document.readyState === 'complete') {
      setTimeout(function () { track(true); }, 0);
    } else {
      window.addEventListener('load', function () {
        setTimeout(function () { track(true); }, 0);
      });
    }
  }

  // Wait for consent before initial track
  if (gate && gate.onConsent) {
    gate.onConsent(initialTrack);
  } else {
    initialTrack();
  }

  window.vsTrack = track;
})();
