package com.github.davidmoten.security;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.junit.Test;

import com.google.common.base.Charsets;
import com.google.common.collect.Lists;

public class PPKTest {

    private static final String content = "Hello World";

    @Test
    public void testEncryptAndDecrypt() throws UnsupportedEncodingException {
        PPK ppk = PPK.publicKey("/public.der").privateKey("/private.der").build();
        byte[] encrypted = ppk.encrypt(content, Charsets.UTF_8);
        String decrypted = ppk.decrypt(encrypted, Charsets.UTF_8);
        assertEquals(content, decrypted);
    }

    @Test
    public void testEncryptAndDecryptSwitchOrder() throws UnsupportedEncodingException {
        PPK ppk = PPK.privateKey("/private.der").publicKey("/public.der").build();
        byte[] encrypted = ppk.encrypt(content, Charsets.UTF_8);
        String decrypted = ppk.decrypt(encrypted, Charsets.UTF_8);
        assertEquals(content, decrypted);
    }

    @Test
    public void testBuilder() {
        byte[] bytes = PPK.publicKey("/public.der").encrypt(content, Charsets.UTF_8);
        assertEquals(content, PPK.privateKey("/private.der").decrypt(bytes, Charsets.UTF_8));
    }

    @Test
    public void testBuilderUsingFiles() {
        byte[] bytes = PPK.publicKey(new File("src/test/resources/public.der")).encrypt(content,
                Charsets.UTF_8);
        assertEquals(content, PPK.privateKey(new File("src/test/resources/private.der"))
                .decrypt(bytes, Charsets.UTF_8));
    }

    @Test
    public void testBuilderUsingInputStream() {
        byte[] bytes = PPK.publicKey(new File("src/test/resources/public.der"))
                .encrypt(new ByteArrayInputStream(content.getBytes(Charsets.UTF_8)));
        assertEquals(content, PPK.privateKey(new File("src/test/resources/private.der"))
                .decrypt(bytes, Charsets.UTF_8));
    }

    @Test(expected = RuntimeException.class)
    public void testBuilderUsingPublicKeyFileThrowsExceptionWhenFileDoesNotExist() {
        PPK.publicKey(new File("src/test/resources/publicDoesNotExist.der"));
    }

    @Test(expected = RuntimeException.class)
    public void testBuilderUsingPrivateKeyFileThrowsExceptionWhenFileDoesNotExist() {
        PPK.privateKey(new File("src/test/resources/privateDoesNotExist.der"));
    }

    @Test
    public void testWithStream() {
        List<byte[]> list = Lists.newArrayList("hi".getBytes(), "there".getBytes());
        PPK ppk = PPK.publicKey("/public.der").build();
        List<byte[]> encrypted = list.stream().map(ppk::encrypt).collect(Collectors.toList());
        assertEquals(list.size(), encrypted.size());
    }

    @Test(expected = PublicKeyNotSetException.class)
    public void testPublicKeyNotSetThrowsException() {
        PPK.privateKey("/private.der").encrypt(content, Charsets.UTF_8);
    }

    @Test(expected = PrivateKeyNotSetException.class)
    public void testPrivateKeyNotSetThrowsException() {
        PPK.publicKey("/public.der").decrypt(content.getBytes(), Charsets.UTF_8);
    }

    @Test
    public void testRoundTrip() {
        PPK ppk = PPK.publicKey("/public.der").privateKey("/private.der").build();
        // result should be the same as bytes
        byte[] result = ppk.decrypt(ppk.encrypt(content.getBytes()));
        assertTrue(equal(content.getBytes(), result));
    }

    @Test
    public void testEncryptionLength() {
        String s = IntStream.range(0, 257).mapToObj(x -> "a").collect(Collectors.joining());
        System.out.println(s);
        System.out.println("Encryption length="
                + PPK.publicKey("/public.der").encrypt(s, Charsets.UTF_8).length);
    }

    private static boolean equal(byte[] a, byte[] b) {
        if (a == null && b == null)
            return true;

        if (a == null || b == null)
            return false;

        if (a.length != b.length)
            return false;

        for (int i = 0; i < a.length; i++) {
            if (a[i] != b[i])
                return false;
        }
        return true;
    }

}

@@ -3650,24 +3650,21 @@
 test
-EncryptionLength
+LongRoundTrip
 () %7B
@@ -3706,11 +3706,13 @@
 (0, 
-257
+10000
 ).ma
@@ -3771,135 +3771,171 @@
     
-System.out.println(s);%0A        System.out.println(%22Encryption length=%22%0A                + PPK.publicKey(%22/public.der%22).en
+PPK ppk = PPK.publicKey(%22/public.der%22).privateKey(%22/private.der%22).build();%0A        byte%5B%5D enc = ppk.encrypt(s, Charsets.UTF_8);%0A        String s2 = ppk.de
 crypt(
-s
+enc
 , Ch
@@ -3947,23 +3947,44 @@
 s.UTF_8)
-.length
+;%0A        assertEquals(s, s2
 );%0A    %7D

test long text round trip