/*
 * 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.channel.cc.mixing.service;

import java.io.IOException;
import java.security.KeyManagementException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.elgamal.bean.ElGamalKeyPair;
import com.scytl.products.ov.channel.cc.commons.keymanagement.KeyManager;
import com.scytl.products.ov.commons.dto.MixingDTO;
import com.scytl.products.ov.commons.dto.mixing.MixingPayload;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestConfig.class)
public class InternalMixDecryptServiceImplTest {

    @Autowired
    private ElGamalKeyPair electoralAuthorityKeyPair;

    @Value("${queues.md-mixdec.req}")
    private String queueName;

    @Autowired
    private InternalMixDecryptService sut;

    @Autowired
    private KeyManager keyManager;

    private TestUtil testUtil;

    @Before
    public void setUp() throws IOException, GeneralCryptoLibException {
        testUtil = new TestUtil(sut, queueName, keyManager, electoralAuthorityKeyPair);
    }

    @Test
    public void testConcurrentMixing() throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        // Simulate more ballot boxes than threads to enqueue the mixings.
        IntStream.rangeClosed(1, 10).parallel().forEach(index -> executor.execute(() -> {
            try {
                MixingDTO dto = testUtil.createMixingDTO(10);
                MixingPayload initialPayload = dto.getPayload();
                testUtil.executeMixing(dto);
                testUtil.testResults(dto, initialPayload);
            } catch (KeyManagementException | GeneralCryptoLibException | IOException e) {
                throw new RuntimeException(e);
            }
        }));

        // Do not accept any more tasks.
        executor.shutdown();
        // Wait for the threads to finish their tasks before letting the test
        // end.
        executor.awaitTermination(5, TimeUnit.MINUTES);
    }

    @Test
    public void test0VoteMixing() throws KeyManagementException, GeneralCryptoLibException, IOException {
        MixingDTO dto = testUtil.createMixingDTO(0);
        MixingPayload initialPayload = dto.getPayload();
        testUtil.executeMixing(dto);
        testUtil.testResults(dto, initialPayload);
    }

    @Test
    public void test1VoteMixing() throws KeyManagementException, GeneralCryptoLibException, IOException {
        MixingDTO dto = testUtil.createMixingDTO(1);
        MixingPayload initialPayload = dto.getPayload();
        testUtil.executeMixing(dto);
        testUtil.testResults(dto, initialPayload);
    }

    @Test
    public void test2VoteMixing() throws KeyManagementException, GeneralCryptoLibException, IOException {
        MixingDTO dto = testUtil.createMixingDTO(2);
        MixingPayload initialPayload = dto.getPayload();
        testUtil.executeMixing(dto);
        testUtil.testResults(dto, initialPayload);
    }

    @Test
    public void test3VoteMixing() throws KeyManagementException, GeneralCryptoLibException, IOException {
        MixingDTO dto = testUtil.createMixingDTO(3);
        MixingPayload initialPayload = dto.getPayload();
        testUtil.executeMixing(dto);
        testUtil.testResults(dto, initialPayload);
    }
}
