/**
 * meSEO Site Tag — Attribution module
 * ------------------------------------
 * Runs on every page load. Identifies the traffic source for the current
 * visit and persists a rolling 20-touchpoint history so downstream
 * conversions can be credited across the full journey (first-touch,
 * last-touch, assisted channels).
 *
 * Exposes:
 *   window.meSEOAttribution.init()      — idempotent, runs per page view
 *   window.meSEOAttribution.getContext() — { visitorId, sessionId, touchpoints, currentTouch }
 *
 * No personal data is captured. Cookieless Machine Enhancing Tracking ID.
 */
(function () {
  "use strict";

  var VID_KEY = "_meseo_vid";
  var TOUCH_KEY = "_meseo_tp";
  var SID_KEY = "_meseo_sid";
  var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
  var MAX_TOUCHPOINTS = 20;

  function uid(prefix) {
    return prefix + "_" + Date.now().toString(36) + "_" + Math.random().toString(36).slice(2, 10);
  }

  // ── Visitor + session IDs ──────────────────────────────────────────
  function getVisitorId() {
    try {
      var vid = localStorage.getItem(VID_KEY);
      if (!vid) { vid = uid("mv"); localStorage.setItem(VID_KEY, vid); }
      return vid;
    } catch (e) { return uid("mv"); }
  }
  function getSessionId() {
    try {
      var sid = sessionStorage.getItem(SID_KEY);
      if (!sid) { sid = uid("ms"); sessionStorage.setItem(SID_KEY, sid); }
      return sid;
    } catch (e) { return uid("ms"); }
  }

  // ── Channel detection ──────────────────────────────────────────────
  var SEARCH_HOSTS = /(?:^|\.)(google|bing|yahoo|duckduckgo|ecosia|baidu|yandex|brave)\./i;
  var SOCIAL_HOSTS = /(?:^|\.)(facebook|fb|instagram|l\.instagram|t\.co|twitter|x\.com|linkedin|lnkd|tiktok|youtube|youtu\.be|pinterest|reddit|snapchat)\./i;
  var EMAIL_HOSTS = /(?:^|\.)(mail\.google|outlook\.live|mailchi\.mp|sendgrid|hubspotemail|campaign-archive)\./i;
  var GBP_HOSTS = /(?:^|\.)(google|maps\.google|g\.co|goo\.gl\/maps)\./i;

  function detectChannel(url, referrer, urlParams) {
    // gclid / msclkid only appear on *paid* Google/Bing clicks — the
    // search engines generate them for Ads click-through. Safe to
    // classify as paid without additional signals.
    if (urlParams.get("gclid")) return { channel: "paid", source: "google", medium: "cpc", clickId: urlParams.get("gclid") };
    if (urlParams.get("msclkid")) return { channel: "paid", source: "bing", medium: "cpc", clickId: urlParams.get("msclkid") };

    // fbclid / ttclid are NOT paid indicators — Facebook and TikTok
    // append them to every outbound click (organic posts, shares,
    // Messenger links). Only promote to paid_social when a UTM param
    // explicitly signals paid traffic; otherwise classify as organic
    // social with the click ID preserved for attribution debugging.
    // This stops organic Facebook shares being bucketed into "Paid
    // Social" on the Conversions by Source chart.
    var paidUtmMedium = (urlParams.get("utm_medium") || "").toLowerCase();
    var utmIsPaid = paidUtmMedium === "cpc" || paidUtmMedium === "ppc" || paidUtmMedium === "paid" || paidUtmMedium === "paid_social" || paidUtmMedium === "paidsocial";
    if (urlParams.get("fbclid")) {
      return {
        channel: utmIsPaid ? "paid_social" : "social",
        source: "facebook",
        medium: utmIsPaid ? "paid_social" : "social",
        clickId: urlParams.get("fbclid"),
      };
    }
    if (urlParams.get("ttclid")) {
      return {
        channel: utmIsPaid ? "paid_social" : "social",
        source: "tiktok",
        medium: utmIsPaid ? "paid_social" : "social",
        clickId: urlParams.get("ttclid"),
      };
    }

    var utmSource = urlParams.get("utm_source"), utmMedium = urlParams.get("utm_medium"), utmCampaign = urlParams.get("utm_campaign");
    if (utmSource || utmMedium) {
      var channel = "other", m = (utmMedium || "").toLowerCase();
      if (m === "cpc" || m === "ppc" || m === "paid") channel = "paid";
      else if (m === "email") channel = "email";
      else if (m === "paid_social" || m === "paidsocial") channel = "paid_social";
      else if (m === "social") channel = "social";
      else if (m === "organic") channel = "organic";
      else if (m === "referral") channel = "referral";
      return { channel: channel, source: utmSource || "direct", medium: utmMedium || null, campaign: utmCampaign || null };
    }
    if (referrer) {
      try {
        var rHost = new URL(referrer).hostname.toLowerCase();
        if (rHost === window.location.hostname) return { channel: "internal", source: rHost, medium: "internal" };
        if (GBP_HOSTS.test(rHost) && /\/maps|\/local/i.test(referrer)) return { channel: "local", source: "gbp", medium: "organic" };
        if (SEARCH_HOSTS.test(rHost)) return { channel: "organic", source: rHost.replace(/^www\./, "").split(".")[0], medium: "organic" };
        if (SOCIAL_HOSTS.test(rHost)) return { channel: "social", source: rHost.replace(/^www\./, "").split(".")[0], medium: "social" };
        if (EMAIL_HOSTS.test(rHost)) return { channel: "email", source: rHost, medium: "email" };
        return { channel: "referral", source: rHost, medium: "referral" };
      } catch (e) {}
    }
    return { channel: "direct", source: "direct", medium: "none" };
  }

  // ── Touchpoint history ─────────────────────────────────────────────
  function readTouchpoints() {
    try { var raw = localStorage.getItem(TOUCH_KEY); return raw ? JSON.parse(raw) : []; }
    catch (e) { return []; }
  }
  function writeTouchpoints(arr) {
    try {
      var cutoff = Date.now() - THIRTY_DAYS_MS;
      var filtered = arr.filter(function (t) { return !t.timestamp || new Date(t.timestamp).getTime() > cutoff; });
      if (filtered.length > MAX_TOUCHPOINTS) filtered = filtered.slice(filtered.length - MAX_TOUCHPOINTS);
      localStorage.setItem(TOUCH_KEY, JSON.stringify(filtered));
    } catch (e) {}
  }
  function appendTouchpoint(touch) {
    var history = readTouchpoints();
    var last = history.length ? history[history.length - 1] : null;
    if (last && last.sessionId === touch.sessionId && last.channel === touch.channel && last.source === touch.source) return history;
    history.push(touch);
    writeTouchpoints(history);
    return readTouchpoints();
  }

  // ── Init ───────────────────────────────────────────────────────────
  var ctx = null;
  function init() {
    if (ctx) return ctx;
    var visitorId = getVisitorId(), sessionId = getSessionId();
    var urlParams;
    try { urlParams = new URLSearchParams(window.location.search); } catch (e) { urlParams = { get: function () { return null; } }; }
    var detected = detectChannel(window.location.href, document.referrer || "", urlParams);
    var touch = { visitorId: visitorId, sessionId: sessionId, channel: detected.channel, source: detected.source, medium: detected.medium || null, campaign: detected.campaign || null, clickId: detected.clickId || null, landingUrl: window.location.href, referrer: document.referrer || null, timestamp: new Date().toISOString() };
    var history = appendTouchpoint(touch);
    ctx = { visitorId: visitorId, sessionId: sessionId, currentTouch: touch, touchpoints: history };
    return ctx;
  }

  window.meSEOAttribution = { init: init, getContext: function () { return ctx || init(); } };
})();
