// Cache-warming prefetch for MainWP (no DOM swap)
// Targets only safe links.
// Opt-in: .mainwp-js-prefetch or [mainwp-data-warm]; Opt-out: [mainwp-data-no-warm]
(function ($) {
    // -------------------- Config --------------------
    const HOVER_DELAY_MS = 200;          // delay before hover prefetch
    const REWARM_TTL_MS = 5 * 60 * 1000;// re-warm same URL after 5 min
    const MAX_INFLIGHT = 3;            // throttle concurrent warms
    const MAX_BYTES = 1.5 * 1024 * 1024; // 1.5 MB content size limit

    // -------------------- State ---------------------
    const SEEN = new Map(); // url -> timestamp last warmed
    let inflight = 0;

    // -------------------- Helpers -------------------
    function canPrefetch() {
        const c = navigator.connection;
        if (c) {
            if (c.saveData) return false;
            if (['slow-2g', '2g'].includes(c.effectiveType)) return false;
        }
        return location.protocol.startsWith('http');
    }

    function isWarmableLink(a) {
        if (!a) return false;
        if (a.hasAttribute('download') || a.target === '_blank') return false;
        if (a.hasAttribute('mainwp-data-no-warm')) return false;

        const href = a.getAttribute('href');
        if (!href) return false;

        // Only warm explicitly opt-in links OR MainWP nav links
        const explicitlyOpted = a.classList.contains('mainwp-js-prefetch') || a.hasAttribute('mainwp-data-warm');
        const url = new URL(href, location.href);

        if (url.origin !== location.origin) return false;      // same-origin only
        if (url.hash && url.pathname === location.pathname) return false; // same-page anchor
        if (!explicitlyOpted) return false; // limit scope to MainWP

        // De-dupe within TTL
        const seenAt = SEEN.get(url.toString());
        if (seenAt && (Date.now() - seenAt) < REWARM_TTL_MS) return false;

        return true;
    }

    function markSeen(urlStr) {
        SEEN.set(urlStr, Date.now());
        // Clean out old entries occasionally
        if (SEEN.size > 200) {
            const now = Date.now();
            for (const [u, ts] of SEEN) if (now - ts > REWARM_TTL_MS) SEEN.delete(u);
        }
    }

    function prefetchViaLink(urlStr) {
        const l = document.createElement('link');
        l.rel = 'prefetch';
        l.href = urlStr;
        l.as = 'document';
        document.head.appendChild(l);
    }

    async function headOKAndSmall(urlStr) {
        // Try HEAD first to check Content-Length; if missing, allow.
        try {
            const res = await fetch(urlStr, {
                method: 'HEAD',
                credentials: 'same-origin',
                mode: 'same-origin',
                cache: 'default'
            });
            if (!res.ok) return false;
            const len = res.headers.get('content-length');
            if (!len) return true; // no info -> allow
            return Number.parseInt(len, 10) <= MAX_BYTES;
        } catch {
            // HEAD may be blocked or not supported; allow rather than fail closed.
            return true;
        }
    }

    async function warm(urlStr) {
        if (inflight >= MAX_INFLIGHT) return;
        inflight++;
        try {
            // 1) Let the browser decide (best-effort)
            prefetchViaLink(urlStr);

            // 2) Skip very large pages
            const ok = await headOKAndSmall(urlStr);
            if (!ok) { markSeen(urlStr); return; }

            // 3) Fetch to warm the HTTP cache & connection with auth cookies
            await fetch(urlStr, {
                method: 'GET',
                credentials: 'same-origin',
                mode: 'same-origin',
                cache: 'default'
            }).catch(() => { /* ignore */ });
            console.log('Warm cached url: ' + urlStr);
            markSeen(urlStr);
        } finally {
            inflight--;
        }
    }

    function scheduleHoverWarm(a) {
        const href = a.getAttribute('href');
        if (!href) return;
        const urlStr = new URL(href, location.href).toString();
        const t = setTimeout(() => warm(urlStr), HOVER_DELAY_MS);
        const cancel = () => clearTimeout(t);
        a.addEventListener('pointerleave', cancel, { once: true });
        a.addEventListener('focusout', cancel, { once: true });
    }

    function setupViewportWarm(root) {
        if (!('IntersectionObserver' in globalThis)) return;
        const io = new IntersectionObserver((entries) => {
            entries.forEach((e) => {
                if (!e.isIntersecting) return;
                const a = e.target;
                if (!isWarmableLink(a)) return;
                const urlStr = new URL(a.getAttribute('href'), location.href).toString();
                io.unobserve(a);
                warm(urlStr);
            });
        }, { root: null, rootMargin: '200px', threshold: 0 });

        root.querySelectorAll('a.mainwp-js-prefetch, a[mainwp-data-warm]')
            .forEach((a) => { if (isWarmableLink(a)) io.observe(a); });
    }

    // -------------------- Boot ----------------------
    $(function () {
        if (!canPrefetch()) return;

        // Intent-based warm (hover/focus)
        document.addEventListener('pointerenter', (e) => {
            const target = e.target;
            if (!(target instanceof Element)) return; // skip if not an element
            const a = target.closest('a');
            if (!a || !isWarmableLink(a)) return;
            scheduleHoverWarm(a);
        }, true);
        document.addEventListener('focusin', (e) => {
            const a = e.target.closest('a');
            if (!isWarmableLink(a)) return;
            scheduleHoverWarm(a);
        }, true);

        // Touch warm
        document.addEventListener('touchstart', (e) => {
            const a = e.target.closest('a');
            if (!isWarmableLink(a)) return;
            const urlStr = new URL(a.getAttribute('href'), location.href).toString();
            warm(urlStr);
        }, { passive: true });

        // Viewport warm for persistent nav (e.g., sidebar)
        setupViewportWarm(document);
    });
})(jQuery);




