(function () {
	'use strict';

	var cfg = window.LogisticaSessionGuard || {};
	var ajaxUrl = cfg.ajaxUrl || '';
	var loginUrl = cfg.loginUrl || '/';
	var expired = false;
	var checking = false;
	var lastCheck = 0;
	var CHECK_MIN_MS = 20000;
	var KEEP_ALIVE_MS = 5 * 60 * 1000;
	var originalFetch = window.fetch.bind(window);
	var modalEl = null;

	function isAjaxRequest(url) {
		var value = String(url || '');
		return value.indexOf('admin-ajax') !== -1 || value.indexOf('ajax.php') !== -1;
	}

	function requestUrl(input) {
		if (typeof input === 'string') {
			return input;
		}
		if (input && typeof input.url === 'string') {
			return input.url;
		}
		return '';
	}

	function payloadLooksExpired(status, payload) {
		if (status === 401) {
			return true;
		}

		var data = payload && payload.data ? payload.data : null;
		var code = data && data.code ? String(data.code) : '';
		if (code === 'session_expired') {
			return true;
		}

		var message = '';
		if (data && data.message) {
			message = String(data.message);
		} else if (payload && payload.message) {
			message = String(payload.message);
		}

		return status === 403 && /sesi[oó]n expirada/i.test(message);
	}

	function payloadLooksNonceInvalid(status, payload) {
		var data = payload && payload.data ? payload.data : null;
		var code = data && data.code ? String(data.code) : '';
		if (code === 'nonce_invalid') {
			return true;
		}

		var message = data && data.message ? String(data.message) : '';
		return status === 403 && /token de seguridad|recarga la p[aá]gina/i.test(message) && !payloadLooksExpired(status, payload);
	}

	function applyRefreshedNonces(nonces) {
		if (!nonces || typeof nonces !== 'object') {
			return;
		}

		if (window.LogisticaAdmin && nonces.user_panel) {
			window.LogisticaAdmin.userPanelNonce = nonces.user_panel;
		}
		if (window.LogisticaUserPanel && nonces.user_panel) {
			window.LogisticaUserPanel.userPanelNonce = nonces.user_panel;
		}

		try {
			document.dispatchEvent(
				new CustomEvent('logistica:session-refreshed', {
					detail: { nonces: nonces }
				})
			);
		} catch (error) {
			// Ignore CustomEvent issues in older environments.
		}
	}

	function ensureModal() {
		if (modalEl && document.body.contains(modalEl)) {
			return modalEl;
		}

		modalEl = document.createElement('div');
		modalEl.className = 'logistica-session-guard';
		modalEl.setAttribute('role', 'alertdialog');
		modalEl.setAttribute('aria-modal', 'true');
		modalEl.setAttribute('aria-labelledby', 'logistica-session-guard-title');
		modalEl.innerHTML =
			'<div class="logistica-session-guard__backdrop" aria-hidden="true"></div>' +
			'<div class="logistica-session-guard__dialog" data-session-guard-dialog>' +
				'<div class="logistica-session-guard__icon" aria-hidden="true">' +
					'<span class="logistica-session-guard__icon-ring"></span>' +
					'<span class="dashicons dashicons-lock" data-session-guard-icon></span>' +
				'</div>' +
				'<p class="logistica-session-guard__brand">JB Planeación</p>' +
				'<h2 id="logistica-session-guard-title" class="logistica-session-guard__title">Sesión expirada</h2>' +
				'<p class="logistica-session-guard__text" data-session-guard-text></p>' +
				'<div class="logistica-session-guard__actions">' +
					'<button type="button" class="logistica-session-guard__btn logistica-session-guard__btn--primary" data-session-guard-login>Iniciar sesión</button>' +
					'<button type="button" class="logistica-session-guard__btn logistica-session-guard__btn--secondary" data-session-guard-reload hidden>Recargar página</button>' +
				'</div>' +
			'</div>';

		document.body.appendChild(modalEl);

		var loginBtn = modalEl.querySelector('[data-session-guard-login]');
		var reloadBtn = modalEl.querySelector('[data-session-guard-reload]');
		if (loginBtn) {
			loginBtn.addEventListener('click', function () {
				window.location.href = loginUrl;
			});
		}
		if (reloadBtn) {
			reloadBtn.addEventListener('click', function () {
				window.location.reload();
			});
		}

		return modalEl;
	}

	function showModal(mode) {
		var modal = ensureModal();
		var text = modal.querySelector('[data-session-guard-text]');
		var loginBtn = modal.querySelector('[data-session-guard-login]');
		var reloadBtn = modal.querySelector('[data-session-guard-reload]');
		var title = modal.querySelector('#logistica-session-guard-title');
		var dialog = modal.querySelector('[data-session-guard-dialog]');
		var icon = modal.querySelector('[data-session-guard-icon]');

		modal.classList.remove('is-mode-session', 'is-mode-nonce');
		if (dialog) {
			dialog.classList.remove('is-session', 'is-nonce');
		}

		if (mode === 'nonce') {
			modal.classList.add('is-mode-nonce');
			if (dialog) {
				dialog.classList.add('is-nonce');
			}
			if (icon) {
				icon.className = 'dashicons dashicons-update';
			}
			if (title) {
				title.textContent = 'Es necesario recargar';
			}
			if (text) {
				text.textContent =
					'Tu sesión sigue activa, pero el token de seguridad de esta página ya no es válido. ' +
					'Recarga para continuar. Si tenías cambios sin guardar, cópialos antes.';
			}
			if (loginBtn) {
				loginBtn.hidden = true;
			}
			if (reloadBtn) {
				reloadBtn.hidden = false;
			}
		} else {
			modal.classList.add('is-mode-session');
			if (dialog) {
				dialog.classList.add('is-session');
			}
			if (icon) {
				icon.className = 'dashicons dashicons-lock';
			}
			if (title) {
				title.textContent = 'Sesión expirada';
			}
			if (text) {
				text.textContent =
					'Tu sesión se cerró. Los cambios que no se hayan guardado no se enviaron al servidor. ' +
					'Copia lo que necesites y vuelve a iniciar sesión.';
			}
			if (loginBtn) {
				loginBtn.hidden = false;
			}
			if (reloadBtn) {
				reloadBtn.hidden = true;
			}
		}

		modal.classList.add('is-visible');
		document.body.classList.add('logistica-session-guard-open');
	}

	function markExpired() {
		if (expired) {
			return;
		}
		expired = true;
		window.__logisticaSessionDead = true;
		showModal('session');
	}

	function markNonceInvalid() {
		if (expired) {
			return;
		}
		showModal('nonce');
	}

	function inspectAjaxResponse(response) {
		if (!response || !isAjaxRequest(response.url || '')) {
			return Promise.resolve(response);
		}

		return response
			.clone()
			.json()
			.then(function (payload) {
				if (payloadLooksExpired(response.status, payload)) {
					markExpired();
				} else if (payloadLooksNonceInvalid(response.status, payload)) {
					markNonceInvalid();
				}
				return response;
			})
			.catch(function () {
				if (response.status === 401) {
					markExpired();
				}
				return response;
			});
	}

	function checkSession(force) {
		if (expired || !ajaxUrl) {
			return Promise.resolve(!expired);
		}

		var now = Date.now();
		if (!force && (checking || now - lastCheck < CHECK_MIN_MS)) {
			return Promise.resolve(!expired);
		}

		checking = true;
		lastCheck = now;

		var body = new FormData();
		body.append('action', 'logistica_session_ping');

		return originalFetch(ajaxUrl, {
			method: 'POST',
			body: body,
			credentials: 'same-origin',
			headers: {
				'X-Requested-With': 'XMLHttpRequest'
			}
		})
			.then(function (response) {
				return response
					.json()
					.then(function (payload) {
						checking = false;
						if (payloadLooksExpired(response.status, payload) || !payload || payload.success !== true) {
							markExpired();
							return false;
						}
						if (payload.data && payload.data.nonces) {
							applyRefreshedNonces(payload.data.nonces);
						}
						return true;
					})
					.catch(function () {
						checking = false;
						if (response.status === 401) {
							markExpired();
							return false;
						}
						return !expired;
					});
			})
			.catch(function () {
				checking = false;
				return !expired;
			});
	}

	window.fetch = function (input, init) {
		var url = requestUrl(input);

		if (expired && isAjaxRequest(url)) {
			markExpired();
			return Promise.reject(new Error('Sesión expirada. Vuelve a iniciar sesión.'));
		}

		return originalFetch(input, init).then(function (response) {
			if (!isAjaxRequest(url) && !isAjaxRequest(response.url || '')) {
				return response;
			}
			return inspectAjaxResponse(response);
		});
	};

	document.addEventListener('visibilitychange', function () {
		if (document.visibilityState === 'visible') {
			checkSession(true);
		}
	});

	window.addEventListener('focus', function () {
		checkSession(false);
	});

	if (document.readyState === 'loading') {
		document.addEventListener('DOMContentLoaded', function () {
			checkSession(true);
		});
	} else {
		checkSession(true);
	}

	window.setInterval(function () {
		if (document.visibilityState === 'visible') {
			checkSession(true);
		}
	}, KEEP_ALIVE_MS);

	window.LogisticaSessionGuardApi = {
		check: checkSession,
		isExpired: function () {
			return expired;
		},
		markExpired: markExpired
	};
})();
