// WHP session keep-alive + CSRF token refresh.
//
// Pings /api/keepalive.php periodically so the server-side PHP session file
// doesn't get garbage-collected while the page is open. If the keepalive
// returns a new csrf_token (which it always does — it's the current one in
// the session), we update window.WHP_CSRF_TOKEN so any subsequent AJAX
// pulls the fresh value.
//
// Pages that previously read `<?php echo get_csrf_token(); ?>` into a local
// `nonce` const should switch to reading `window.WHP_CSRF_TOKEN` (which is
// initialized to the page-render token but kept current by this script).
// Until they migrate, just having a recent token in the session prevents
// the GC scenario from biting most flows: the cached token stays valid as
// long as the session file survives.
//
// Cadence: 4 minutes — comfortably under the 8h panel session lifetime but
// frequent enough that a tab idle for ~5 min remains warm. Skips when the
// document is hidden (background tab) to avoid burning server resources on
// abandoned panels.
//
// Surfaced 2026-06-01 — a long-running alsacorp/alsaeurope WP import session
// hit a CSRF rejection because the page had been open longer than the
// then-default 24-minute gc_maxlifetime.

(function () {
    'use strict';

    // Seed from the page-render token if the page exposed one (any of the
    // common variable names used by panel pages today). The keepalive will
    // bring this current as it runs.
    if (typeof window.WHP_CSRF_TOKEN === 'undefined') {
        if (typeof window.nonce !== 'undefined' && typeof window.nonce === 'string') {
            window.WHP_CSRF_TOKEN = window.nonce;
        } else if (typeof window.csrfToken !== 'undefined' && typeof window.csrfToken === 'string') {
            window.WHP_CSRF_TOKEN = window.csrfToken;
        } else {
            window.WHP_CSRF_TOKEN = '';
        }
    }

    const KEEPALIVE_INTERVAL_MS = 4 * 60 * 1000;  // 4 minutes
    const ENDPOINT = '/api/keepalive.php';

    let lastPingTs = 0;

    async function ping() {
        if (document.hidden) return;  // skip backgrounded tabs
        const now = Date.now();
        // Defensive throttle: if some external code calls ping() directly,
        // don't spam the server faster than once per interval.
        if (now - lastPingTs < KEEPALIVE_INTERVAL_MS - 5000) return;
        lastPingTs = now;

        try {
            const resp = await fetch(ENDPOINT, {
                method: 'GET',
                credentials: 'same-origin',
                cache: 'no-store',
                headers: { 'Accept': 'application/json' },
            });

            if (resp.status === 401) {
                // Session is genuinely dead. Most graceful: do nothing here;
                // next user-initiated action will hit auto-prepend's auth
                // gate and redirect to /login.php. Showing a banner from a
                // background keepalive would be noisy on idle tabs.
                return;
            }

            if (!resp.ok) return;

            const data = await resp.json();
            if (data && data.ok && data.csrf_token) {
                window.WHP_CSRF_TOKEN = data.csrf_token;
                // Best-effort: also update common legacy names if the page
                // declared them with `let` (re-assignable). `const` will
                // stay stale; those pages should migrate to WHP_CSRF_TOKEN.
                if (typeof window.nonce === 'string') {
                    try { window.nonce = data.csrf_token; } catch (e) { /* const-bound; ignore */ }
                }
            }
        } catch (e) {
            // Network blip or page tear-down. No-op; next interval retries.
        }
    }

    // Kick off the interval. Don't ping immediately on load — the page just
    // rendered with a fresh token; first ping after one interval is fine.
    setInterval(ping, KEEPALIVE_INTERVAL_MS);

    // Also ping on visibility change (tab regains focus after >1 min idle)
    // to refresh the cached token before the user submits something.
    let lastHidden = 0;
    document.addEventListener('visibilitychange', () => {
        if (document.hidden) {
            lastHidden = Date.now();
        } else if (lastHidden > 0 && (Date.now() - lastHidden) > 60000) {
            // Reset throttle so this opportunistic ping isn't blocked.
            lastPingTs = 0;
            ping();
        }
    });
})();
