/**
 * VisitorLens — Admin JS (shared across all admin pages)
 * Provides API helper and common UI utilities.
 */
(function () {
  'use strict';

  window.VSR = window.VSR || {};

  const REST_BASE = typeof vlensAdmin !== 'undefined' ? vlensAdmin.restBase : '/wp-json/vlens/v1/';
  const NONCE     = typeof vlensAdmin !== 'undefined' ? vlensAdmin.nonce : '';

  /**
   * Fetch wrapper for VSR REST endpoints.
   */
  VSR.api = async function (path, opts = {}) {
    const url = REST_BASE + path;
    const headers = {
      'X-WP-Nonce': NONCE,
      ...(opts.headers || {}),
    };

    if (opts.body && typeof opts.body === 'object' && !(opts.body instanceof FormData)) {
      headers['Content-Type'] = 'application/json';
      opts.body = JSON.stringify(opts.body);
    }

    const res = await fetch(url, { ...opts, headers });
    if (!res.ok) {
      throw new Error(`VSR API error: ${res.status} ${res.statusText}`);
    }
    return res.json();
  };

  VSR.get = function (path) {
    return VSR.api(path, { method: 'GET' });
  };

  VSR.post = function (path, data) {
    return VSR.api(path, { method: 'POST', body: data });
  };

  VSR.del = function (path) {
    return VSR.api(path, { method: 'DELETE' });
  };

  VSR.fmt = function (n) {
    return (+n).toLocaleString();
  };

  VSR.fmtDur = function (ms) {
    const s = Math.round(ms / 1000);
    return s >= 60 ? `${Math.floor(s / 60)}m ${s % 60}s` : `${s}s`;
  };

  VSR.fmtDate = function (d) {
    return new Date(d).toLocaleDateString('en-GB', { day: 'numeric', month: 'short' });
  };

  VSR.fmtTime = function (dt) {
    if (!dt) return '—';
    return new Date(dt).toLocaleString('en-GB', {
      day: 'numeric', month: 'short',
      hour: '2-digit', minute: '2-digit',
    });
  };

  VSR.esc = function (s) {
    const d = document.createElement('div');
    d.textContent = s || '';
    return d.innerHTML;
  };

  VSR.flag = function (code) {
    if (!code || code === '--') return '';
    try {
      return code.toUpperCase().replace(/./g, c => String.fromCodePoint(127397 + c.charCodeAt()));
    } catch { return ''; }
  };

  // ── TOP NAV OVERFLOW ("More ▾" dropdown) ────────────────────────
  // Ad Lens / Orders Lens / Incomplete Orders (li[data-collapsible]) move
  // into #vsrNavMoreDropdown one at a time as the bar runs out of room,
  // and move back out as room frees up. Dashboard / Session Replay /
  // Statistics / Settings never collapse.
  (function initNavOverflow() {
    const navLinks = document.getElementById('vsrNavLinks');
    const moreLi    = document.getElementById('vsrNavMore');
    const moreBtn   = document.getElementById('vsrNavMoreBtn');
    const dropdown  = document.getElementById('vsrNavMoreDropdown');
    if (!navLinks || !moreLi || !moreBtn || !dropdown) return;

    function fits() {
      return navLinks.scrollWidth <= navLinks.clientWidth + 1;
    }

    // Moves the last visible collapsible item into the dropdown (prepend,
    // so the dropdown always reads in the same left-to-right order the
    // items had in the bar — the earliest-collapsed item ends up last).
    function collapseOne() {
      const prev = moreLi.previousElementSibling;
      if (prev && prev.hasAttribute('data-collapsible')) {
        dropdown.prepend(prev);
        return true;
      }
      return false;
    }

    // Moves the dropdown's first item back into the bar, just before the
    // More <li>. Caller is responsible for checking it still fits.
    function restoreOne() {
      const first = dropdown.firstElementChild;
      if (!first) return false;
      moreLi.parentNode.insertBefore(first, moreLi);
      return true;
    }

    // Keeps the button's hidden state in sync with whether the dropdown
    // actually has anything in it. Must be called after every single
    // restore/collapse move (not just once at the end) — the button's own
    // width has to be part of every fits() check below it, including the
    // check that decides whether the very last item can be restored. Get
    // that ordering wrong and the loop either collapses an item purely to
    // make room for a button nothing needs, or gets stuck one item short
    // of a full restore because it measured the button as still taking
    // space in the hypothetical fully-restored (button-hidden) state.
    function syncMoreVisibility() {
      moreLi.hidden = dropdown.children.length === 0;
    }

    function update() {
      // Try restoring items in case the bar has more room now; undo if
      // a restored item makes it overflow again.
      let guard = 20;
      while (dropdown.firstElementChild && guard-- > 0) {
        const candidate = dropdown.firstElementChild;
        restoreOne();
        syncMoreVisibility();
        if (!fits()) {
          dropdown.prepend(candidate);
          syncMoreVisibility();
          break;
        }
      }

      // Then collapse further while still overflowing.
      guard = 20;
      while (!fits() && guard-- > 0) {
        if (!collapseOne()) break;
        syncMoreVisibility();
      }

      syncMoreVisibility();
      if (dropdown.children.length === 0) closeDropdown();
      moreBtn.classList.toggle('has-active', !!dropdown.querySelector('.is-active'));
    }

    let raf = null;
    function scheduleUpdate() {
      if (raf) cancelAnimationFrame(raf);
      raf = requestAnimationFrame(update);
    }

    // The dropdown is a top-level sibling of <nav> (see nav.php), not
    // nested inside the button's <li> — so it can't rely on CSS
    // position:absolute + a positioned ancestor. Compute its screen
    // position from the button's live rect instead, right-aligned to the
    // button's right edge (matching the old `right:0` look) and clamped
    // so it can never run off either edge of the viewport.
    function positionDropdown() {
      const rect = moreBtn.getBoundingClientRect();
      const dw = dropdown.offsetWidth || 190;
      let left = rect.right - dw;
      left = Math.max(8, Math.min(left, window.innerWidth - dw - 8));
      dropdown.style.top  = (rect.bottom + 6) + 'px';
      dropdown.style.left = left + 'px';
    }

    function closeDropdown() {
      dropdown.classList.remove('open');
      moreBtn.setAttribute('aria-expanded', 'false');
    }

    moreBtn.addEventListener('click', function (e) {
      e.stopPropagation();
      const open = dropdown.classList.toggle('open');
      moreBtn.setAttribute('aria-expanded', open ? 'true' : 'false');
      if (open) positionDropdown();
    });
    document.addEventListener('click', function (e) {
      if (!moreLi.contains(e.target) && !dropdown.contains(e.target)) {
        closeDropdown();
      }
    });
    document.addEventListener('keydown', function (e) {
      if (e.key === 'Escape') closeDropdown();
    });
    // Keep it glued to the button if the page scrolls (e.g. while the
    // sticky nav is still transitioning into its stuck position) or the
    // window resizes while open.
    window.addEventListener('scroll', function () {
      if (dropdown.classList.contains('open')) positionDropdown();
    }, true);
    window.addEventListener('resize', function () {
      if (dropdown.classList.contains('open')) positionDropdown();
    });

    // ResizeObserver catches the WP admin-menu auto-fold/expand too, not
    // just window resizes (that alone doesn't fire a 'resize' event).
    if (typeof ResizeObserver !== 'undefined') {
      new ResizeObserver(scheduleUpdate).observe(navLinks.closest('.vsr-top-nav-inner') || navLinks);
    } else {
      window.addEventListener('resize', scheduleUpdate);
    }
    window.addEventListener('load', scheduleUpdate);
    update();
  })();

  // Auto-fold admin menu on Session Replay pages: unfold while the mouse
  // is over #adminmenuwrap, re-fold once it leaves.
  if (document.body.classList.contains('auto-fold')) {
    const adminMenuWrap = document.getElementById('adminmenuwrap');
    if (adminMenuWrap) {
      adminMenuWrap.addEventListener('mouseenter', function () {
        document.body.classList.remove('auto-fold', 'folded');
      });
      adminMenuWrap.addEventListener('mouseleave', function () {
        document.body.classList.add('auto-fold', 'folded');
      });
    }
  }

})();
