/**
 * modules/auth/biometricLogin.js — WebAuthn (navigator.credentials) + desafío servidor.
 */
(function (g) {
    g.modules = g.modules || {};
    g.modules.auth = g.modules.auth || {};

    var storage = g.modules.core.storageAdapter;
    var bus = g.eventBus;

    function b64encode(buf) {
        var bytes = new Uint8Array(buf);
        var s = '';
        for (var i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
        return btoa(s);
    }

    function b64decode(b64) {
        var bin = atob(b64);
        var bytes = new Uint8Array(bin.length);
        for (var i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
        return bytes.buffer;
    }

    g.modules.auth.biometricLogin = {
        /** Registro: create() con challenge del servidor (opcionalmente randomBytes locales si falla API). */
        register: async function (user) {
            var uid = user.id;
            var begin = await storage.webAuthnBegin({ purpose: 'registration', userId: uid });
            if (!begin.success || !begin.challengeB64) throw new Error('challenge_failed');
            var challenge = b64decode(begin.challengeB64);
            var enc = new TextEncoder();

            var publicKeyCredentialCreationOptions = {
                challenge: challenge,
                rp: { name: 'Antara Grid', id: (g.location && g.location.hostname) ? g.location.hostname : 'localhost' },
                user: {
                    id: enc.encode(uid),
                    name: user.email || uid,
                    displayName: user.email || uid,
                },
                pubKeyCredParams: [{ type: 'public-key', alg: -7 }, { type: 'public-key', alg: -257 }],
                authenticatorSelection: {
                    authenticatorAttachment: g.modules.hardware.deviceDetector.isMobile() ? 'platform' : undefined,
                    userVerification: 'preferred',
                },
                timeout: 60000,
                attestation: 'none',
            };

            var cred = await navigator.credentials.create({ publicKey: publicKeyCredentialCreationOptions });
            if (!cred || cred.type !== 'public-key') throw new Error('no_credential');

            await storage.webAuthnFinish({ challengeId: begin.challengeId });

            return {
                credentialIdB64: b64encode(cred.rawId),
                rawIdB64: b64encode(cred.rawId),
                transports: cred.response.getTransports ? cred.response.getTransports() : [],
            };
        },

        /** Autenticación con credencial existente. */
        authenticate: async function (user) {
            var creds = user.webauthnCredentials || [];
            if (!creds.length) throw new Error('no_credentials');

            var begin = await storage.webAuthnBegin({ purpose: 'authentication', userId: user.id });
            if (!begin.success || !begin.challengeB64) throw new Error('challenge_failed');
            var challenge = b64decode(begin.challengeB64);

            var allowCredentials = creds.map(function (c) {
                var id = c.rawIdB64 ? b64decode(c.rawIdB64) : b64decode(c.credentialIdB64);
                return { type: 'public-key', id: id, transports: c.transports || ['internal'] };
            });

            var assertion = await navigator.credentials.get({
                publicKey: {
                    challenge: challenge,
                    allowCredentials: allowCredentials,
                    userVerification: 'preferred',
                    timeout: 60000,
                },
            });

            if (!assertion) {
                bus.emit('auth:biometricFailed', { userId: user.id });
                throw new Error('cancelled');
            }

            await storage.webAuthnFinish({ challengeId: begin.challengeId });
            return true;
        },
    };
})(typeof window !== 'undefined' ? window : this);
