const { createHash, createHmac, createCipheriv, createDecipheriv, randomBytes, scryptSync, randomUUID } = require('crypto');
const { v4: uuidv4 } = require('uuid');

module.exports = {

    randomUUID: function() {
        return randomUUID ? randomUUID() : uuidv4();
    },

    md5: function(data, salt, enc) {
        return hash('md5', data, salt, enc)
    },

    sha1: function(data, salt, enc) {
        return hash('sha1', data, salt, enc)
    },

    sha256: function(data, salt, enc) {
        return hash('sha256', data, salt, enc)
    },

    sha512: function(data, salt, enc) {
        return hash('sha512', data, salt, enc)
    },

    hash: function(data, alg, enc) {
        return hash(alg, data, '', enc);
    },

    hmac: function(data, alg, secret, enc) {
        return hmac(alg, data, secret, enc);
    },

    transform: function(data, from, to) {
        return transform(data, from, to);
    },

    encodeBase64: function(data, enc) {
        enc = typeof enc == 'string' ? enc : 'utf8';
        return transform(data, enc, 'base64');
    },

    decodeBase64: function(data, enc) {
        enc = typeof enc == 'string' ? enc : 'utf8';
        return transform(data, 'base64', enc);
    },

    encodeBase64Url: function(data, enc) {
        enc = typeof enc == 'string' ? enc : 'utf8';
        return transform(data, enc, 'base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
    },

    decodeBase64Url: function(data, enc) {
        enc = typeof enc == 'string' ? enc : 'utf8';
        return transform(data.replace(/-/g, '+').replace(/_/g, '/'), 'base64', enc);
    },

    encrypt: function(data, password, enc) {
        const config = require('../setup/config');
        password = typeof password == 'string' ? password : config.secret;
        enc = typeof enc == 'string' ? enc : 'base64';
        const iv = randomBytes(16);
        const key = scryptSync(password, iv, 32);
        const cipher = createCipheriv('aes-256-cbc', key, iv);
        const encrypted = cipher.update(data, 'utf8', enc);
        return iv.toString(enc) + '.' + encrypted + cipher.final(enc);
    },

    decrypt: function(data, password, enc) {
        const config = require('../setup/config');
        password = typeof password == 'string' ? password : config.secret;
        enc = typeof enc == 'string' ? enc : 'base64';
        const iv = Buffer.from(data.split('.')[0], enc);
        const key = scryptSync(password, iv, 32);
        const decipher = createDecipheriv('aes-256-cbc', key, iv);
        const decrypted = decipher.update(data.split('.')[1], enc, 'utf8');
        return decrypted + decipher.final('utf8');
    },

};

function hash(alg, data, salt, enc) {
    if (data == null) return data;
    salt = typeof salt == 'string' ? salt : '';
    enc = typeof enc == 'string' ? enc : 'hex';
    return createHash(alg).update(data + salt).digest(enc);
}

function hmac(alg, data, secret, enc) {
    if (data == null) return data;
    secret = typeof secret == 'string' ? secret : '';
    enc = typeof enc == 'string' ? enc : 'hex';
    return createHmac(alg, secret).update(data).digest(enc);
}

function transform(data, from, to) {
    if (data == null) return data;
    return Buffer.from(String(data), from).toString(to);
}