/*
 * Copyright 2021 Netsyms Technologies.
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

var keymgr;
var keyring = new kbpgp.keyring.KeyRing();

/**
 * Load and unlock the private key in localstorage, prompting user as needed. If there is no key, generates, saves, and loads a new one.
 * @param {function} callback Passed two arguments: message for user, and boolean true if OK false if error.
 * @returns {undefined}
 */
function loadKeyFromLocalStorage(callback) {
    if (typeof keymgr != "undefined") {
        callback("Key already loaded.", true);
        return;
    }
    $("#lockstatus").css("display", "none");
    if (!inStorage("signingkey") || getStorage("signingkey") == "undefined") {
        showPasswordPrompt("Generating a new signing key (might take a while, be patient). Enter a password to protect it. You'll need to save this password somewhere safe; it cannot be recovered.", function (pass) {
            generatePrivateKey(getStorage("notary_name") + " <null@null.com>", pass, function (key) {
                if (typeof key == "undefined") {
                    callback("Could not generate key.", false);
                    return;
                }
                keymgr = key;
                keyring.add_key_manager(keymgr);
                setStorage("signingkey", keymgr.armored_pgp_private);
                callback("Signing key generated.", true);
            });
        });
    } else {
        showPasswordPrompt("Enter password to unlock signing key:", function (pass) {
            loadPrivateKey(getStorage("signingkey"), pass, function (key) {
                if (typeof key == "undefined") {
                    callback("Could not unlock key. Password is probably incorrect.", false);
                    return;
                }
                keymgr = key;
                keyring.add_key_manager(keymgr);
                callback("Signing key unlocked.", true);
            });
        });
    }
}

function unloadKey() {
    keymgr = undefined;
    $("#lockstatus").css("display", "");
    showToast("<i class='fas fa-lock'></i> Signing key locked.");
}

function loadKeyFromLocalStorageWithUserFeedback() {
    loadKeyFromLocalStorage(function (msg, ok) {
        if (ok) {
            showToast("<i class='fas fa-unlock'></i> " + msg);
        } else {
            showAlert("Error: " + msg);
        }
    });
}

/**
 * Load a private key.
 * @param {string} armoredkey PGP private key
 * @param {string} pass key password
 * @param {function} callback Passed a new keymanager for the key.
 * @returns {undefined}
 */
function loadPrivateKey(armoredkey, pass, callback) {
    kbpgp.KeyManager.import_from_armored_pgp({
        armored: armoredkey
    }, function (err, key) {
        if (!err) {
            if (key.is_pgp_locked()) {
                key.unlock_pgp({
                    passphrase: pass
                }, function (err) {
                    if (!err) {
                        console.log("Loaded private key with passphrase");
                        callback(key);
                    } else {
                        console.error(err);
                        callback(undefined);
                    }
                });
            } else {
                console.log("Loaded private key w/o passphrase");
                callback(key);
            }
        } else {
            console.error(err);
            callback(undefined);
        }
    });
}

/**
 * Sign a message with a key and return the signed message in the callback.
 * @param {type} text
 * @param {type} key
 * @param {type} callback
 * @returns {undefined}
 */
function signMessage(text, key, callback) {
    var params = {
        msg: text,
        sign_with: key
    };
    kbpgp.box(params, function (err, result_string, result_buffer) {
        //console.log(err, result_string, result_buffer);
        callback(result_string);
    });
}

/**
 * Read a signed PGP message and return the contents and signer's fingerprint/key ID.
 * @param {string} pgpmsg "-----BEGIN PGP MESSAGE----- ..."
 * @param {function} callback function(message, fingerprint) {}
 * @param {function} onerror function(errormessage) {}
 * @returns {undefined}
 */
function verifyMessage(pgpmsg, callback, onerror) {
    kbpgp.unbox({keyfetch: keyring, armored: pgpmsg}, function (err, literals) {
        if (err != null) {
            onerror(err);
        } else {
            var message = literals[0].toString();
            var fingerprint = null;
            var ds = km = null;
            ds = literals[0].get_data_signer();
            if (ds) {
                km = ds.get_key_manager();
            }
            if (km) {
                fingerprint = km.get_pgp_fingerprint().toString('hex');
            }
            callback(message, fingerprint);
        }
    });
}

/**
 * Generate a new private key.
 * @param {string} userid Something like "Test User <test@netsyms.com>"
 * @param {string} passphrase protects the key
 * @param {function} callback Passed the keymanager for the new key
 * @returns {undefined}
 */
