/**
 * Hash
 * bcrypt-based password hashing utility.
 *
 * Uses bcryptjs to avoid native module issues in edge runtimes.
 */
import { Logger } from '../config/logger.js';
import { ErrorFactory } from '../exceptions/ZintrustError.js';
const BCRYPT_HASH_RE = /^\$2[aby]\$\d{2}\$[./A-Za-z0-9]{53}$/;
let bcryptModule = null;
const loadBcrypt = async () => {
    if (bcryptModule !== null)
        return bcryptModule;
    try {
        const mod = (await import('bcryptjs'));
        const resolved = (mod.default ?? mod);
        if (typeof resolved?.hash !== 'function' || typeof resolved?.compare !== 'function') {
            throw ErrorFactory.createConfigError('Invalid bcryptjs module shape');
        }
        bcryptModule = resolved;
        return resolved;
    }
    catch (error) {
        throw ErrorFactory.createConfigError('bcryptjs module unavailable', error);
    }
};
export const Hash = Object.freeze({
    isValidHash(hash) {
        return BCRYPT_HASH_RE.test(hash);
    },
    async hash(plaintext) {
        try {
            const bcrypt = await loadBcrypt();
            return await bcrypt.hash(plaintext, 12);
        }
        catch (error) {
            Logger.error('Password hashing failed', error);
            const err = error;
            if (err?.name === 'ConfigError' || err?.code === 'CONFIG_ERROR') {
                throw ErrorFactory.createConfigError('Password hashing failed', error);
            }
            throw ErrorFactory.createSecurityError('Password hashing failed', error);
        }
    },
    async hashWithRounds(plaintext, rounds) {
        const normalizedRounds = Number.isFinite(rounds) ? Math.trunc(rounds) : 0;
        if (normalizedRounds <= 0) {
            throw ErrorFactory.createConfigError('Invalid bcrypt rounds', { rounds });
        }
        try {
            const bcrypt = await loadBcrypt();
            return await bcrypt.hash(plaintext, normalizedRounds);
        }
        catch (error) {
            Logger.error('Password hashing failed', error);
            const err = error;
            if (err?.name === 'ConfigError' || err?.code === 'CONFIG_ERROR') {
                throw ErrorFactory.createConfigError('Password hashing failed', error);
            }
            throw ErrorFactory.createSecurityError('Password hashing failed', error);
        }
    },
    async verify(plaintext, hashed) {
        if (!Hash.isValidHash(hashed))
            return false;
        try {
            const bcrypt = await loadBcrypt();
            return await bcrypt.compare(plaintext, hashed);
        }
        catch (error) {
            Logger.error('Password verify failed', error);
            return false;
        }
    },
});
export default Hash;
