/**
 * modules/ui/loginUI.js — Pantalla de acceso, registro y recuperación.
 */
(function (g) {
    g.modules = g.modules || {};
    g.modules.ui = g.modules.ui || {};

    var dom = g.modules.core.dom;
    var messages = g.modules.ui.messages;
    var sess = g.modules.core.authSession;

    var overlay = null;
    var adminFab = null;
    var profileFab = null;

    async function uploadLoginMedia(file) {
        var formData = new FormData();
        formData.append('file', file);
        var base = typeof BASE_URL !== 'undefined' ? BASE_URL : '';
        var res = await fetch(base + '?action=uploadMedia', { method: 'POST', body: formData, credentials: 'same-origin' });
        var result = await res.json();
        if (result && result.success && result.url) return result.url;
        throw new Error('upload failed');
    }

    function syncLoginBackdrop(cfg) {
        try {
            if (!overlay || !g.modules.ui.loginBackground) return;
            var bgRoot = overlay.querySelector('#grid-auth-bg-root');
            var clock = overlay.querySelector('#grid-auth-clock');
            if (!bgRoot) return;
            g.modules.ui.loginBackground.mount(bgRoot, clock);
            g.modules.ui.loginBackground.apply(cfg || {});
        } catch (_) {}
    }

    function ensureOverlay() {
        if (overlay) return overlay;
        overlay = dom.el('div', { id: 'grid-auth-overlay', className: 'grid-auth-overlay', 'aria-hidden': 'true' });
        overlay.innerHTML =
            '<div id="grid-auth-bg-root" class="grid-auth-bg-root" aria-hidden="true"></div>' +
            '<div class="grid-auth-os-shell">' +
            '<div class="grid-auth-os-top">' +
            '<div id="grid-auth-clock" class="grid-auth-clock" aria-live="polite"></div>' +
            '</div>' +
            '<div class="grid-auth-os-spacer" aria-hidden="true"></div>' +
            '<div class="grid-auth-card-wrap">' +
            '<div class="grid-auth-card">' +
            '<div class="grid-auth-brand">' +
            messages.escape(messages.text('gateBrandTitle')) +
            '</div>' +
            '<div id="grid-auth-body"></div>' +
            '</div>' +
            '</div>' +
            '</div>';
        document.body.appendChild(overlay);
        return overlay;
    }

    function setBusy(b) {
        var el = ensureOverlay();
        el.classList.toggle('auth-loading', !!b);
    }

    function hideGate() {
        if (!overlay) return;
        try {
            if (g.modules.ui.loginBackground) g.modules.ui.loginBackground.teardown();
        } catch (_) {}
        overlay.style.display = 'none';
        overlay.setAttribute('aria-hidden', 'true');
        document.body.classList.remove('grid-auth-locked');
        try {
            var su = sess.getUser();
            if (adminFab) adminFab.style.display = su && su.role === 'admin' ? 'flex' : 'none';
            if (profileFab) profileFab.style.display = su ? 'flex' : 'none';
        } catch (_) {}
    }

    function showGate() {
        var el = ensureOverlay();
        el.style.display = 'flex';
        el.setAttribute('aria-hidden', 'false');
        document.body.classList.add('grid-auth-locked');
        if (adminFab) adminFab.style.display = 'none';
        if (profileFab) profileFab.style.display = 'none';
    }

    async function renderLogin() {
        var cfg = await g.modules.config.globalConfig.load();
        ensureOverlay();
        syncLoginBackdrop(cfg);
        showGate();
        var host = ensureOverlay().querySelector('#grid-auth-body');
        var canRegister = cfg.publicSignup !== false;

        host.innerHTML =
            '<div class="auth-tabs" role="tablist">' +
            '<button type="button" class="auth-tab auth-tab-active" data-tab="in" role="tab" aria-selected="true">' +
            messages.escape(messages.text('tabLogin')) +
            '</button>' +
            (canRegister ? '<button type="button" class="auth-tab" data-tab="reg" role="tab" aria-selected="false">' +
                messages.escape(messages.text('tabRegister')) +
                '</button>' : '') +
            '<button type="button" class="auth-tab" data-tab="rec" role="tab" aria-selected="false">' +
            messages.escape(messages.text('tabRecover')) +
            '</button>' +
            '</div>' +
            '<div id="auth-tab-in" class="auth-tab-panel">' +
            '<label class="auth-label">' +
            messages.escape(messages.text('identifier')) +
            '</label>' +
            '<input type="text" id="authIdent" class="auth-input" autocomplete="username" />' +
            '<label class="auth-label">' +
            messages.escape(messages.text('password')) +
            '</label>' +
            '<input type="password" id="authPass" class="auth-input" autocomplete="current-password" />' +
            '<label class="auth-label">' + messages.escape(messages.text('methodLabel')) + '</label>' +
            '<select id="authMethod" class="auth-input" aria-label="' + messages.escape(messages.text('methodLabel')) + '">' +
            '<option value="password">' + messages.escape(messages.text('methodPassword')) + '</option>' +
            '<option value="pin">' + messages.escape(messages.text('methodPin')) + '</option>' +
            '<option value="pattern">' + messages.escape(messages.text('methodPattern')) + '</option>' +
            '<option value="webauthn">' + messages.escape(messages.text('methodWebauthn')) + '</option>' +
            '</select>' +
            '<div id="authAltFields"></div>' +
            '<div class="auth-modal-actions">' +
            '<button type="button" id="authDoLogin" class="auth-btn auth-btn-primary">' +
            messages.escape(messages.text('loginBtn')) +
            '</button>' +
            '</div>' +
            '<p class="auth-foot"><button type="button" class="auth-link" id="authForgot">' +
            messages.escape(messages.text('forgot')) +
            '</button></p>' +
            '</div>' +
            '<div id="auth-tab-reg" class="auth-tab-panel" style="display:none"></div>' +
            '<div id="auth-tab-rec" class="auth-tab-panel" style="display:none">' +
            '<label class="auth-label">' +
            messages.escape(messages.text('email')) +
            '</label>' +
            '<input type="email" id="recEmail" class="auth-input" />' +
            '<label class="auth-label">' +
            messages.escape(messages.text('recoveryCode')) +
            '</label>' +
            '<input type="text" id="recCode" class="auth-input" autocomplete="off" />' +
            '<label class="auth-label">' +
            messages.escape(messages.text('newPassword')) +
            '</label>' +
            '<input type="password" id="recNewPw" class="auth-input" />' +
            '<div class="auth-modal-actions">' +
            '<button type="button" id="authDoRec" class="auth-btn auth-btn-primary">' +
            messages.escape(messages.text('recoverBtn')) +
            '</button>' +
            '</div>' +
            '</div>';

        var det = g.modules.hardware.deviceDetector;
        var methodSel = host.querySelector('#authMethod');
        var alt = host.querySelector('#authAltFields');

        async function refreshMethod() {
            var m = methodSel.value;
            alt.innerHTML = '';
            var showBio = await det.showBiometricOption();
            if (m === 'webauthn' && !showBio) {
                var wo = methodSel.querySelector('option[value="webauthn"]');
                if (wo) wo.remove();
                methodSel.value = 'password';
                return refreshMethod();
            }
            if (m === 'pin') {
                alt.innerHTML =
                    '<label class="auth-label">' +
                    messages.escape(messages.text('pin')) +
                    '</label>' +
                    '<input type="password" id="authPin" class="auth-input" inputmode="numeric" />';
            } else if (m === 'pattern') {
                alt.innerHTML =
                    '<p class="auth-hint">' + messages.escape(messages.text('patternHintLogin')) + '</p>' +
                    '<input type="text" id="authPat" class="auth-input" inputmode="numeric" />';
            }
        }

        methodSel.addEventListener('change', refreshMethod);
        det.showBiometricOption().then(function (showBio) {
            if (!showBio) {
                var wo = methodSel.querySelector('option[value="webauthn"]');
                if (wo) wo.remove();
            }
            refreshMethod();
        });

        host.querySelector('#authDoLogin').addEventListener('click', async function () {
            var identifier = host.querySelector('#authIdent').value.trim();
            var method = methodSel.value;
            setBusy(true);
            try {
                var secrets = { password: host.querySelector('#authPass').value };
                if (method === 'pin') {
                    var pi = host.querySelector('#authPin');
                    secrets.pin = pi ? pi.value : '';
                }
                if (method === 'pattern') {
                    var pt = host.querySelector('#authPat');
                    secrets.patternSeq = pt ? pt.value : '';
                }
                var r = await g.modules.auth.login.attempt({ identifier: identifier, method: method, password: secrets.password, pin: secrets.pin, patternSeq: secrets.patternSeq });
                if (!r.ok) {
                    messages.toast(messages.text('errBadCredentials'));
                    return;
                }
                sess.setUser(r.user);
                hideGate();
                g.eventBus.emit('auth:login', { userId: r.user.id });
            } finally {
                setBusy(false);
            }
        });

        host.querySelector('#authForgot').addEventListener('click', function () {
            host.querySelectorAll('.auth-tab').forEach(function (b) {
                var on = b.getAttribute('data-tab') === 'rec';
                b.classList.toggle('auth-tab-active', on);
                b.setAttribute('aria-selected', on ? 'true' : 'false');
            });
            host.querySelector('#auth-tab-in').style.display = 'none';
            host.querySelector('#auth-tab-rec').style.display = 'block';
            if (host.querySelector('#auth-tab-reg')) host.querySelector('#auth-tab-reg').style.display = 'none';
        });

        host.querySelectorAll('.auth-tab').forEach(function (btn) {
            btn.addEventListener('click', function () {
                var t = btn.getAttribute('data-tab');
                host.querySelectorAll('.auth-tab').forEach(function (b) {
                    b.classList.toggle('auth-tab-active', b === btn);
                    b.setAttribute('aria-selected', b === btn ? 'true' : 'false');
                });
                host.querySelector('#auth-tab-in').style.display = t === 'in' ? 'block' : 'none';
                var reg = host.querySelector('#auth-tab-reg');
                if (reg) reg.style.display = t === 'reg' ? 'block' : 'none';
                host.querySelector('#auth-tab-rec').style.display = t === 'rec' ? 'block' : 'none';
            });
        });

        if (canRegister && host.querySelector('#auth-tab-reg')) {
            g.modules.ui.registerWizard.run(host.querySelector('#auth-tab-reg'), async function (state) {
                setBusy(true);
                try {
                    var user = await g.modules.users.userCRUD.create({
                        email: state.email,
                        phone: state.phone,
                        password: state.password,
                        pin: state.pin,
                        patternSeq: state.patternSeq,
                        recoveryCode: state.recoveryCode,
                        loginType: state.wantBio ? 'webauthn' : 'password',
                    });
                    if (state.wantBio) {
                        try {
                            var cred = await g.modules.auth.biometricLogin.register(user);
                            await g.modules.users.changeCredentials.addWebAuthnCredential(user.id, cred);
                            await g.modules.auth.loginTypeSelector.setPreferred(user.id, 'webauthn');
                        } catch (_) {
                            messages.toast(messages.text('useAltMethod'));
                        }
                    }
                    sess.setUser(user);
                    hideGate();
                    messages.toast('Cuenta creada.');
                    g.eventBus.emit('auth:login', { userId: user.id });
                } catch (e) {
                    messages.alert(messages.text('errGeneric'));
                } finally {
                    setBusy(false);
                }
            });
        }

        host.querySelector('#authDoRec').addEventListener('click', async function () {
            var email = host.querySelector('#recEmail').value.trim();
            var code = host.querySelector('#recCode').value.trim();
            var np = host.querySelector('#recNewPw').value;
            setBusy(true);
            try {
                var u = await g.modules.users.userStore.findByEmail(email);
                if (!u) {
                    messages.toast(messages.text('errNotFound'));
                    return;
                }
                var ok = await g.modules.users.changeCredentials.verifyRecoveryCode(u.id, code);
                if (!ok) {
                    messages.toast(messages.text('errBadCredentials'));
                    return;
                }
                if (!np || np.length < 6) {
                    messages.toast(messages.text('errWeakPassword'));
                    return;
                }
                await g.modules.users.changeCredentials.changePassword(u.id, np);
                messages.toast('Contraseña actualizada. Ya puedes entrar.');
                host.querySelector('.auth-tab[data-tab="in"]').click();
            } finally {
                setBusy(false);
            }
        });
    }

    function openAdminModal() {
        var backdrop = dom.el('div', { className: 'auth-modal-backdrop' });
        var box = dom.el('div', { className: 'auth-modal auth-modal--wide' });
        box.innerHTML =
            '<h3 class="auth-modal-title">' +
            messages.escape(messages.text('adminPanel')) +
            '</h3>' +
            '<label class="auth-check"><input type="checkbox" id="admReq" /> ' +
            messages.escape(messages.text('requireLoginLabel')) +
            '</label>' +
            '<label class="auth-check"><input type="checkbox" id="admPub" /> ' +
            messages.escape(messages.text('publicSignupLabel')) +
            '</label>' +
            '<fieldset class="auth-fieldset-login">' +
            '<legend>Pantalla de inicio de sesión</legend>' +
            '<p class="auth-muted">Estilo bloqueo de sistema: vídeo o imágenes (subidas a <code>media/</code>).</p>' +
            '<label class="auth-label">Tipo de fondo</label>' +
            '<select id="admLoginBgType" class="auth-input">' +
            '<option value="default">Predeterminado</option>' +
            '<option value="video">Vídeo</option>' +
            '<option value="images">Galería de imágenes</option>' +
            '</select>' +
            '<div id="admLoginVideoBlock" style="display:none">' +
            '<label class="auth-label">URL o ruta del vídeo</label>' +
            '<input type="text" id="admLoginVideoUrl" class="auth-input" placeholder="https://… o media/archivo.mp4" />' +
            '<label class="auth-label">Subir vídeo</label>' +
            '<input type="file" id="admLoginVideoFile" class="auth-input" accept="video/mp4,video/webm,video/ogg,video/quicktime,video/*" />' +
            '</div>' +
            '<div id="admLoginImagesBlock" style="display:none">' +
            '<label class="auth-label">URLs o rutas (una por línea)</label>' +
            '<textarea id="admLoginImageUrls" class="auth-textarea" rows="4" placeholder="media/imagen1.jpg"></textarea>' +
            '<label class="auth-label">Subir imágenes (se añaden a la lista)</label>' +
            '<input type="file" id="admLoginImagesFiles" class="auth-input" accept="image/*" multiple />' +
            '<label class="auth-label">Modo de galería</label>' +
            '<select id="admLoginSlideshow" class="auth-input">' +
            '<option value="sequential">Secuencial</option>' +
            '<option value="random">Aleatorio</option>' +
            '<option value="fixed">Una imagen fija</option>' +
            '</select>' +
            '<div id="admLoginFixedWrap">' +
            '<label class="auth-label">Índice de imagen fija (0 = primera)</label>' +
            '<input type="number" id="admLoginFixedIdx" class="auth-input" min="0" value="0" />' +
            '</div>' +
            '<div id="admLoginIntervalWrap">' +
            '<label class="auth-label">Intervalo entre imágenes (segundos)</label>' +
            '<input type="number" id="admLoginInterval" class="auth-input" min="5" max="600" value="18" />' +
            '</div>' +
            '</div>' +
            '</fieldset>' +
            '<div class="auth-modal-actions">' +
            '<button type="button" class="auth-btn auth-btn-secondary" id="admClose">' +
            messages.escape(messages.text('cancel')) +
            '</button>' +
            '<button type="button" class="auth-btn auth-btn-primary" id="admSave">' +
            messages.escape(messages.text('save')) +
            '</button>' +
            '</div>';
        backdrop.appendChild(box);
        document.body.appendChild(backdrop);

        function refreshLoginAdminUi() {
            var t = box.querySelector('#admLoginBgType').value;
            box.querySelector('#admLoginVideoBlock').style.display = t === 'video' ? 'block' : 'none';
            box.querySelector('#admLoginImagesBlock').style.display = t === 'images' ? 'block' : 'none';
            var sm = box.querySelector('#admLoginSlideshow').value;
            var imgMode = t === 'images';
            box.querySelector('#admLoginFixedWrap').style.display = imgMode && sm === 'fixed' ? 'block' : 'none';
            box.querySelector('#admLoginIntervalWrap').style.display = imgMode && sm !== 'fixed' ? 'block' : 'none';
        }

        g.modules.config.globalConfig.load().then(function (s) {
            box.querySelector('#admReq').checked = !!s.requireLogin;
            box.querySelector('#admPub').checked = s.publicSignup !== false;
            var ls = g.modules.ui.loginBackground.mergeLoginScreen(s);
            box.querySelector('#admLoginBgType').value = ls.bgType || 'default';
            box.querySelector('#admLoginVideoUrl').value = ls.videoUrl || '';
            box.querySelector('#admLoginImageUrls').value = (ls.imageUrls || []).join('\n');
            box.querySelector('#admLoginSlideshow').value = ls.slideshowMode || 'sequential';
            box.querySelector('#admLoginFixedIdx').value = String(ls.fixedImageIndex != null ? ls.fixedImageIndex : 0);
            box.querySelector('#admLoginInterval').value = String(ls.slideshowIntervalSec || 18);
            refreshLoginAdminUi();
        });

        box.querySelector('#admLoginBgType').addEventListener('change', refreshLoginAdminUi);
        box.querySelector('#admLoginSlideshow').addEventListener('change', refreshLoginAdminUi);

        function close() {
            if (backdrop.parentNode) backdrop.parentNode.removeChild(backdrop);
        }

        box.querySelector('#admClose').addEventListener('click', close);
        backdrop.addEventListener('click', function (e) {
            if (e.target === backdrop) close();
        });
        box.querySelector('#admSave').addEventListener('click', async function () {
            try {
                var vf = box.querySelector('#admLoginVideoFile').files && box.querySelector('#admLoginVideoFile').files[0];
                if (vf) {
                    var vu = await uploadLoginMedia(vf);
                    box.querySelector('#admLoginVideoUrl').value = vu;
                }
                var mfs = box.querySelector('#admLoginImagesFiles').files;
                if (mfs && mfs.length) {
                    var urls = box.querySelector('#admLoginImageUrls').value.split(/\r?\n/).map(function (x) {
                        return x.trim();
                    }).filter(Boolean);
                    for (var i = 0; i < mfs.length; i++) {
                        urls.push(await uploadLoginMedia(mfs[i]));
                    }
                    box.querySelector('#admLoginImageUrls').value = urls.join('\n');
                }
            } catch (_) {
                messages.toast('Error al subir archivos.');
                return;
            }

            var imageLines = box.querySelector('#admLoginImageUrls').value.split(/\r?\n/).map(function (x) {
                return x.trim();
            }).filter(Boolean);

            var loginScreen = {
                bgType: box.querySelector('#admLoginBgType').value,
                videoUrl: box.querySelector('#admLoginVideoUrl').value.trim(),
                imageUrls: imageLines,
                slideshowMode: box.querySelector('#admLoginSlideshow').value,
                fixedImageIndex: parseInt(box.querySelector('#admLoginFixedIdx').value, 10) || 0,
                slideshowIntervalSec: parseInt(box.querySelector('#admLoginInterval').value, 10) || 18,
            };

            var saved = await g.modules.config.globalConfig.save({
                requireLogin: box.querySelector('#admReq').checked,
                publicSignup: box.querySelector('#admPub').checked,
                loginScreen: loginScreen,
            });
            messages.toast(messages.text('permSaved'));
            try {
                if (overlay && overlay.style.display !== 'none') syncLoginBackdrop(saved);
            } catch (_) {}
            close();
        });
    }

    function openProfileModal() {
        var u = sess.getUser();
        if (!u || !u.id) return;
        var backdrop = dom.el('div', { className: 'auth-modal-backdrop' });
        var box = dom.el('div', { className: 'auth-modal' });
        box.innerHTML =
            '<h3 class="auth-modal-title">' +
            messages.escape(messages.text('profileTitle')) +
            '</h3>' +
            '<label class="auth-label">' +
            messages.escape(messages.text('changePw')) +
            '</label>' +
            '<input type="password" id="profPw" class="auth-input" autocomplete="new-password" />' +
            '<label class="auth-label">' +
            messages.escape(messages.text('pin')) +
            '</label>' +
            '<input type="password" id="profPin" class="auth-input" inputmode="numeric" />' +
            '<label class="auth-label">' +
            messages.escape(messages.text('patternLabelProfile')) +
            '</label>' +
            '<input type="text" id="profPat" class="auth-input" />' +
            '<label class="auth-label">' +
            messages.escape(messages.text('loginTypeLabel')) +
            '</label>' +
            '<select id="profLt" class="auth-input" aria-label="' + messages.escape(messages.text('loginTypeLabel')) + '">' +
            '<option value="password">' + messages.escape(messages.text('methodPassword')) + '</option>' +
            '<option value="pin">' + messages.escape(messages.text('methodPin')) + '</option>' +
            '<option value="pattern">' + messages.escape(messages.text('methodPattern')) + '</option>' +
            '<option value="webauthn">' + messages.escape(messages.text('methodWebauthn')) + '</option>' +
            '</select>' +
            '<div class="auth-modal-actions">' +
            '<button type="button" class="auth-btn auth-btn-secondary" id="profClose">' +
            messages.escape(messages.text('cancel')) +
            '</button>' +
            '<button type="button" class="auth-btn auth-btn-primary" id="profSave">' +
            messages.escape(messages.text('saveProfile')) +
            '</button>' +
            '</div>' +
            '<p class="auth-hint"><button type="button" class="auth-link" id="profBio">' +
            messages.escape(messages.text('registerBioLink')) +
            '</button></p>';
        backdrop.appendChild(box);
        document.body.appendChild(backdrop);

        var detProf = g.modules.hardware.deviceDetector;
        Promise.all([
            detProf.showBiometricOption(),
            g.modules.users.userStore.findById(u.id),
        ]).then(function (results) {
            var showBio = results[0];
            var full = results[1];
            var sel = box.querySelector('#profLt');
            var bioBtn = box.querySelector('#profBio');
            if (!showBio) {
                var opt = sel.querySelector('option[value="webauthn"]');
                if (opt) opt.remove();
                if (bioBtn) bioBtn.style.display = 'none';
            }
            if (!full) return;
            var lt = (full.metadata && full.metadata.loginType) || 'password';
            if (lt === 'webauthn' && !showBio) {
                sel.value = 'password';
            } else {
                sel.value = lt;
            }
        });

        function close() {
            if (backdrop.parentNode) backdrop.parentNode.removeChild(backdrop);
        }

        box.querySelector('#profClose').addEventListener('click', close);
        backdrop.addEventListener('click', function (e) {
            if (e.target === backdrop) close();
        });
        box.querySelector('#profSave').addEventListener('click', async function () {
            var pw = box.querySelector('#profPw').value;
            var pi = box.querySelector('#profPin').value;
            var pat = box.querySelector('#profPat').value;
            var lt = box.querySelector('#profLt').value;
            try {
                if (pw && pw.length >= 6) await g.modules.users.changeCredentials.changePassword(u.id, pw);
                if (pi && /^\d{4,8}$/.test(pi)) await g.modules.users.changeCredentials.changePin(u.id, pi);
                if (pat && pat.length >= 4) await g.modules.users.changeCredentials.changePattern(u.id, pat);
                await g.modules.auth.loginTypeSelector.setPreferred(u.id, lt);
                messages.toast(messages.text('permSaved'));
                close();
            } catch (_) {
                messages.alert(messages.text('errGeneric'));
            }
        });
        box.querySelector('#profBio').addEventListener('click', async function () {
            try {
                var full = await g.modules.users.userStore.findById(u.id);
                var cred = await g.modules.auth.biometricLogin.register(full);
                await g.modules.users.changeCredentials.addWebAuthnCredential(u.id, cred);
                await g.modules.auth.loginTypeSelector.setPreferred(u.id, 'webauthn');
                messages.toast(messages.text('permSaved'));
            } catch (_) {
                messages.toast(messages.text('useAltMethod'));
            }
        });
    }

    function ensureProfileFab() {
        if (profileFab) return profileFab;
        profileFab = dom.el('button', {
            type: 'button',
            className: 'grid-auth-profile-fab',
            title: messages.text('profileTitle'),
            onclick: openProfileModal,
        });
        profileFab.innerHTML = '<i class="fas fa-user" aria-hidden="true"></i>';
        profileFab.style.display = 'none';
        document.body.appendChild(profileFab);
        return profileFab;
    }

    function ensureAdminFab() {
        if (adminFab) return adminFab;
        adminFab = dom.el('button', {
            type: 'button',
            className: 'grid-auth-admin-fab',
            title: messages.text('adminPanel'),
            onclick: openAdminModal,
        });
        adminFab.innerHTML = '<i class="fas fa-user-shield" aria-hidden="true"></i>';
        adminFab.style.display = 'none';
        document.body.appendChild(adminFab);
        return adminFab;
    }

    g.modules.ui.loginUI = {
        showGate: showGate,
        hideGate: hideGate,
        renderLogin: renderLogin,
        openAdminModal: openAdminModal,

        boot: async function () {
            try {
                document.body.classList.add('grid-auth-booting');
            } catch (_) {}
            ensureOverlay().style.display = 'none';
            ensureAdminFab();
            ensureProfileFab();
            await g.modules.users.userStore.load();
            await g.modules.config.globalConfig.load();

            var cfg = await g.modules.config.globalConfig.load();
            var users = await g.modules.users.userStore.getAll();

            var needOverlay = cfg.requireLogin || (users.length === 0 && !cfg.publicDesktopWithoutUsers);
            if (!needOverlay) {
                hideGate();
                try {
                    document.body.classList.remove('grid-auth-booting');
                } catch (_) {}
                return;
            }
            if (sess.isAuthenticated()) {
                hideGate();
                try {
                    document.body.classList.remove('grid-auth-booting');
                } catch (_) {}
                return;
            }

            if (users.length === 0) {
                var cfgFirst = await g.modules.config.globalConfig.load();
                ensureOverlay();
                syncLoginBackdrop(cfgFirst);
                var host = ensureOverlay().querySelector('#grid-auth-body');
                host.innerHTML = '';
                var title = dom.el('div', { className: 'grid-auth-brand', text: 'Primer usuario' });
                host.appendChild(title);
                var panel = dom.el('div', { id: 'auth-first-reg' });
                host.appendChild(panel);
                g.modules.ui.registerWizard.run(panel, async function (state) {
                    setBusy(true);
                    try {
                        var user = await g.modules.users.userCRUD.create({
                            email: state.email,
                            phone: state.phone,
                            password: state.password,
                            pin: state.pin,
                            patternSeq: state.patternSeq,
                            recoveryCode: state.recoveryCode,
                            loginType: state.wantBio ? 'webauthn' : 'password',
                        });
                        if (state.wantBio) {
                            try {
                                var cred = await g.modules.auth.biometricLogin.register(user);
                                await g.modules.users.changeCredentials.addWebAuthnCredential(user.id, cred);
                                await g.modules.auth.loginTypeSelector.setPreferred(user.id, 'webauthn');
                            } catch (_) {}
                        }
                        sess.setUser(user);
                        hideGate();
                        g.eventBus.emit('auth:login', { userId: user.id });
                    } catch (e) {
                        messages.alert(messages.text('errGeneric'));
                    } finally {
                        setBusy(false);
                    }
                });
                showGate();
                try {
                    document.body.classList.remove('grid-auth-booting');
                } catch (_) {}
                return;
            }

            await renderLogin();
            try {
                document.body.classList.remove('grid-auth-booting');
            } catch (_) {}
        },

        logout: function () {
            sess.clear();
            g.eventBus.emit('auth:logout', {});
            window.location.reload();
        },
    };

    g.gridAuthLogout = function () {
        g.modules.ui.loginUI.logout();
    };
})(typeof window !== 'undefined' ? window : this);
