// Pixo Analytics — Cookieless tracker
// < 4KB gzipped · Zero dependencies · Privacy-first
// Usage: <script defer data-token="SITE_TOKEN" src="https://t.pixo.sh/"></script>

;(function () {
  'use strict'

  var doc = document
  var win = window
  var nav = navigator
  var loc = win.location

  // ─── Bot / prerender guard ───────────────────────────────────────────────
  if (nav.webdriver) return
  if (/bot|crawl|spider|slurp|lighthouse|headless/i.test(nav.userAgent)) return

  // ─── Script tag config ───────────────────────────────────────────────────
  var script = doc.currentScript
  if (!script) return

  var token = script.getAttribute('data-token')
  if (!token) return

  // Derive collector endpoint: data-api override or from script src origin
  var endpoint = script.getAttribute('data-api')
  if (!endpoint) {
    var scriptSrc = script.src
    endpoint = new URL(scriptSrc).origin + '/api/collect'
  }

  // ─── State ───────────────────────────────────────────────────────────────
  var lastPage = null  // track SPA navigation dedup

  // ─── Brave detect (async, opt-in API esposta dal browser) ──────────────
  // Brave maschera lo User-Agent come Chrome per anti-fingerprinting; usa
  // navigator.brave.isBrave() che il browser espone esplicitamente per chi
  // vuole identificarsi. Risolve quasi-sync — il primo pageview ha defer
  // 300ms (SPA-aware), abbastanza tempo per settare il flag.
  var isBrave = false
  try {
    if (nav.brave && typeof nav.brave.isBrave === 'function') {
      nav.brave.isBrave().then(function (v) { isBrave = !!v }).catch(function () {})
    }
  } catch (_) {}

  // ─── Send beacon ─────────────────────────────────────────────────────────
  function send(payload) {
    payload.t = token
    payload.u = loc.href
    payload.r = doc.referrer || null
    payload.lang = (nav.language || '').split('-')[0] || null
    payload.sw = screen.width
    payload.sh = screen.height
    payload.title = doc.title || null
    if (isBrave) payload.b = 1

    var body = JSON.stringify(payload)

    if (nav.sendBeacon) {
      try { nav.sendBeacon(endpoint, body); return } catch (_) {}
    }
    // Fallback
    try {
      fetch(endpoint, {
        method: 'POST',
        body: body,
        keepalive: true,
        headers: { 'Content-Type': 'text/plain' },
      })
    } catch (_) {}
  }

  // ─── 404 detection ──────────────────────────────────────────────────────
  // Three detection methods (framework-agnostic):
  // 1. Performance API: HTTP 404 status on initial load (most reliable)
  // 2. Title check: "404" or "not found" in document.title
  // 3. Meta tag: <meta name="pixo-status" content="404">
  function is404() {
    var title = (doc.title || '').toLowerCase()
    if (title.indexOf('404') !== -1 || title.indexOf('not found') !== -1) return true
    var metaStatus = doc.querySelector('meta[name="pixo-status"]')
    if (metaStatus && metaStatus.content === '404') return true
    return false
  }

  // ─── Page tracking ───────────────────────────────────────────────────────
  function trackPageview(deferred) {
    // Dedup: same URL as last tracked page
    var url = loc.href
    if (lastPage === url) return
    lastPage = url

    // On SPA navigation, defer to let framework render error page
    if (!deferred) {
      setTimeout(function () { sendPageview(url) }, 120)
    } else {
      sendPageview(url)
    }
  }

  function sendPageview(url) {
    // Guard: URL changed again while we waited — skip stale send
    if (lastPage !== url) return

    var payload = { type: 'pageview' }
    if (is404()) payload.type = '404'
    send(payload)
  }

  // ─── SPA navigation ─────────────────────────────────────────────────────
  var origPush = history.pushState
  var origReplace = history.replaceState

  history.pushState = function () {
    origPush.apply(this, arguments)
    trackPageview()
  }
  history.replaceState = function () {
    origReplace.apply(this, arguments)
    trackPageview()
  }

  win.addEventListener('popstate', trackPageview)
  win.addEventListener('hashchange', trackPageview)

  // ─── Auto-outbound & auto-download ──────────────────────────────────────
  var downloadExts = /\.(pdf|zip|docx|csv|xlsx|tar|gz|rar|7z|exe|dmg)$/i

  doc.addEventListener('click', function (e) {
    var link = e.target
    // Walk up to find <a>
    while (link && link.tagName !== 'A') link = link.parentElement
    if (!link || !link.href) return

    var href
    try { href = new URL(link.href) } catch (_) { return }

    // Outbound — strip querystring/hash to avoid leaking PII (tokens, emails, ecc.)
    if (href.hostname !== loc.hostname) {
      send({ type: 'outbound', name: href.origin + href.pathname })
      return
    }

    // Download
    if (downloadExts.test(href.pathname)) {
      send({ type: 'download', name: href.pathname })
    }
  }, true)  // capture phase to catch before preventDefault

  // ─── Custom events API ───────────────────────────────────────────────────
  function trackEvent(name, props) {
    if (!name || typeof name !== 'string') return
    var payload = { type: 'event', name: name }

    if (props && typeof props === 'object') {
      // Max 5 keys, string values only
      var clean = {}
      var count = 0
      for (var k in props) {
        if (count >= 5) break
        if (props.hasOwnProperty(k)) {
          clean[k] = String(props[k])
          count++
        }
      }
      payload.props = clean
    }

    send(payload)
  }

  // ─── Public API ──────────────────────────────────────────────────────────
  win.pixo = {
    track: trackEvent,
  }

  // ─── Initial pageview ────────────────────────────────────────────────────
  // On initial load, check HTTP status via Performance API (works even if
  // the error page doesn't have "404" in the title — e.g. custom error pages)
  var httpIs404 = false
  try {
    var navEntry = performance.getEntriesByType('navigation')[0]
    if (navEntry && navEntry.responseStatus === 404) httpIs404 = true
  } catch (_) {}

  function initialPageview() {
    lastPage = loc.href

    // SSR 404: HTTP status or title/meta already rendered — send immediately
    if (httpIs404 || is404()) {
      send({ type: '404' })
      return
    }

    // SPA/CSR: defer to let framework render (title, meta tags via vue-meta etc.)
    setTimeout(function () {
      if (lastPage !== loc.href) return  // navigated away while waiting
      var payload = { type: 'pageview' }
      if (is404()) payload.type = '404'
      send(payload)
    }, 300)
  }

  // Wait for visibility — skip if page is prerendered
  if (doc.visibilityState === 'prerender') {
    doc.addEventListener('visibilitychange', function onVisible() {
      if (doc.visibilityState !== 'prerender') {
        doc.removeEventListener('visibilitychange', onVisible)
        initialPageview()
      }
    })
  } else {
    initialPageview()
  }
})()