function generatePrivateKey(userid, passphrase, callback) {
    var statustextEl = $("#statustext");
    statustextEl.html("<i class='fas fa-spin fa-spinner'></i> Generating cryptographic key...");
    setTimeout(function () {
        var F = kbpgp["const"].openpgp;

        var opts = {
            userid: userid,
            primary: {
                nbits: 2048,
                flags: F.certify_keys | F.sign_data | F.auth | F.encrypt_comm | F.encrypt_storage,
                expire_in: 0  // never expire
            },
            subkeys: []
        };

        kbpgp.KeyManager.generate(opts, function (err, alice) {
            if (!err) {
                alice.sign({}, function (err) {
                    alice.export_pgp_private({
                        passphrase: passphrase
                    }, function (err, pgp_private) {
                        statustextEl.html("<i class='fas fa-check'></i> Key generated!");
                        setTimeout(function () {
                            statustextEl.html("");
                        }, 5000);
                        callback(alice);
                    });
                });
            }
        });
    }, 100);
}

function exportPublicKey() {
    loadKeyFromLocalStorage(function (message, ok) {
        if (ok) {
            openSaveFileDialog(function (path) {
                keymgr.export_pgp_public({}, function (err, pgp_public) {
                    if (err) {
                        showAlert("Something went wrong.");
                    } else {
                        writeToFile(path, pgp_public);
                    }
                });
            }, "public-key.asc", ".asc");
        } else {
            showAlert("Error: " + message);
        }
    });
}

/**
 * This should be modified to prompt the user for a backup password,
 * but that doesn't work. https://github.com/keybase/kbpgp/issues/211
 * @returns {undefined}
 */
function exportPrivateKey() {
    loadKeyFromLocalStorage(function (message, ok) {
        if (!ok) {
            showAlert("Error: " + message);
            return;
        }
        openSaveFileDialog(function (path) {
            keymgr.export_pgp_private({}, function (err, pgp_private) {
                if (err) {
                    showAlert("Something went wrong.");
                } else {
                    writeToFile(path, pgp_private);
                    showAlert("Private key backup saved.");
                }
            });
        }, "private-key.asc", ".asc");
    });
}

function importPrivateKey() {
    if (inStorage("signingkey") && getStorage("signingkey") != "undefined") {
        if (!confirm("The restored key will replace the current key, which will be unrecoverable unless you made a backup. Continue?")) {
            return;
        }
    }
    keymgr = null;
    openFileDialog(function (path) {
        var keyfile = getFileAsString(path);
        showPasswordPrompt("Enter password for imported key (password was set when exported):", function (pass) {
            loadPrivateKey(keyfile, pass, function (key) {
                if (typeof key == "undefined") {
                    showAlert("Could not import key. Password is probably incorrect.");
                    return;
                }
                keymgr = key;
                setStorage("signingkey", keymgr.armored_pgp_private);
                showAlert("Private key imported.");
            });
        });
    }, ".asc");
}

function calculateSHA256HashOfBuffer(buffer) {
    const hasha = require('hasha');
    var hashstr = hasha(Buffer.from(buffer), {algorithm: 'sha256'});
    return hashstr;
}

function calculateSHA256HashOfString(str) {
    const hasha = require('hasha');
    var hashstr = hasha(str, {algorithm: 'sha256'});
    return hashstr;
}

function openPublicKeyFile() {
    openFileDialog(function (path, html5file) {
        var importpk = function (keyfile) {
            kbpgp.KeyManager.import_from_armored_pgp({
                armored: keyfile
            }, function (err, pubkeymgr) {
                if (!err) {
                    keyring.add_key_manager(pubkeymgr);
                    showAlert("Public key file loaded. You can now analyze PDFs signed by the key's owner.");
                } else {
                    showAlert("Error loading public key: " + err);
                }
            });
        };

        if (typeof nw != 'undefined') {
            var keyfile = getFileAsString(path);
            importpk(keyfile);
        } else {
            var fileReader = new FileReader();
            fileReader.onload = function (e) {
                importpk(e.target.result);
            }
            fileReader.readAsText(html5file);
        }
    }, ".asc");
}

/**
 * Show visual indicator when private key is not loaded/unlocked.
 * @returns {undefined}
 */
setInterval(function () {
    if (typeof keymgr == "undefined") {
        $("#lockstatus").css("display", "");
    } else {
        $("#lockstatus").css("display", "none");
    }
}, 1000);