import * as CryptoJS from 'crypto-js';
import {inflate} from 'pako/lib/inflate';
import {ECKCDSA} from './ec-kcdsa';
import {Converter} from './converter';
import {EncryptedData} from './typings/encryptedData';

const IV_LENGTH = 16;
const SHARED_KEY_SIZE = 32;

function decrypt(ivCiphertext: Uint8Array, nonce: Uint8Array, sharedKeyOrig: any[]): Uint8Array {

    if (ivCiphertext.length < IV_LENGTH ||
        ivCiphertext.length % IV_LENGTH !== 0) {
        throw new Error('Invalid Ciphertext');
    }

    const sharedKey = sharedKeyOrig.slice(0);

    for (let i = 0; i < SHARED_KEY_SIZE; i++) {
        // tslint:disable:no-bitwise
        sharedKey[i] ^= nonce[i];
    }

    const key = CryptoJS.SHA256(Converter.convertByteArrayToWordArray(sharedKey));
    const iv = Converter.convertByteArrayToWordArray(ivCiphertext.slice(0, IV_LENGTH));
    const ciphertext = Converter.convertByteArrayToWordArray(ivCiphertext.slice(IV_LENGTH));

    // @ts-ignore
    const encrypted = CryptoJS.lib.CipherParams.create({
        ciphertext: ciphertext,
        iv: iv,
        key: key
    });

    // @ts-ignore
    const decrypted = CryptoJS.AES.decrypt(encrypted, key, {iv});

    return Converter.convertWordArrayToUint8Array(decrypted);
}

/**
 * Decrypts an encrypted cipher text
 * @param encryptedData The encrypted data
 * @param senderPublicKeyHex The senders public key in hex format
 * @param recipientPrivateKeyHex The recipients private (agreement) key in hex format
 * @return The original plain text
 * @module crypto
 */
export function decryptData(
    encryptedData: EncryptedData,
    senderPublicKeyHex: string,
    recipientPrivateKeyHex: string): Uint8Array {

    const sharedKey =
        ECKCDSA.sharedkey(
            Converter.convertHexStringToByteArray(recipientPrivateKeyHex),
            Converter.convertHexStringToByteArray(senderPublicKeyHex)
        );

    const compressedPlaintext = decrypt(encryptedData.data, encryptedData.nonce, sharedKey);
    return inflate(compressedPlaintext);
}
