//modules for core functions

var crypto = require("crypto-js"); // library for enrypt/decrypt accounts
var storage = require("node-persist"); // library for read/write account on local container
var signale = require('signale'); // library for handling the output rendering process

/**MODULE
 * Decript container "accounts" ( if not exists, create it ),
 * Insert new account on container
 * Encrypt all accounts with new one
 * @param {*} account -> new account created
 * @param {*} masterPassword -> password for crypt and decrypt
 */
exports.createAccount = function(account, masterPassword)    {

    //create variable called "accounts" that contain all data on file "accounts" stored on fileSystem
    var accounts = module.exports.getAccounts(masterPassword);

    if (accounts) {
        accounts.push(account); // push the object into array accounts

        //Save new account
        module.exports.saveAccounts(accounts, masterPassword);
        return account;
    } else  {
        return null;
    }
    

};

/**MODULE
 * Get return value from getAccounts function and find an existing account
 * Can delete the account
 * @param {*} nameAccount -> name of new account
 * @param {*} masterPassword -> password for crypt and decrypt
 * @param {*} deleteAccount -> boolean for decide if delete the account
 */
exports.getAccount = function(nameAccount, masterPassword) {

    //Get all accounts decrypted
    var existAccounts = module.exports.getAccounts(masterPassword);    // get entire content of "accounts" file on filesystem and insert into variable
    var matchedAccount = null;
    if (existAccounts) {
        existAccounts.forEach(function (account) {

            //If an acount is corrupted ( i can obtain this case )
            if (account != undefined) {

                if (nameAccount == account.name) {
                    matchedAccount = account;
                };
            }
        });
    }

    return matchedAccount;
};

/**MODULE
 * Modify the existing account
 * @param {*} existingAccount ->Object of existing account
 * @param {*} newData -> Object of newAccount 
 * @param {*} masterPassword -> password for crypt and decrypt
 */
exports.modifyAccount = function (existingAccount, newData, masterPassword) {

    var newAccount = {};

    //If new account and existing account exist
    if (existingAccount && newData) {

        //Get all property names of two accounts
        var oldAccountProps = Object.getOwnPropertyNames(existingAccount);
        var newAccountProp = Object.getOwnPropertyNames(newData);

        // if they have different length
        if (oldAccountProps.length != newAccountProp.length) {
            return [];
        }

        oldAccountProps.forEach(function(propName)  {

            //if property of old account exist on new account
            if (oldAccountProps[propName] == newAccountProp[propName]) {

                //if property has been defined
                if (newData[propName]) {
                    newAccount[propName] = newData[propName];

                    //get old property value
                } else  {
                    newAccount[propName] = existingAccount[propName];
                }
            }
        })

        //Get all existing account decrypted
        var oldAccounts = exports.getAccounts(masterPassword);

        if (oldAccounts) {
            oldAccounts.forEach(function (oldAccount, index) {

                //Update old account with new account
                if (oldAccount.name == existingAccount.name) {
                    oldAccounts[index] = newAccount;
                }

            });
        }
        //save all accounts
        exports.saveAccounts(oldAccounts, masterPassword);
        
    }

    return newAccount;
}

/**MODULE
 * Get content from "accounts" file on filesystem and if it content is populated, decrypt data
 * If accounts variable not exist, create it
 * @param {*} masterPassword -> password for crypt and decrypt
 */
exports.getAccounts = function(masterPassword)  {
    
    var accountEncrypted = storage.getItemSync("accounts"); // create a variable for control the content of "accounts" file on filesystem
    var accounts = [];

    if(accountEncrypted !== undefined)   {
        
        try {
            var accountDecrypted = crypto.AES.decrypt(accountEncrypted, masterPassword);
            accounts = JSON.parse(accountDecrypted.toString(crypto.enc.Utf8));
        } catch(err) {
            signale.error("Master password is not correct!");
            return null;
        }
    };
    return accounts;
};

/**MODULE
 * Encrypt all data and set into container "accounts"
 *
 * The Advanced Encryption Standard (AES) is a U.S. Federal Information Processing Standard (FIPS). It was selected after a 5-year process where 15 competing designs were evaluated.
 * CryptoJS supports AES-128, AES-192, and AES-256. It will pick the variant by the size of the key you pass in. If you use a passphrase, then it will generate a 256-bit key.
 * @param {*} accounts -> list accounts
 * @param {*} masterPassword -> password for crypt and decrypt
 */
exports.saveAccounts = function(accounts, masterPassword) {

    var encryptAccount = crypto.AES.encrypt(JSON.stringify(accounts), masterPassword);
    storage.setItemSync("accounts", encryptAccount.toString()); //insert into "accounts" file on filesystem the entire array
}

/**MODULE
 * Delete specific account
 * @param {*} nameAccount -> Account name
 * @param {*} masterPassword -> password for crypt and decrypt
 */
exports.deleteAccount = function(nameAccount, masterPassword) {

    var existAccounts = module.exports.getAccounts(masterPassword);    // get entire content of "accounts" file on filesystem and insert into variable

    if (existAccounts) {
        existAccounts.forEach(function (account, index) {

            if (nameAccount == account.name) {

                existAccounts.splice(index, 1);
                module.exports.saveAccounts(existAccounts, masterPassword);
            }
        });
    }
}

/**MODULE
 * Generate a random password
 */
exports.generatePassword = function () {
        var pass = Math.random()        // Generate random number, eg: 0.123456

        .toString(36)           // Convert  to base-36 : "0.4fzyo82mvyr". Base36 is a binary-to-text encoding scheme that represents 
        //binary data in an ASCII string format by translating it into a radix-36 representation. The choice of 36 
        //is convenient in that the digits can be represented using the Arabic numerals 0–9 and the Latin letters A–Z[1] (the ISO basic Latin alphabet).

        .slice(-8);// Cut off last 8 characters : "yo82mvyr"

        return crypto.SHA256(pass).toString(crypto.enc.Base64);

}

/**
 * Module for encrypt document
 */
exports.encryptDocument = function(data, masterPassword)   {
    return crypto.AES.encrypt(JSON.stringify(data), masterPassword);
}

/**
 * Module for decrypt document
 */
exports.decryptDocument = function(data, masterPassword)    {
    var dataEncrypted = crypto.AES.decrypt(data, masterPassword);

    return dataEncrypted.toString(crypto.enc.Utf8).replace(/\\n/g, "\n") //replace new line
    .replace(/\\t/g, "        ") // replace tab
    .replace(/["]/g, ""); //replace double quotes
}