/*
 * Copyright (c) 2022.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */

import {
    createPublicEncryptionKeyPair,
    decryptPublicEncryptionMessage,
    encryptPublicEncryptionMessage
} from "~/modules/security/encryption/utils";
import localforage from "localforage";
import {SecurityKeyPair} from "~/modules/security";

import {mutations, getters} from "~/modules/security/store";
import {addContactData} from "~/domains/contact-data/api";
import {ContactDataSecretType} from "~/domains/contact-data/utils";

export function encodeContactMessage(message: string, contactId: string) {
    const selfKeyPair : SecurityKeyPair | undefined = getters.contactSelfKeyPair(contactId);
    const relatedKey = getters.contactRelatedKey(contactId);

    console.log(selfKeyPair, relatedKey);

    if(typeof selfKeyPair === 'undefined' || typeof relatedKey === 'undefined') {
        throw new Error('Die benötigten Schlüssel sind nicht verfügbar..');
    }

    return encryptPublicEncryptionMessage(message, relatedKey, selfKeyPair.privateKey);
}

export function decodeContactMessage(message: string, contactId: string) {
    const selfKeyPair : SecurityKeyPair | undefined = getters.contactSelfKeyPair(contactId);
    const relatedKey = getters.contactRelatedKey(contactId);

    if(typeof selfKeyPair === 'undefined' || typeof relatedKey === 'undefined') {
        throw new Error('Die benötigten Schlüssel sind nicht verfügbar..');
    }

    return decryptPublicEncryptionMessage(message, relatedKey, selfKeyPair.privateKey)
}

// self

export async function createAndSaveContactSelfKeyPair(contactId: string, shareKey: boolean = true) {
    const keyPair = createPublicEncryptionKeyPair();

    await (await useContactSelfKeyPairStorage()).setItem(contactId, keyPair);

    mutations.setContactSelfKeyPair(contactId, keyPair);

    if(shareKey) {
        await shareContactSelfKey(contactId, keyPair.publicKey);
    }
}

export async function shareContactSelfKey(contactId: string, publicKey?: string) {
    if(typeof publicKey === 'undefined') {
        const keyPair = await getContactSelfKeyPair(contactId);
        if(typeof keyPair === 'undefined') return;

        publicKey = keyPair.publicKey;
    }

    await addContactData({
        type: ContactDataSecretType.CONTACT,
        content: publicKey,
        encrypted: false,
        contactId: contactId
    });
}

export async function dropContactSelfKeyPair(contactId: string) {
    await (await useContactSelfKeyPairStorage()).removeItem(contactId);
}

export async function getContactSelfKeyPair(contactId: string) : Promise<SecurityKeyPair|undefined> {
    const keyPair = await (await useContactSelfKeyPairStorage())
        .getItem<SecurityKeyPair>(contactId);

    if(!keyPair) {
        return undefined;
    }

    return keyPair;
}

// related

export async function saveContactRelatedKey(contactId: string, key: string) {
    await (await useContactRelatedKeyStorage()).setItem(contactId, key);

    mutations.setContactRelatedKey(contactId, key);

    const selfKeyPair : SecurityKeyPair | undefined = await getContactSelfKeyPair(contactId);
    if(typeof selfKeyPair === 'undefined') {
        await createAndSaveContactSelfKeyPair(contactId);
    }
}

export async function dropContactRelatedKey(contactId: string) {
    await (await useContactRelatedKeyStorage()).removeItem(contactId);
}

export async function getContactRelatedKey(contactId: string) : Promise<string | undefined> {
    const key = await (await useContactRelatedKeyStorage())
        .getItem<string>(contactId);

    if(!key) {
        return undefined;
    }

    return key;
}

let selfKeyPairStorage : undefined | LocalForage;
let relatedKeyStorage : undefined | LocalForage;

export async function useContactSelfKeyPairStorage() : Promise<LocalForage> {
    if(typeof selfKeyPairStorage !== 'undefined') {
        return selfKeyPairStorage;
    }

    selfKeyPairStorage = localforage.createInstance({
        driver: localforage.INDEXEDDB,
        name: 'app',
        storeName: 'contactSelfKeyPair'
    });

    await initContactSelfKeys(selfKeyPairStorage);

    return selfKeyPairStorage;
}

export async function useContactRelatedKeyStorage() : Promise<LocalForage> {
    if(typeof relatedKeyStorage !== 'undefined') {
        return relatedKeyStorage;
    }

    relatedKeyStorage = localforage.createInstance({
        driver: localforage.INDEXEDDB,
        name: 'app',
        storeName: 'contactRelatedKeys'
    });

    await intContactedRelatedKeys(relatedKeyStorage);

    return relatedKeyStorage;
}

async function initContactSelfKeys(storage: LocalForage) {
    const keys : string[] = await storage.keys();
    for(let i=0; i<keys.length; i++) {
        const key : string = keys[i];
        const keyPair = <SecurityKeyPair> await storage.getItem(key);

        mutations.setContactSelfKeyPair(key, keyPair);
    }
}

async function intContactedRelatedKeys(storage: LocalForage) {
    const keys : string[] = await storage.keys();
    for(let i=0; i<keys.length; i++) {
        const key : string = keys[i];
        const keyPair = <string> await storage.getItem(key);

        mutations.setContactRelatedKey(key, keyPair);
    }
}
