var apx = apx || {};

apx.crypto = apx.crypto || {};
apx.indexedDB = apx.indexedDB || {};

apx.crypto.genKey = async (uuid) => {
    return await openpgp.generateKey(
        {
            type: "ecc",
            curve: "curve25519",
            userIDs: [
                {
                    alias: uuid
                }
            ],
            passphrase: "",
            format: "armored",
        }
    );
};

apx.crypto.encryptMessage = async (message, publicKey) => {
    publicKey = await openpgp.readKey(
        {
            armoredKey: publicKey
        }
    );

    return await openpgp.encrypt(
        {
            message: await openpgp.createMessage(
                {
                    text: message
                }
            ),
            encryptionKeys: publicKey
        }
    );
};

apx.crypto.decryptMessage = async (encryptedMessage, privateKey) => {
    privateKey = await openpgp.readPrivateKey(
        {
            armoredKey: privateKey
        }
    );

    const message = await openpgp.readMessage(
        {
            armoredMessage: encryptedMessage
        }
    );

    return await openpgp.decrypt(
        {
            message,
            decryptionKeys: privateKey
        }
    );
};

apx.crypto.sign = async (message, privateKey) => {
    privateKey = await openpgp.readPrivateKey(
        {
            armoredKey: privateKey
        }
    );

    return await openpgp.sign(
        {
            message: await openpgp.createMessage(
                {
                    text: message
                }
            ),
            signingKeys: privateKey
        }
    );
};

apx.crypto.verifySignature = async (message, signature, publicKey) => {
    publicKey = await openpgp.readKey(
        {
            armoredKey: publicKey
        }
    );

    const verified = await openpgp.verify(
        {
            message: await openpgp.createMessage(
                {
                    text: message
                }
            ),
            signature: await openpgp.readSignature(
                {
                    armoredSignature: signature
                }
            ),
            verificationKeys: publicKey
        }
    );

    if (await verified.signatures[0].verified) {
        return true;
    } else {
        return false;
    };
};

apx.indexedDB.set = async (db, storeName, value) => {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open(db, 1);

        request.onupgradeneeded = (event) => {
            const db = event.target.result;

            if (!db.objectStoreNames.contains("threads")) {
                db.createObjectStore("threads", { keyPath: "uuid" });
            };

            if (!db.objectStoreNames.contains("messages")) {
                db.createObjectStore("messages", { keyPath: "privatriid" });
            };
        };

        request.onsuccess = (event) => {
            const db = event.target.result;

            if (!db.objectStoreNames.contains(storeName)) {
                return resolve();
            };

            const transaction = db.transaction(storeName, "readwrite");
            const store = transaction.objectStore(storeName);

            const putRequest = store.put(value);
            putRequest.onsuccess = () => resolve();
            putRequest.onerror = (error) => reject(error);
        };

        request.onerror = (error) => reject(error);
    });
};

apx.indexedDB.get =  async (db, storeName, key) => {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open(db, 1);

        request.onsuccess = (event) => {
            const db = event.target.result;
            if (!db.objectStoreNames.contains(storeName)) {
                return resolve(null);
            }
            const transaction = db.transaction(storeName, "readonly");
            const store = transaction.objectStore(storeName);
            
            const getRequest = store.get(key);
            
            getRequest.onsuccess = () => {
                resolve(getRequest.result || null);
            };

            getRequest.onerror = () => resolve(null);
        };

        request.onerror = (error) => reject(error);
    });
};

apx.indexedDB.del = async (db, storeName, key) => {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open(db, 1);

        request.onsuccess = (event) => {
            const db = event.target.result;

            if (!db.objectStoreNames.contains(storeName)) {
                return resolve();
            };

            const transaction = db.transaction(storeName, "readwrite");
            const store = transaction.objectStore(storeName);

            const deleteRequest = store.delete(key);
            deleteRequest.onsuccess = () => resolve();
            deleteRequest.onerror = (error) => reject(error);
        };

        request.onerror = (error) => reject(error);
    });
};

export default apx;