(function () {
    'use strict';

    const body = document.body;
    if (!body || body.getAttribute('data-session-timeout') !== 'enabled') return;

    const numberValue = function (name, fallback) {
        const value = Number(body.getAttribute(name));
        return Number.isFinite(value) && value > 0 ? value : fallback;
    };

    const config = {
        idleMs: numberValue('data-session-idle-seconds', 600) * 1000,
        warningMs: numberValue('data-session-warning-seconds', 60) * 1000,
        activityThrottleMs: numberValue('data-session-activity-throttle-seconds', 60) * 1000,
        sessionStartedAt: numberValue('data-session-started-at', Math.floor(Date.now() / 1000)),
        csrfToken: body.getAttribute('data-session-csrf') || '',
        activityUrl: body.getAttribute('data-session-activity-url') || 'session_activity.php',
        extendUrl: body.getAttribute('data-session-extend-url') || 'session_extend.php',
        logoutUrl: body.getAttribute('data-session-logout-url') || 'logout.php',
        loginUrl: body.getAttribute('data-session-login-url') || 'login.php',
        expiredLoginUrl: body.getAttribute('data-session-expired-login-url') || 'login.php?session=expired'
    };

    const modal = document.getElementById('sessionWarningModal');
    const dialog = modal ? modal.querySelector('.session-timeout-dialog') : null;
    const countdown = document.getElementById('sessionWarningRemainingSeconds');
    const extendButton = document.getElementById('sessionExtendButton');
    const logoutButton = document.getElementById('sessionLogoutButton');
    const status = document.getElementById('sessionTimeoutStatus');
    if (!modal || !dialog || !countdown || !extendButton || !logoutButton) return;

    const nativeFetch = window.fetch ? window.fetch.bind(window) : null;
    if (!nativeFetch) return;

    const sessionScope = String(config.sessionStartedAt);
    const storageCurrentSessionKey = 'sos-ticketing-session-current';
    const storageEventKey = 'sos-ticketing-session-event';
    const storageServerActivityKey = 'sos-ticketing-session-server-activity:' + sessionScope;
    const channelName = 'sos-ticketing-session';
    const broadcastChannel = 'BroadcastChannel' in window ? new BroadcastChannel(channelName) : null;

    let serverLastActivityAt = numberValue('data-session-last-activity-at', Math.floor(Date.now() / 1000)) * 1000;
    let lastObservedUserActivityAt = serverLastActivityAt;
    let lastSyncedObservedActivityAt = serverLastActivityAt;
    let lastActivityRequestAt = 0;
    let activityRequestInFlight = false;
    let activityRequestPending = false;
    let activitySyncFailed = false;
    let lastActivitySyncAttemptAt = 0;
    let activitySyncTimer = 0;
    let tickTimer = 0;
    let modalVisible = false;
    let terminating = false;
    let destroyed = false;
    let previousFocusedElement = null;

    function safeStorageGet(key) {
        try { return window.localStorage.getItem(key); } catch (error) { return null; }
    }

    function safeStorageSet(key, value) {
        try { window.localStorage.setItem(key, value); } catch (error) {}
    }

    function safeStorageRemove(key) {
        try { window.localStorage.removeItem(key); } catch (error) {}
    }

    function clearSessionSynchronizationState() {
        safeStorageRemove(storageServerActivityKey);
        if (safeStorageGet(storageCurrentSessionKey) === sessionScope) {
            safeStorageRemove(storageCurrentSessionKey);
        }
    }

    function initializeStorageScope() {
        const previousScope = safeStorageGet(storageCurrentSessionKey);
        if (previousScope && previousScope !== sessionScope) {
            safeStorageRemove('sos-ticketing-session-server-activity:' + previousScope);
        }
        safeStorageSet(storageCurrentSessionKey, sessionScope);

        const storedServerActivity = Number(safeStorageGet(storageServerActivityKey));
        if (Number.isFinite(storedServerActivity) && storedServerActivity > serverLastActivityAt) {
            serverLastActivityAt = storedServerActivity;
            lastObservedUserActivityAt = Math.max(lastObservedUserActivityAt, storedServerActivity);
        }
    }

    function publish(type, payload) {
        const message = {
            scope: sessionScope,
            type: type,
            payload: payload || {},
            createdAt: Date.now(),
            nonce: Math.random().toString(36).slice(2)
        };
        if (broadcastChannel) {
            try { broadcastChannel.postMessage(message); } catch (error) {}
        }
        safeStorageSet(storageEventKey, JSON.stringify(message));
    }

    function updateServerActivity(timestampMs, shouldPublish, messageType, observedAtMs) {
        if (!Number.isFinite(timestampMs) || timestampMs <= 0) return;
        serverLastActivityAt = Math.max(serverLastActivityAt, timestampMs);
        const acknowledgedObservedAt = Number(observedAtMs);
        if (Number.isFinite(acknowledgedObservedAt) && acknowledgedObservedAt > 0) {
            lastSyncedObservedActivityAt = Math.max(lastSyncedObservedActivityAt, acknowledgedObservedAt);
        }
        lastObservedUserActivityAt = Math.max(lastObservedUserActivityAt, timestampMs);
        safeStorageSet(storageServerActivityKey, String(serverLastActivityAt));
        if (modalVisible) hideWarning();
        if (shouldPublish) {
            publish(messageType || 'activity-synced', {
                at: serverLastActivityAt,
                observedAt: Number.isFinite(acknowledgedObservedAt) ? acknowledgedObservedAt : serverLastActivityAt
            });
        }
    }

    function setButtonsDisabled(disabled) {
        extendButton.disabled = disabled;
        logoutButton.disabled = disabled;
    }

    function setStatus(message) {
        if (status) status.textContent = message || '';
    }

    function showWarning(force) {
        if (modalVisible || (terminating && !force)) return;
        modalVisible = true;
        previousFocusedElement = document.activeElement;
        modal.hidden = false;
        modal.setAttribute('aria-hidden', 'false');
        body.classList.add('session-warning-open');
        setButtonsDisabled(false);
        setStatus('');
        updateCountdown();
        window.setTimeout(function () { extendButton.focus(); }, 0);
    }

    function hideWarning() {
        if (!modalVisible) return;
        modalVisible = false;
        modal.hidden = true;
        modal.setAttribute('aria-hidden', 'true');
        body.classList.remove('session-warning-open');
        setButtonsDisabled(false);
        setStatus('');
        if (previousFocusedElement && typeof previousFocusedElement.focus === 'function' && document.contains(previousFocusedElement)) {
            previousFocusedElement.focus();
        }
        previousFocusedElement = null;
    }

    function updateCountdown() {
        if (!modalVisible) return;
        const expiresAt = serverLastActivityAt + config.idleMs + config.warningMs;
        const remaining = Math.max(0, Math.ceil((expiresAt - Date.now()) / 1000));
        countdown.textContent = String(remaining);
    }

    function closeOpenInterfaceElements() {
        document.querySelectorAll('.user-dropdown.otvoren, .notif-dropdown.otvoren, .sidebar.otvoren').forEach(function (element) {
            element.classList.remove('otvoren');
        });
        document.querySelectorAll('[aria-expanded="true"]').forEach(function (element) {
            element.setAttribute('aria-expanded', 'false');
        });
    }

    function appendLogoutReason(url, reason) {
        try {
            const parsed = new URL(url, window.location.href);
            parsed.searchParams.set('reason', reason);
            return parsed.href;
        } catch (error) {
            return url + (url.indexOf('?') === -1 ? '?' : '&') + 'reason=' + encodeURIComponent(reason);
        }
    }

    function redirectThroughLogout(reason) {
        window.location.replace(appendLogoutReason(config.logoutUrl, reason));
    }

    function handleCrossTabTermination(reason) {
        if (terminating) return;
        terminateSession(reason === 'expired' ? 'expired' : 'manual', false);
    }

    function processMessage(message) {
        if (!message || message.scope !== sessionScope || !message.type) return;
        const payload = message.payload || {};

        if (message.type === 'activity') {
            const activityAt = Number(payload.at);
            if (Number.isFinite(activityAt)) {
                lastObservedUserActivityAt = Math.max(lastObservedUserActivityAt, activityAt);
                if (modalVisible) scheduleActivitySync(true);
            }
            return;
        }

        if (message.type === 'activity-synced' || message.type === 'extended') {
            updateServerActivity(Number(payload.at), false, '', Number(payload.observedAt));
            return;
        }

        if (message.type === 'logout') {
            handleCrossTabTermination(payload.reason || 'manual');
        }
    }

    if (broadcastChannel) {
        broadcastChannel.addEventListener('message', function (event) { processMessage(event.data); });
    }

    window.addEventListener('storage', function (event) {
        if (event.key === storageServerActivityKey && event.newValue) {
            updateServerActivity(Number(event.newValue), false);
            return;
        }
        if (event.key === storageEventKey && event.newValue) {
            try { processMessage(JSON.parse(event.newValue)); } catch (error) {}
        }
    });

    async function postSessionAction(url, extraData, csrfRetryAttempted) {
        const data = new URLSearchParams();
        data.set('csrf_token', config.csrfToken);
        data.set('ajax', '1');
        Object.keys(extraData || {}).forEach(function (key) { data.set(key, extraData[key]); });

        const response = await nativeFetch(url, {
            method: 'POST',
            credentials: 'same-origin',
            cache: 'no-store',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
                'X-Requested-With': 'XMLHttpRequest'
            },
            body: data.toString()
        });

        let payload = {};
        try { payload = await response.json(); } catch (error) {}

        if (response.status === 419 && payload.csrf_token && !csrfRetryAttempted) {
            config.csrfToken = String(payload.csrf_token);
            body.setAttribute('data-session-csrf', config.csrfToken);
            try {
                const refreshedLogoutUrl = new URL(config.logoutUrl, window.location.href);
                refreshedLogoutUrl.searchParams.set('csrf', config.csrfToken);
                config.logoutUrl = refreshedLogoutUrl.href;
                body.setAttribute('data-session-logout-url', config.logoutUrl);
            } catch (error) {}
            return postSessionAction(url, extraData, true);
        }

        return { response: response, payload: payload };
    }

    async function syncActivity() {
        if (terminating || destroyed) return;
        if (activityRequestInFlight) {
            activityRequestPending = true;
            return;
        }

        activityRequestInFlight = true;
        activityRequestPending = false;
        lastActivityRequestAt = Date.now();
        lastActivitySyncAttemptAt = lastActivityRequestAt;
        const observedActivitySnapshot = lastObservedUserActivityAt;

        try {
            const result = await postSessionAction(config.activityUrl);
            if (result.response.status === 401) {
                terminateSession('expired');
                return;
            }
            if (!result.response.ok || !result.payload.ok) {
                activitySyncFailed = true;
                return;
            }
            activitySyncFailed = false;
            updateServerActivity(
                Number(result.payload.last_activity_at) * 1000,
                true,
                'activity-synced',
                observedActivitySnapshot
            );
        } catch (error) {
            // Mrežna greška ne potvrđuje aktivnost. Klijentsko upozorenje se zato
            // zasniva na poslednjoj serverski potvrđenoj aktivnosti i ne odlaže se beskonačno.
            activitySyncFailed = true;
        } finally {
            activityRequestInFlight = false;
            if (activityRequestPending && !terminating) scheduleActivitySync(false);
        }
    }

    function scheduleActivitySync(force) {
        if (terminating || destroyed) return;
        window.clearTimeout(activitySyncTimer);
        const elapsed = Date.now() - lastActivityRequestAt;
        const delay = force || elapsed >= config.activityThrottleMs ? 0 : config.activityThrottleMs - elapsed;
        activitySyncTimer = window.setTimeout(syncActivity, delay);
    }

    function registerUserActivity(forceServerSync) {
        if (terminating || modalVisible || destroyed) return;
        const now = Date.now();
        lastObservedUserActivityAt = now;
        publish('activity', { at: now });
        scheduleActivitySync(Boolean(forceServerSync));
    }

    async function extendSession() {
        if (terminating) return;
        setButtonsDisabled(true);
        setStatus('Provera i produženje sesije…');

        try {
            const result = await postSessionAction(config.extendUrl);
            if (result.response.status === 401 || !result.response.ok || !result.payload.ok) {
                terminateSession('expired');
                return;
            }
            const confirmedAt = Number(result.payload.last_activity_at) * 1000;
            lastObservedUserActivityAt = confirmedAt;
            lastSyncedObservedActivityAt = confirmedAt;
            updateServerActivity(confirmedAt, true, 'extended', confirmedAt);
            lastActivityRequestAt = Date.now();
        } catch (error) {
            setButtonsDisabled(false);
            setStatus('Sesiju trenutno nije moguće proveriti. Pokušajte ponovo ili izaberite odjavu.');
        }
    }

    async function terminateSession(reason, shouldPublish) {
        if (terminating) return;
        terminating = true;
        window.clearTimeout(activitySyncTimer);
        closeOpenInterfaceElements();
        if (!modalVisible) showWarning(true);
        setButtonsDisabled(true);
        setStatus(reason === 'expired' ? 'Sesija je istekla. Odjavljivanje…' : 'Odjavljivanje…');
        if (shouldPublish !== false) publish('logout', { reason: reason });
        clearSessionSynchronizationState();

        try {
            const result = await postSessionAction(config.logoutUrl, { reason: reason });
            const redirectUrl = result.payload && result.payload.redirect_url
                ? result.payload.redirect_url
                : (reason === 'expired' ? config.expiredLoginUrl : config.loginUrl);
            window.location.replace(redirectUrl);
        } catch (error) {
            redirectThroughLogout(reason === 'expired' ? 'expired' : 'manual');
        }
    }

    function evaluateTimeout() {
        if (terminating || destroyed) return;
        const now = Date.now();
        const warningAt = serverLastActivityAt + config.idleMs;
        const expiresAt = warningAt + config.warningMs;

        if (now >= expiresAt) {
            terminateSession('expired');
            return;
        }

        if (now >= warningAt) {
            // Ako postoji lokalna ili cross-tab aktivnost koja još nije potvrđena serveru,
            // potvrđuje se pre prikaza upozorenja. Time background zahtevi ne produžavaju sesiju.
            if (lastObservedUserActivityAt > lastSyncedObservedActivityAt) {
                // Dozvoli jednu kratku serversku proveru, ali nikada ne odlaži
                // upozorenje beskonačno ako endpoint nije dostupan ili zahtev ne uspe.
                const retryDue = !activityRequestInFlight && (now - lastActivitySyncAttemptAt >= 5000);
                if (!activitySyncFailed) {
                    if (retryDue) scheduleActivitySync(true);
                    return;
                }
                if (retryDue) scheduleActivitySync(true);
            }
            showWarning();
            updateCountdown();
            return;
        }

        if (modalVisible) hideWarning();
    }

    function trapModalKeyboard(event) {
        if (!modalVisible) return;
        if (event.key === 'Escape') {
            event.preventDefault();
            event.stopImmediatePropagation();
            return;
        }
        if (event.key !== 'Tab') return;

        const focusable = [extendButton, logoutButton].filter(function (element) { return !element.disabled; });
        if (!focusable.length) {
            event.preventDefault();
            dialog.focus();
            return;
        }
        const first = focusable[0];
        const last = focusable[focusable.length - 1];
        if (event.shiftKey && document.activeElement === first) {
            event.preventDefault();
            last.focus();
        } else if (!event.shiftKey && document.activeElement === last) {
            event.preventDefault();
            first.focus();
        }
    }

    function isMeaningfulKey(event) {
        return !['Shift', 'Control', 'Alt', 'Meta', 'CapsLock', 'NumLock', 'ScrollLock'].includes(event.key);
    }

    document.addEventListener('pointerdown', function () { registerUserActivity(false); }, true);
    document.addEventListener('keydown', function (event) {
        trapModalKeyboard(event);
        if (!modalVisible && isMeaningfulKey(event)) registerUserActivity(false);
    }, true);
    document.addEventListener('input', function () { registerUserActivity(false); }, true);
    document.addEventListener('change', function () { registerUserActivity(false); }, true);
    document.addEventListener('submit', function () { registerUserActivity(true); }, true);

    extendButton.addEventListener('click', extendSession);
    logoutButton.addEventListener('click', function () { terminateSession('manual'); });

    document.addEventListener('click', function (event) {
        const link = event.target.closest ? event.target.closest('a[href]') : null;
        if (!link || terminating) return;
        try {
            const target = new URL(link.href, window.location.href);
            if (target.pathname.endsWith('/logout.php') || target.pathname === '/logout.php') {
                publish('logout', { reason: target.searchParams.get('reason') || 'manual' });
                clearSessionSynchronizationState();
            }
        } catch (error) {}
    }, true);

    document.addEventListener('visibilitychange', evaluateTimeout);
    window.addEventListener('focus', evaluateTimeout);
    window.addEventListener('pageshow', evaluateTimeout);

    window.fetch = function () {
        const args = arguments;
        return nativeFetch.apply(null, args).then(function (response) {
            if (response.status === 401 && !terminating) {
                terminateSession('expired');
            }
            return response;
        });
    };

    function destroy() {
        destroyed = true;
        window.clearInterval(tickTimer);
        window.clearTimeout(activitySyncTimer);
        if (broadcastChannel) broadcastChannel.close();
    }

    window.addEventListener('pagehide', destroy, { once: true });

    initializeStorageScope();
    body.setAttribute('data-session-timeout-initialized', 'true');
    tickTimer = window.setInterval(evaluateTimeout, 250);
    // Server je već evidentirao klasičnu navigaciju u bootstrap.php. Dodatni
    // keep-alive pri svakom učitavanju nije potreban i može prikriti deployment grešku.
    evaluateTimeout();
})();
