/**
 * VisitorLens — Statistics Page
 * Ported from the reference standalone stats.php design.
 * Uses VSR.get() (WP REST API) instead of ?api= endpoints.
 */
(function () {
  'use strict';

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

  /* ── STATE ────────────────────────────────────────────────── */
  var currentRange    = 30;
  var currentSection  = 'overview';
  var mainChartInst   = null;
  var deviceChartInst = null;
  var rtInterval      = null;

  /* Overview widget state */
  var ovTrendChart    = null;
  var ovSearchChart   = null;
  var ovTrendGran     = 'daily';
  var ovTrendCompare  = false;
  var ovOnlineTimer   = null;

  var ACCENT_COLORS = ['#1a56db','#7c3aed','#059669','#d97706','#dc2626','#0891b2','#be185d','#065f46'];

  /* OS/Browser icon map */
  var BROWSER_ICONS = {
    'Chrome':'🌐','Firefox':'🦊','Safari':'🧭','Edge':'🔷','Opera':'🔴',
    'Samsung Internet':'📱','IE':'🔵','Brave':'🦁','Vivaldi':'🎻',
    'Unknown':'❓','Headless Chrome':'🤖','Other':'🌐'
  };

  var OS_ICONS = {
    'Windows':'🪟','GNU/Linux':'🐧','Android':'🤖','iOS':'🍎',
    'macOS':'🍎','Ubuntu':'🐧','ChromeOS':'💻','Unknown':'❓','Other':'💻'
  };

  var DEVICE_ICONS = { 'desktop':'🖥','mobile':'📱','smartphone':'📱','tablet':'📟','(not set)':'❓','unknown':'❓' };

  /* ── SHELL ELEMENT ───────────────────────────────────────── */
  var shell = document.getElementById('vsr-stats-shell');
  if (!shell) return;

  /* ── THEME ───────────────────────────────────────────────── */
  /* The light/dark theme is set on the Settings screen (General → Appearance)
     and rendered onto the shell's data-theme attribute server-side. Chart
     colors read that attribute at draw time — nothing to do at runtime. */

  /* ── NAVIGATION ──────────────────────────────────────────── */
  var sectionTitles = {
    overview: 'Overview',
    pages:    'Pages',
    audience: 'Audience',
    traffic:  'Traffic Sources',
    realtime: 'Real-time',
  };

  shell.querySelectorAll('.nav-item').forEach(function (btn) {
    btn.addEventListener('click', function () {
      var s = btn.dataset.section;
      shell.querySelectorAll('.nav-item').forEach(function (b) { b.classList.remove('active'); });
      btn.classList.add('active');
      shell.querySelectorAll('.section').forEach(function (el) { el.classList.remove('active'); });
      var sec = document.getElementById('sec-' + s);
      if (sec) sec.classList.add('active');
      var title = document.getElementById('vsrSectionTitle');
      if (title) title.textContent = sectionTitles[s] || s;
      currentSection = s;
      loadSection(s);
      // Real-time auto-refresh
      if (rtInterval) { clearInterval(rtInterval); rtInterval = null; }
      if (s === 'realtime') {
        rtInterval = setInterval(function () { loadSection('realtime'); }, 15000);
      }
      // Stop overview online-now timer when leaving overview
      if (s !== 'overview' && ovOnlineTimer) {
        clearInterval(ovOnlineTimer); ovOnlineTimer = null;
      }
    });
  });

  /* ── RANGE TABS ──────────────────────────────────────────── */
  shell.querySelectorAll('.range-tab').forEach(function (btn) {
    btn.addEventListener('click', function () {
      shell.querySelectorAll('.range-tab').forEach(function (b) { b.classList.remove('active'); });
      btn.classList.add('active');
      currentRange = parseInt(btn.dataset.range, 10);
      loadSection(currentSection);
    });
  });

  /* ── API HELPER ──────────────────────────────────────────── */
  function api(action) {
    var path = 'stats/' + action + '?range=' + currentRange;
    // recent doesn't take range
    if (action === 'recent') path = 'stats/recent';
    return VSR.get(path);
  }

  /* ── LOAD SECTION ────────────────────────────────────────── */
  function loadSection(s) {
    var rangeLabel = currentRange === 365 ? '1 year' : currentRange + ' days';
    shell.querySelectorAll('[id$="Badge"]').forEach(function (el) { el.textContent = rangeLabel; });

    if (s === 'overview')  loadOverview();
    if (s === 'pages')     loadPages();
    if (s === 'audience')  loadAudience();
    if (s === 'traffic')   loadTraffic();
    if (s === 'realtime')  loadRealtime();
  }

  /* ═══════════════════════════════════════════════════════════
     OVERVIEW — 14-widget modular system
     ═══════════════════════════════════════════════════════════ */

  /* ── Collapse / expand ───────────────────────────────────── */
  document.addEventListener('click', function (e) {
    var btn = e.target.closest('.ov-collapse-btn');
    if (!btn) return;
    var cardId = btn.dataset.target;
    if (!cardId) return;
    var card = document.getElementById(cardId);
    if (card) card.classList.toggle('is-collapsed');
  });

  /* ── loadOverview entry point ────────────────────────────── */
  function loadOverview() {
    ovWidget1_TrafficSummary();
    ovWidget2_Trend();
    ovWidget3_Search();
    ovWidget4_Browsers();
    ovWidget5_OS();
    ovWidget6_Devices();
    ovWidget7_DeviceModels();
    ovWidget8_Countries();
    ovWidget9_Referrers();
    ovWidget10_Pages();
    ovWidget11_MostActive();
    ovWidget12_Latest();
    ovWidget13_OnlineNow();
    ovWidget14_Map();

    // update online count in topbar
    api('summary').then(function (s) {
      setText('vsrOnlineCount', s.online);
      setText('vsrRtOnline',    s.online);
    });
  }

  /* ── Widget 1: Traffic Summary ───────────────────────────── */
  function ovWidget1_TrafficSummary() {
    VSR.get('stats/traffic_summary').then(function (d) {
      var rows = [
        { label: 'Today',        key: 'today',      hi: true },
        { label: 'Yesterday',    key: 'yesterday' },
        { label: 'This Week',    key: 'this_week' },
        { label: 'Last Week',    key: 'last_week' },
        { label: 'This Month',   key: 'this_month' },
        { label: 'Last Month',   key: 'last_month' },
        { label: 'Last 7 Days',  key: 'last_7' },
        { label: 'Last 30 Days', key: 'last_30' },
        { label: 'Last 90 Days', key: 'last_90' },
        { label: 'This Year',    key: 'this_year' },
        { label: 'Total',        key: 'total',      hi: true },
      ];
      var h = '';
      rows.forEach(function (r) {
        var p = d[r.key] || { visitors: 0, page_views: 0 };
        h += '<tr' + (r.hi ? ' class="ov-row-highlight"' : '') + '>' +
          '<td>' + r.label + '</td>' +
          '<td>' + fmt(p.visitors) + '</td>' +
          '<td>' + fmt(p.page_views) + '</td>' +
          '</tr>';
      });
      setHTML('ov-traffic-summary-body', h);
    });
  }

  /* ── Widget 2: Traffic Trend ─────────────────────────────── */
  function ovWidget2_Trend() {
    if (typeof Chart === 'undefined') return;
    var endpoint = ovTrendGran === 'weekly' ? 'chart_weekly' : 'chart';

    var currentPromise  = VSR.get('stats/' + endpoint + '?range=' + currentRange);
    var comparePromise  = ovTrendCompare
      ? VSR.get('stats/' + endpoint + '?range=' + (currentRange * 2))
      : Promise.resolve(null);

    Promise.all([currentPromise, comparePromise]).then(function (res) {
      var rows    = res[0];
      var rowsPrev = res[1];
      var isDark  = shell.getAttribute('data-theme') === 'dark';
      var grid    = isDark ? 'rgba(255,255,255,.05)' : 'rgba(0,0,0,.05)';
      var txt     = isDark ? '#9a99a8' : '#9e9690';
      var fmtFn   = ovTrendGran === 'weekly'
        ? function (r) { return 'Wk ' + String(r.yw).slice(-2); }
        : function (r) { return fmtDate(r.date); };

      var labels   = rows.map(fmtFn);
      var visitors = rows.map(function (r) { return +r.visitors; });
      var views    = rows.map(function (r) { return +r.page_views; });

      var datasets = [
        {
          label: 'Visitors',
          data: visitors,
          borderColor: '#1a56db',
          backgroundColor: 'rgba(26,86,219,.08)',
          borderWidth: 2, pointRadius: 2, pointHoverRadius: 5,
          fill: true, tension: .4,
        },
        {
          label: 'Page Views',
          data: views,
          borderColor: '#059669',
          backgroundColor: 'rgba(5,150,105,.06)',
          borderWidth: 2, pointRadius: 2, pointHoverRadius: 5,
          fill: true, tension: .4,
        },
      ];

      if (rowsPrev) {
        var prevSlice = rowsPrev.slice(0, currentRange);
        datasets.push({
          label: 'Visitors (prev)',
          data: prevSlice.map(function (r) { return +r.visitors; }),
          borderColor: 'rgba(26,86,219,.35)',
          borderDash: [5, 4],
          backgroundColor: 'transparent',
          borderWidth: 1.5, pointRadius: 0, fill: false, tension: .4,
        });
        datasets.push({
          label: 'Views (prev)',
          data: prevSlice.map(function (r) { return +r.page_views; }),
          borderColor: 'rgba(5,150,105,.35)',
          borderDash: [5, 4],
          backgroundColor: 'transparent',
          borderWidth: 1.5, pointRadius: 0, fill: false, tension: .4,
        });
      }

      var canvas = document.getElementById('ov-trend-chart');
      if (!canvas) return;
      if (ovTrendChart) { ovTrendChart.destroy(); ovTrendChart = null; }
      ovTrendChart = new Chart(canvas, {
        type: 'line',
        data: { labels: labels, datasets: datasets },
        options: {
          responsive: true,
          maintainAspectRatio: false,
          interaction: { mode: 'index', intersect: false },
          plugins: {
            legend: {
              labels: { color: txt, font: { family: 'Plus Jakarta Sans', size: 11 }, boxWidth: 12, usePointStyle: true },
            },
          },
          scales: {
            x: { grid: { color: grid }, ticks: { color: txt, font: { family: 'Plus Jakarta Sans', size: 11 }, maxTicksLimit: 10 } },
            y: { grid: { color: grid }, ticks: { color: txt, font: { family: 'Plus Jakarta Sans', size: 11 } }, beginAtZero: true },
          },
        },
      });
    });

    /* granularity tabs */
    var gran = document.getElementById('ov-trend-gran');
    if (gran && !gran._bound) {
      gran._bound = true;
      gran.querySelectorAll('.ov-mini-tab').forEach(function (btn) {
        btn.addEventListener('click', function () {
          gran.querySelectorAll('.ov-mini-tab').forEach(function (b) { b.classList.remove('active'); });
          btn.classList.add('active');
          ovTrendGran = btn.dataset.gran;
          ovWidget2_Trend();
        });
      });
    }

    /* compare checkbox */
    var cmp = document.getElementById('ov-trend-compare');
    if (cmp && !cmp._bound) {
      cmp._bound = true;
      cmp.addEventListener('change', function () {
        ovTrendCompare = cmp.checked;
        ovWidget2_Trend();
      });
    }
  }

  /* ── Widget 3: Search Engine Referrals ───────────────────── */
  function ovWidget3_Search() {
    api('search_engines').then(function (rows) {
      var total = rows.reduce(function (a, r) { return a + +r.v; }, 0);
      var google = rows.find(function (r) { return /google/i.test(r.engine); });
      var googleV = google ? +google.v : 0;
      var otherV  = total - googleV;

      var kpi = '<div class="ov-search-kpi-strip">' +
        '<div class="ov-search-kpi-item"><span class="ov-search-kpi-val">' + fmt(total) + '</span><span class="ov-search-kpi-lbl">Total Referrals</span></div>' +
        '<div class="ov-search-kpi-item"><span class="ov-search-kpi-val">' + fmt(googleV) + '</span><span class="ov-search-kpi-lbl">Google</span></div>' +
        '<div class="ov-search-kpi-item"><span class="ov-search-kpi-val">' + fmt(otherV) + '</span><span class="ov-search-kpi-lbl">Others</span></div>' +
        '</div>';
      setHTML('ov-search-kpi', kpi);

      /* mini line chart of search trend */
      if (typeof Chart !== 'undefined') {
        var canvas = document.getElementById('ov-search-chart');
        if (canvas) {
          if (ovSearchChart) { ovSearchChart.destroy(); ovSearchChart = null; }
          var isDark = shell.getAttribute('data-theme') === 'dark';
          ovSearchChart = new Chart(canvas, {
            type: 'bar',
            data: {
              labels: rows.map(function (r) { return r.engine; }),
              datasets: [{
                label: 'Referrals',
                data: rows.map(function (r) { return +r.v; }),
                backgroundColor: 'rgba(26,86,219,.55)',
                borderRadius: 4,
                borderSkipped: false,
              }],
            },
            options: {
              responsive: true, maintainAspectRatio: false,
              plugins: { legend: { display: false } },
              scales: {
                x: { grid: { display: false }, ticks: { color: isDark ? '#9a99a8' : '#9e9690', font: { size: 10 }, maxRotation: 30 } },
                y: { display: false, beginAtZero: true },
              },
            },
          });
        }
      }

      var max = total || 1;
      var bars = '';
      rows.forEach(function (r) {
        var pct = Math.round(+r.v / max * 100);
        bars += ovStatRow(r.engine, '🔍', +r.v, pct, max);
      });
      setHTML('ov-search-bars', bars || ovEmpty());
    });
  }

  /* ── Widget 4: Browser Usage ─────────────────────────────── */
  function ovWidget4_Browsers() {
    api('browsers').then(function (rows) {
      var total = rows.reduce(function (a, r) { return a + +r.v; }, 0) || 1;
      var h = '';
      rows.forEach(function (r) {
        var pct = Math.round(+r.v / total * 100);
        var icon = BROWSER_ICONS[r.browser] || BROWSER_ICONS['Other'];
        h += ovStatRow(r.browser, icon, +r.v, pct, total);
      });
      setHTML('ov-browsers-body', h || ovEmpty());
    });
  }

  /* ── Widget 5: Operating Systems ────────────────────────────*/
  function ovWidget5_OS() {
    api('platforms').then(function (rows) {
      var total = rows.reduce(function (a, r) { return a + +r.v; }, 0) || 1;
      var h = '';
      rows.forEach(function (r) {
        var pct  = Math.round(+r.v / total * 100);
        var icon = OS_ICONS[r.platform] || OS_ICONS['Other'];
        h += ovStatRow(r.platform, icon, +r.v, pct, total);
      });
      setHTML('ov-os-body', h || ovEmpty());
    });
  }

  /* ── Widget 6: Device Usage ──────────────────────────────── */
  function ovWidget6_Devices() {
    api('devices').then(function (rows) {
      var total = rows.reduce(function (a, r) { return a + +r.v; }, 0) || 1;
      var h = '';
      rows.forEach(function (r) {
        var dt   = (r.device_type || 'unknown').toLowerCase();
        var pct  = Math.round(+r.v / total * 100);
        var icon = DEVICE_ICONS[dt] || '💻';
        h += ovStatRow(cap(r.device_type || 'Unknown'), icon, +r.v, pct, total);
      });
      setHTML('ov-devices-body', h || ovEmpty());
    });
  }

  /* ── Widget 7: Top Device Models (device×browser combo) ─── */
  function ovWidget7_DeviceModels() {
    Promise.all([api('devices'), api('browsers')]).then(function (res) {
      var devices  = res[0];
      var browsers = res[1];
      var total = browsers.reduce(function (a, r) { return a + +r.v; }, 0) || 1;

      /* Derive "models": mobile/tablet → Apple / Android / Other; desktop by browser */
      var models = [];
      var mobileRows = devices.filter(function (r) {
        var dt = (r.device_type || '').toLowerCase();
        return dt === 'mobile' || dt === 'smartphone' || dt === 'tablet';
      });
      var mobileTotal = mobileRows.reduce(function (a, r) { return a + +r.v; }, 0);

      /* Approximate Apple share via Safari */
      var safari = browsers.find(function (b) { return /safari/i.test(b.browser) && !/chrome/i.test(b.browser); });
      var safariV = safari ? Math.min(+safari.v, mobileTotal) : 0;
      var androidV = Math.max(0, mobileTotal - safariV);

      if (safariV > 0)  models.push({ label: 'Apple (iPhone/iPad)', icon: '🍎', v: safariV });
      if (androidV > 0) models.push({ label: 'Android',             icon: '🤖', v: androidV });

      var desktopRows = devices.filter(function (r) {
        return (r.device_type || '').toLowerCase() === 'desktop';
      });
      var desktopTotal = desktopRows.reduce(function (a, r) { return a + +r.v; }, 0);

      browsers.slice(0, 3).forEach(function (b) {
        var approx = Math.round(+b.v * desktopTotal / (total || 1));
        if (approx > 0) {
          models.push({ label: b.browser + ' (Desktop)', icon: BROWSER_ICONS[b.browser] || '🖥', v: approx });
        }
      });

      var notSet = devices.find(function (r) { return !r.device_type || r.device_type === 'unknown'; });
      if (notSet) models.push({ label: '(not set)', icon: '❓', v: +notSet.v });

      models.sort(function (a, b) { return b.v - a.v; });
      var gTotal = models.reduce(function (a, m) { return a + m.v; }, 0) || 1;
      var h = '';
      models.forEach(function (m) {
        h += ovStatRow(m.label, m.icon, m.v, Math.round(m.v / gTotal * 100), gTotal);
      });
      setHTML('ov-device-models-body', h || ovEmpty());
    });
  }

  /* ── Widget 8: Top Countries ─────────────────────────────── */
  function ovWidget8_Countries() {
    api('countries').then(function (rows) {
      var total = rows.reduce(function (a, r) { return a + +r.v; }, 0) || 1;
      var h = '';
      rows.forEach(function (r) {
        var pct = Math.round(+r.v / total * 100);
        var flag = countryFlag(r.country_code);
        h += '<div class="ov-stat-row">' +
          '<div class="ov-stat-icon" style="font-size:18px;background:none;border:none">' + flag + '</div>' +
          '<div class="ov-stat-info">' +
            '<div class="ov-stat-name">' + esc(r.country_name || r.country_code || 'Unknown') + '</div>' +
            '<div class="ov-stat-bar"><div class="ov-stat-bar-fill" style="width:' + pct + '%"></div></div>' +
          '</div>' +
          '<span class="ov-stat-pct">' + pct + '%</span>' +
          '<span class="ov-stat-count">' + fmt(r.v) + '</span>' +
          '</div>';
      });
      setHTML('ov-countries-body', h || ovEmpty());
    });
  }

  /* ── Widget 9: Top Referring Domains ─────────────────────── */
  function ovWidget9_Referrers() {
    api('referrers').then(function (rows) {
      var max = Math.max.apply(null, rows.map(function (r) { return +r.v; }).concat([1]));
      var h = '';
      rows.forEach(function (r, i) {
        var w = Math.round(+r.v / max * 100);
        h += '<tr>' +
          '<td style="color:var(--text3);width:28px">' + (i + 1) + '</td>' +
          '<td class="ov-td-main" style="max-width:180px">' +
            '<div class="ov-minibar-wrap">' +
              esc(r.host || 'Direct') +
              '<div class="ov-minibar"><div class="ov-minibar-fill" style="width:' + w + '%"></div></div>' +
            '</div>' +
          '</td>' +
          '<td class="ov-td-num">' + fmt(r.v) + '</td>' +
          '</tr>';
      });
      setHTML('ov-referrers-body',
        h ? '<div class="ov-tbl-wrap"><table class="ov-table"><thead><tr><th>#</th><th>Domain</th><th>Visits</th></tr></thead><tbody>' + h + '</tbody></table></div>'
          : ovEmpty()
      );
    });
  }

  /* ── Widget 10: Top Pages ────────────────────────────────── */
  function ovWidget10_Pages() {
    api('pages').then(function (rows) {
      var max = Math.max.apply(null, rows.map(function (r) { return +r.v; }).concat([1]));
      var h = '';
      rows.forEach(function (r, i) {
        var w   = Math.round(+r.v / max * 100);
        var url = esc(new URL(vlensAdmin.siteUrl).origin + (r.page_uri || '/'));
        h += '<tr>' +
          '<td style="color:var(--text3);width:28px">' + (i + 1) + '</td>' +
          '<td class="ov-td-main" style="max-width:200px">' +
            '<div style="font-size:12px;font-weight:600;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis" title="' + esc(r.page_uri) + '">' + esc(r.page_title || r.page_uri) + '</div>' +
            '<div style="font-size:11px;color:var(--text3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">' + esc(r.page_uri) + '</div>' +
          '</td>' +
          '<td>' +
            '<div class="ov-minibar-wrap">' +
              '<span class="ov-td-num" style="white-space:nowrap">' + fmt(r.v) + '</span>' +
              '<div class="ov-minibar"><div class="ov-minibar-fill" style="width:' + w + '%"></div></div>' +
            '</div>' +
          '</td>' +
          '<td><a href="' + url + '" target="_blank" class="ov-view-btn">&#8599; View</a></td>' +
          '</tr>';
      });
      setHTML('ov-pages-body', h || '<tr><td colspan="4" style="color:var(--text3);text-align:center;padding:20px">No data</td></tr>');
    });
  }

  /* ── Widget 11: Most Active Visitors ─────────────────────── */
  function ovWidget11_MostActive() {
    VSR.get('stats/most_active?range=' + currentRange).then(function (rows) {
      var h = '';
      rows.forEach(function (r) {
        var visitorCell =
          countryFlag(r.country_code) +
          '<span class="ov-visitor-ip">' + esc(r.ip_raw || '—') + '</span>' +
          ' <span class="ov-device-pill">' + (DEVICE_ICONS[(r.device_type || '').toLowerCase()] || '💻') + ' ' + esc(cap(r.device_type) || '?') + '</span>';

        var origin = new URL(vlensAdmin.siteUrl).origin;
        var entryLink = r.entry_page
          ? '<a href="' + esc(origin + r.entry_page) + '" target="_blank" class="ov-page-link" title="' + esc(r.entry_page) + '">' + esc(r.entry_page) + '</a>'
          : '<span style="color:var(--text3)">—</span>';
        var exitLink = r.exit_page
          ? '<a href="' + esc(origin + r.exit_page) + '" target="_blank" class="ov-page-link" title="' + esc(r.exit_page) + '">' + esc(r.exit_page) + '</a>'
          : '<span style="color:var(--text3)">—</span>';

        h += '<tr>' +
          '<td><span class="ov-views-badge">' + fmt(r.views) + '</span></td>' +
          '<td><div class="ov-visitor-cell">' + visitorCell + '</div></td>' +
          '<td style="color:var(--text3);font-size:12px;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc(r.referrer || 'Direct') + '</td>' +
          '<td class="ov-page-cell">' + entryLink + '</td>' +
          '<td class="ov-page-cell">' + exitLink + '</td>' +
          '<td style="font-size:11px;color:var(--text3);white-space:nowrap">' + fmtTime(r.last_seen) + '</td>' +
          '</tr>';
      });
      setHTML('ov-most-active-body', h || '<tr><td colspan="6" style="color:var(--text3);text-align:center;padding:20px">No data</td></tr>');
    });
  }

  /* ── Widget 12: Latest Visitors ──────────────────────────── */
  function ovWidget12_Latest() {
    api('recent').then(function (data) {
      var visitors  = Array.isArray(data) ? data : (data.visitors || []);
      var onlineIps = Array.isArray(data) ? [] : (data.online_ips || []);

      /* Aggregate views per IP */
      var viewCount = {};
      visitors.forEach(function (r) {
        viewCount[r.ip_raw] = (viewCount[r.ip_raw] || 0) + 1;
      });

      /* Dedupe to one row per visitor (latest) */
      var seen = {};
      var deduped = [];
      visitors.forEach(function (r) {
        if (!seen[r.ip_raw]) { seen[r.ip_raw] = true; deduped.push(r); }
      });

      var h = '';
      deduped.slice(0, 15).forEach(function (r) {
        var isOnline = onlineIps.indexOf(r.ip_raw) !== -1;
        var dot = isOnline
          ? '<span class="ov-live-dot" style="margin-right:4px"></span>'
          : '';

        var srcBadge = '<span class="badge badge-' + (r.referrer_type || 'direct') + '">' + cap(r.referrer_type || 'direct') + '</span>';
        h += '<tr>' +
          '<td style="white-space:nowrap;color:var(--text3);font-size:12px">' + fmtTime(r.created_at) + '</td>' +
          '<td><div class="ov-visitor-cell">' + dot + countryFlag(r.country_code) + '<span class="ov-visitor-ip">' + esc(r.ip_raw || '—') + '</span></div></td>' +
          '<td>' + srcBadge + '</td>' +
          '<td class="ov-td-main" style="max-width:180px;font-size:12px" title="' + esc(r.page_uri) + '">' + esc(r.page_uri || '/') + '</td>' +
          '<td><span class="ov-views-badge">' + fmt(viewCount[r.ip_raw] || 1) + '</span></td>' +
          '</tr>';
      });
      setHTML('ov-latest-body', h || '<tr><td colspan="5" style="color:var(--text3);text-align:center;padding:20px">No recent visitors</td></tr>');
    });
  }

  /* ── Widget 13: Currently Online ─────────────────────────── */
  function ovWidget13_OnlineNow() {
    VSR.get('stats/online_now').then(function (rows) {
      if (!rows || !rows.length) {
        setHTML('ov-online-now-body',
          '<div class="ov-online-empty">' +
          '<svg width="38" height="38" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>' +
          '<p>No activity right now</p></div>'
        );
        return;
      }
      var h = '<div class="ov-online-list">';
      rows.forEach(function (r) {
        h += '<div class="ov-online-row">' +
          '<span class="ov-live-dot"></span>' +
          countryFlag(r.country_code) +
          '<span class="ov-online-ip">' + esc(r.ip_raw || '—') + '</span>' +
          '<span class="ov-online-page">' + esc(r.page_uri || '/') + '</span>' +
          '<span class="ov-online-time">' + fmtTimeAgo(r.seconds_ago) + '</span>' +
          '</div>';
      });
      h += '</div>';
      setHTML('ov-online-now-body', h);
    });

    /* auto-refresh every 15s while on overview */
    if (!ovOnlineTimer) {
      ovOnlineTimer = setInterval(function () {
        if (currentSection === 'overview') ovWidget13_OnlineNow();
      }, 15000);
    }
  }

  /* ── Widget 14: Global Visitor Distribution ──────────────── */
  function ovWidget14_Map() {
    api('countries').then(function (rows) {
      var total = rows.reduce(function (a, r) { return a + +r.v; }, 0) || 1;
      /* Two-column grid of countries with bar */
      var h = '<div class="ov-map-grid">';
      rows.forEach(function (r) {
        var pct = Math.round(+r.v / total * 100);
        h += '<div class="ov-map-row">' +
          '<span class="ov-map-flag">' + countryFlag(r.country_code) + '</span>' +
          '<div class="ov-map-info">' +
            '<div class="ov-map-country">' + esc(r.country_name || r.country_code) + '</div>' +
            '<div class="ov-map-bar"><div class="ov-map-bar-fill" style="width:' + pct + '%"></div></div>' +
          '</div>' +
          '<span class="ov-map-count">' + fmt(r.v) + '</span>' +
          '<span class="ov-map-pct">' + pct + '%</span>' +
          '</div>';
      });
      h += '</div>';
      setHTML('ov-map-countries', rows.length ? h : '<p style="color:var(--text3);padding:20px;text-align:center">No geographic data</p>');
    });
  }

  /* ── Overview helpers ────────────────────────────────────── */
  function ovStatRow(label, icon, count, pct, total) {
    void total; // used by caller for reference
    return '<div class="ov-stat-row">' +
      '<div class="ov-stat-icon">' + icon + '</div>' +
      '<div class="ov-stat-info">' +
        '<div class="ov-stat-name" title="' + esc(label) + '">' + esc(label) + '</div>' +
        '<div class="ov-stat-bar"><div class="ov-stat-bar-fill" style="width:' + pct + '%"></div></div>' +
      '</div>' +
      '<span class="ov-stat-pct">' + pct + '%</span>' +
      '<span class="ov-stat-count">' + fmt(count) + '</span>' +
      '</div>';
  }

  function ovEmpty() { return '<p style="color:var(--text3);font-size:12px;padding:16px 0">No data available</p>'; }

  function fmtTimeAgo(seconds) {
    if (seconds === null || seconds === undefined) return '—';
    var diff = Math.max(0, Math.floor(seconds));
    if (diff < 60)  return diff + 's ago';
    if (diff < 3600) return Math.floor(diff / 60) + 'm ago';
    return Math.floor(diff / 3600) + 'h ago';
  }

  function fmtMs(ms) {
    if (ms >= 1000) return (ms / 1000).toFixed(2) + 's';
    return ms + 'ms';
  }


  /* ── PAGES ───────────────────────────────────────────────── */
  function loadPages() {
    api('pages').then(function (rows) {
      var max = Math.max.apply(null, rows.map(function (r) { return +r.v; }).concat([1]));
      var html = '';
      rows.forEach(function (r, i) {
        html += '<tr>' +
          '<td>' + (i + 1) + '</td>' +
          '<td class="tdmain" style="max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + esc(r.page_uri) + '">' + esc(r.page_uri) + '</td>' +
          '<td style="color:var(--text3);max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + esc(r.page_title || '—') + '</td>' +
          '<td><strong>' + fmt(r.v) + '</strong>' +
            '<div style="height:4px;background:var(--bg3);border-radius:2px;margin-top:4px;width:100px">' +
              '<div style="height:4px;background:var(--accent);border-radius:2px;width:' + Math.round(r.v / max * 100) + '%"></div>' +
            '</div>' +
          '</td></tr>';
      });
      setHTML('vsrPagesBody', html || '<tr><td colspan="4" style="color:var(--text3)">No data</td></tr>');
    });
  }

  /* ── AUDIENCE ────────────────────────────────────────────── */
  function loadAudience() {
    Promise.all([api('countries'), api('browsers'), api('platforms'), api('devices')]).then(function (res) {
      loadCountries(res[0]);
      loadBars('vsrBrowsersBody',  res[1], 'browser',  'v');
      loadBars('vsrPlatformsBody', res[2], 'platform', 'v');
      loadDevices(res[3]);
    });
  }

  function loadCountries(rows) {
    var max = Math.max.apply(null, rows.map(function (r) { return +r.v; }).concat([1]));
    var h = '';
    rows.forEach(function (r) {
      h += '<div class="bar-row">' +
        '<div class="bar-label" title="' + esc(r.country_name) + '">' + countryFlag(r.country_code) + esc(r.country_name || r.country_code) + '</div>' +
        '<div class="bar-track"><div class="bar-fill" style="width:' + Math.round(r.v / max * 100) + '%"></div></div>' +
        '<div class="bar-count">' + fmt(r.v) + '</div>' +
        '</div>';
    });
    setHTML('vsrCountriesBody', h || '<p style="color:var(--text3)">No data</p>');
  }

  function loadDevices(rows) {
    if (typeof Chart === 'undefined') return;
    var canvas = document.getElementById('vsrDeviceChart');
    if (!canvas) return;

    var labels = rows.map(function (r) { return cap(r.device_type); });
    var data   = rows.map(function (r) { return +r.v; });
    var colors = ['#1a56db', '#059669', '#d97706', '#dc2626', '#7c3aed'];

    if (deviceChartInst) { deviceChartInst.destroy(); deviceChartInst = null; }
    deviceChartInst = new Chart(canvas, {
      type: 'doughnut',
      data: {
        labels: labels,
        datasets: [{ data: data, backgroundColor: colors, borderWidth: 0, hoverOffset: 4 }],
      },
      options: {
        responsive: false,
        cutout: '68%',
        plugins: {
          legend: { display: false },
          tooltip: {
            callbacks: {
              label: function (ctx) { return ctx.label + ': ' + fmt(ctx.raw); },
            },
          },
        },
      },
    });

    var total = data.reduce(function (a, b) { return a + b; }, 0);
    var leg = '';
    labels.forEach(function (l, i) {
      var pct = total ? Math.round(data[i] / total * 100) : 0;
      leg += '<div class="legend-row">' +
        '<div class="legend-label"><span class="legend-dot" style="background:' + colors[i] + '"></span>' + l + '</div>' +
        '<div class="legend-val">' + pct + '%</div>' +
        '</div>';
    });
    setHTML('vsrDeviceLegend', leg || 'No data');
  }

  /* ── TRAFFIC ─────────────────────────────────────────────── */
  function loadTraffic() {
    Promise.all([api('referrers'), api('search_engines')]).then(function (res) {
      var referrers = res[0];
      var search    = res[1];

      var maxR = Math.max.apply(null, referrers.map(function (r) { return +r.v; }).concat([1]));
      var h = '';
      referrers.forEach(function (r) {
        h += '<div class="bar-row">' +
          '<div class="bar-label" title="' + esc(r.host) + '">' + esc(r.host || 'Direct') + '</div>' +
          '<div class="bar-track"><div class="bar-fill" style="width:' + Math.round(r.v / maxR * 100) + '%"></div></div>' +
          '<div class="bar-count">' + fmt(r.v) + '</div>' +
          '</div>';
      });
      setHTML('vsrReferrersBody', h || '<p style="color:var(--text3)">No referrers</p>');

      var maxS = Math.max.apply(null, search.map(function (r) { return +r.v; }).concat([1]));
      var hs = '';
      search.forEach(function (r) {
        hs += '<div class="bar-row">' +
          '<div class="bar-label">' + esc(r.engine) + '</div>' +
          '<div class="bar-track"><div class="bar-fill" style="width:' + Math.round(r.v / maxS * 100) + '%;background:#059669"></div></div>' +
          '<div class="bar-count">' + fmt(r.v) + '</div>' +
          '</div>';
      });
      setHTML('vsrSearchBody', hs || '<p style="color:var(--text3)">No search traffic</p>');
    });
  }

  /* ── REAL-TIME ───────────────────────────────────────────── */
  function loadRealtime() {
    Promise.all([api('summary'), api('recent')]).then(function (res) {
      var summary    = res[0];
      var recentData = res[1];

      setText('vsrRtOnline',    summary.online);
      setText('vsrOnlineCount', summary.online);

      var recent    = Array.isArray(recentData) ? recentData : (recentData.visitors  || []);
      var onlineIps = Array.isArray(recentData) ? []         : (recentData.online_ips || []);

      var h = '';
      recent.forEach(function (r) {
        var badgeClass = 'badge-' + (r.referrer_type || 'direct');
        var badge      = '<span class="badge ' + badgeClass + '">' + cap(r.referrer_type || 'direct') + '</span>';
        var isOnline   = (r.is_online === true || r.is_online === 1 || r.is_online === '1')
                         || (r.ip_raw && onlineIps.indexOf(r.ip_raw) !== -1);
        var statusHtml = isOnline
          ? '<div class="status-cell online"><span class="status-dot online"></span>Online</div>'
          : '<div class="status-cell offline"><span class="status-dot offline"></span>Offline</div>';

        var replayCell = '';
        if (r.session_id) {
          var replayUrl = vlensAdmin.adminUrl + 'admin.php?page=visitorlens-replay&sid=' + r.session_id;
          replayCell = '<a href="' + replayUrl + '" style="display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:600;color:var(--accent);text-decoration:none;padding:3px 8px;border-radius:5px;background:rgba(26,86,219,0.1);border:1px solid rgba(26,86,219,0.2);white-space:nowrap">&#9654; Replay</a>';
        }

        var userCell = '';
        if (r.wp_user_id && +r.wp_user_id > 0) {
          var editUrl = vlensAdmin.adminUrl + 'user-edit.php?user_id=' + r.wp_user_id;
          userCell = '<a href="' + esc(editUrl) + '" target="_blank" style="font-size:12px;font-weight:600;color:var(--accent);text-decoration:none;white-space:nowrap">' + esc(r.wp_display_name || 'User #' + r.wp_user_id) + '</a>';
        } else {
          userCell = '<span style="color:var(--text3)">—</span>';
        }

        h += '<tr>' +
          '<td>' + statusHtml + '</td>' +
          '<td style="white-space:nowrap;color:var(--text3)">' + fmtTime(r.created_at) + '</td>' +
          '<td style="font-family:monospace;font-size:12px;color:var(--text2)">' + esc(r.ip_raw || '—') + '</td>' +
          '<td>' + userCell + '</td>' +
          '<td class="tdmain" style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + esc(r.page_uri) + '">' +
            '<a href="' + esc(new URL(vlensAdmin.siteUrl).origin + (r.page_uri || '/')) + '" target="_blank" style="color:var(--accent);text-decoration:none;font-size:12px">' + esc(r.page_uri || '/') + '</a>' +
          '</td>' +
          '<td>' + countryFlag(r.country_code) + esc(r.country_name || r.country_code || '—') + '</td>' +
          '<td>' + esc(r.browser   || '—') + '</td>' +
          '<td>' + esc(r.platform  || '—') + '</td>' +
          '<td>' + cap(r.device_type || '—') + '</td>' +
          '<td>' + badge + '</td>' +
          '<td>' + replayCell + '</td>' +
          '</tr>';
      });
      setHTML('vsrRecentBody', h || '<tr><td colspan="11" style="color:var(--text3)">No recent visitors</td></tr>');
    });
  }

  /* ── GENERIC BAR RENDERER ────────────────────────────────── */
  function loadBars(elId, rows, labelKey, valKey) {
    var max = Math.max.apply(null, rows.map(function (r) { return +r[valKey]; }).concat([1]));
    var h   = '';
    rows.forEach(function (r) {
      h += '<div class="bar-row">' +
        '<div class="bar-label" title="' + esc(r[labelKey]) + '">' + esc(r[labelKey] || 'Unknown') + '</div>' +
        '<div class="bar-track"><div class="bar-fill" style="width:' + Math.round(r[valKey] / max * 100) + '%"></div></div>' +
        '<div class="bar-count">' + fmt(r[valKey]) + '</div>' +
        '</div>';
    });
    setHTML(elId, h || '<p style="color:var(--text3);font-size:13px">No data</p>');
  }

  /* ── UTILS ───────────────────────────────────────────────── */
  function fmt(n)  { return (+n).toLocaleString(); }
  function cap(s)  { return s ? s.charAt(0).toUpperCase() + s.slice(1) : s; }
  function esc(s)  { var d = document.createElement('div'); d.textContent = s || ''; return d.innerHTML; }
  function setText(id, val) { var el = document.getElementById(id); if (el) el.textContent = val; }
  function setHTML(id, val) { var el = document.getElementById(id); if (el) el.innerHTML  = val; }

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

  function fmtTime(dt) {
    if (!dt) return '—';
    return new Date(dt).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });
  }

  function countryFlag(code) {
    if (!code || code === '--') return '';
    try {
      var flag = code.toUpperCase().replace(/./g, function (c) {
        return String.fromCodePoint(127397 + c.charCodeAt(0));
      });
      return '<span class="flag">' + flag + '</span>';
    } catch (e) { return ''; }
  }

  /* ── PER-CARD & GLOBAL REFRESH ──────────────────────────── */

  var REFRESH_SVG = '<svg class="vsr-spin-icon" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M23 4v6h-6"/><path d="M1 20v-6h6"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>';

  /* Map: overview card element ID → its individual refresh function */
  var OV_CARD_REFRESH = {
    'ov-card-traffic-summary': ovWidget1_TrafficSummary,
    'ov-card-trend':           ovWidget2_Trend,
    'ov-card-search':          ovWidget3_Search,
    'ov-card-browsers':        ovWidget4_Browsers,
    'ov-card-os':              ovWidget5_OS,
    'ov-card-devices':         ovWidget6_Devices,
    'ov-card-device-models':   ovWidget7_DeviceModels,
    'ov-card-countries':       ovWidget8_Countries,
    'ov-card-referrers':       ovWidget9_Referrers,
    'ov-card-pages':           ovWidget10_Pages,
    'ov-card-most-active':     ovWidget11_MostActive,
    'ov-card-latest':          ovWidget12_Latest,
    'ov-card-online-now':      ovWidget13_OnlineNow,
    'ov-card-map':             ovWidget14_Map,
  };

  /* Map: data-refresh-section value → section load function */
  var SECTION_REFRESH = {
    pages:    loadPages,
    audience: loadAudience,
    traffic:  loadTraffic,
    realtime: loadRealtime,
  };

  /* Build a refresh button wired to the given function */
  function _makeRefreshBtn(fn) {
    var btn = document.createElement('button');
    btn.className = 'vsr-refresh-btn';
    btn.title     = 'Refresh';
    btn.setAttribute('aria-label', 'Refresh');
    btn.innerHTML = REFRESH_SVG;
    btn.addEventListener('click', function (e) {
      e.stopPropagation();
      btn.classList.add('vsr-refreshing');
      setTimeout(function () { btn.classList.remove('vsr-refreshing'); }, 700);
      fn();
    });
    return btn;
  }

  /* Inject refresh buttons into every card head */
  function initCardRefreshButtons() {
    /* Overview ov-cards: append inside .ov-title-group */
    Object.keys(OV_CARD_REFRESH).forEach(function (cardId) {
      var card = document.getElementById(cardId);
      if (!card) return;
      var titleGroup = card.querySelector('.ov-title-group');
      if (!titleGroup) return;
      titleGroup.appendChild(_makeRefreshBtn(OV_CARD_REFRESH[cardId]));
    });

    /* Section cards: append to .card-header */
    shell.querySelectorAll('.card[data-refresh-section]').forEach(function (card) {
      var section = card.dataset.refreshSection;
      var fn      = SECTION_REFRESH[section];
      if (!fn) return;
      var header = card.querySelector('.card-header');
      if (!header) return;
      header.appendChild(_makeRefreshBtn(fn));
    });
  }

  /* Global refresh button in topbar */
  var vsrGlobalRefreshBtn = document.getElementById('vsrGlobalRefresh');
  if (vsrGlobalRefreshBtn) {
    vsrGlobalRefreshBtn.addEventListener('click', function () {
      vsrGlobalRefreshBtn.classList.add('vsr-refreshing');
      setTimeout(function () { vsrGlobalRefreshBtn.classList.remove('vsr-refreshing'); }, 900);
      loadSection(currentSection);
    });
  }

  /* ── INIT ────────────────────────────────────────────────── */
  loadOverview();
  initCardRefreshButtons();

  // Refresh online count every 30s
  setInterval(function () {
    api('summary').then(function (s) {
      setText('vsrOnlineCount', s.online);
      setText('vsrRtOnline',    s.online);
    });
  }, 30000);

})();
