/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.sdm.config.spring.batch.writers;

import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.batch.item.ExecutionContext;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.scytl.cryptolib.elgamal.bean.ElGamalEncryptionParameters;
import com.scytl.cryptolib.elgamal.bean.ElGamalKeyPair;
import com.scytl.cryptolib.elgamal.service.ElGamalService;
import com.scytl.cryptolib.primitives.service.PrimitivesService;
import com.scytl.products.ov.commons.beans.VerificationCardPublicKeyAndSignature;
import com.scytl.products.ov.config.commands.voters.datapacks.beans.VerificationCardCredentialDataPack;
import com.scytl.products.ov.sdm.config.spring.batch.GeneratedVotingCardOutput;
import com.scytl.products.ov.utils.FingerprintGenerator;

public class VerificationCardDataWriterTest {

    private ObjectMapper jsonMapper = new ObjectMapper();

    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();

    // this is to document what the exact format we are expecting the writer
    // writes
    // verificationCardId,
    // verificationCardSerializedKeyStoreB64,serializedVerCardBeanB64,
    // electionEventId,
    // verificationCardSetId

    private String verificationCardId = "verificationCardId";

    private String verificationCardSerializedKeyStoreB64 = "{}";

    private String electionEventId = "electionEventId";

    private String verificationCardSetId = "verificationCardSetId";

    private int expectedFormatLength = "%s,%s,%s,%s,%s".split(",").length;

    // cant mock final class
    private FingerprintGenerator fingerprintGenerator;

    private byte[] randomByteArray = new byte[16];

    private ElGamalKeyPair elGamalKeyPair;

    @Before
    public void setup() throws Exception {

        ElGamalEncryptionParameters params =
            new ElGamalEncryptionParameters(new BigInteger("23"), new BigInteger("11"), new BigInteger("2"));
        elGamalKeyPair = new ElGamalService().getElGamalKeyPairGenerator().generateKeys(params, 1);

        new Random().nextBytes(randomByteArray);

        fingerprintGenerator = new FingerprintGenerator(new PrimitivesService());
    }

    @Test
    public void generateOutputFileWithCorrectDataFormat() throws Exception {

        Path tempPath = tempFolder.newFile().toPath();

        VerificationCardDataWriter sut = new VerificationCardDataWriter(tempPath, fingerprintGenerator,
            (level, logContent) -> System.out.println("ignore log message"));

        sut.open(new ExecutionContext(Collections.emptyMap()));

        // for later verification
        String signatureBase64 = Base64.getEncoder().encodeToString(randomByteArray);
        VerificationCardPublicKeyAndSignature original = new VerificationCardPublicKeyAndSignature();
        String verificationCardPublicKeyB64 = Base64.getEncoder()
            .encodeToString(elGamalKeyPair.getPublicKeys().toJson().getBytes(StandardCharsets.UTF_8));
        original.setPublicKey(verificationCardPublicKeyB64);
        original.setSignature(signatureBase64);

        List<GeneratedVotingCardOutput> items = new ArrayList<>();
        items.add(createOutput());
        items.add(createOutput());
        items.add(createOutput());

        sut.write(items);

        final List<String> strings = Files.readAllLines(tempPath);
        Assert.assertEquals(items.size(), strings.size());

        strings.forEach((String l) -> {
            final String[] columns = l.split(",");
            Assert.assertEquals(expectedFormatLength, columns.length);
            Assert.assertEquals(verificationCardId, columns[0]);
            Assert.assertEquals(verificationCardSerializedKeyStoreB64, columns[1]);

            try {
                String json = new String(Base64.getDecoder().decode(columns[2]));
                VerificationCardPublicKeyAndSignature deserialized =
                    jsonMapper.readValue(json, VerificationCardPublicKeyAndSignature.class);

                Assert.assertEquals(original.getPublicKey(), deserialized.getPublicKey());
                Assert.assertEquals(original.getSignature(), deserialized.getSignature());

            } catch (IOException e) {
                fail("invalid format");
            }

            Assert.assertEquals(electionEventId, columns[3]);
            Assert.assertEquals(verificationCardSetId, columns[4]);
        });
    }

    private GeneratedVotingCardOutput createOutput() throws Exception {

        VerificationCardCredentialDataPack verificationCardCredentialDataPack =
            mock(VerificationCardCredentialDataPack.class);
        when(verificationCardCredentialDataPack.getSerializedKeyStore()).thenReturn("{}");
        when(verificationCardCredentialDataPack.getSignatureVCardPubKeyEEIDVCID()).thenReturn(randomByteArray);
        when(verificationCardCredentialDataPack.getVerificationCardKeyPair()).thenReturn(elGamalKeyPair);

        return GeneratedVotingCardOutput.success(null, null, null, null, null, electionEventId, verificationCardId,
            verificationCardSetId, null, null, verificationCardCredentialDataPack, null, null);
    }

}
