/*
 * 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.orchestrator.mixdec.domain.services;

import static java.util.Collections.singletonList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;
import org.slf4j.Logger;

import com.scytl.products.ov.channel.cc.orchestrator.commons.config.QueuesConfig;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.model.BallotBoxStatus;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.model.MixDecBallotBox;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.model.MixDecBallotBoxStatus;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.model.MixDecNodeOutput;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.domain.model.MixDecNodeOutputId;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.infrastructure.persistence.MixDecBallotBoxRepository;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.infrastructure.persistence.MixDecBallotBoxStatusRepository;
import com.scytl.products.ov.channel.cc.orchestrator.mixdec.infrastructure.persistence.MixDecNodeOutputRepository;
import com.scytl.products.ov.commons.beans.domain.model.EntityId;
import com.scytl.products.ov.commons.beans.domain.model.ballotbox.BallotBoxId;
import com.scytl.products.ov.commons.beans.domain.model.ballotbox.BallotBoxIdImpl;
import com.scytl.products.ov.commons.beans.domain.model.vote.VoteSetId;
import com.scytl.products.ov.commons.beans.domain.model.vote.VoteSetIdImpl;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;

@RunWith(MockitoJUnitRunner.class)
public class MixDecBallotBoxServiceTest {

    private static final String PAYLOAD_JSON = "{}";

    @Mock
    private MixDecBallotBoxRepository mixDecBallotBoxRepository;

    @Mock
    private MixDecBallotBoxStatusRepository mixDecBallotBoxStatusRepository;

    @Mock
    private MixDecNodeOutputRepository nodeOutputRepository;

    @Mock
    private Logger logger;

    @InjectMocks
    private static final MixDecBallotBoxServiceImpl sut = new MixDecBallotBoxServiceImpl();

    private String ccQueueNames;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(getClass());
        ccQueueNames = System.getProperty("cc_queue_names");
        System.setProperty("cc_queue_names",
            "{\"g4\": {\"cg-comp\": {\"res\": \"cg-comp-g4-res\", \"req\": \"cg-comp-g4-req\"}, \"cg-keygen\": {\"res\": \"cg-keygen-g4-res\", \"req\": \"cg-keygen-g4-req\"}}, \"g3\": {\"cg-comp\": {\"res\": \"cg-comp-g3-res\", \"req\": \"cg-comp-g3-req\"}, \"cg-keygen\": {\"res\": \"cg-keygen-g3-res\", \"req\": \"cg-keygen-g3-req\"}}, \"g2\": {\"cg-comp\": {\"res\": \"cg-comp-g2-res\", \"req\": \"cg-comp-g2-req\"}, \"cg-keygen\": {\"res\": \"cg-keygen-g2-res\", \"req\": \"cg-keygen-g2-req\"}}, \"g1\": {\"cg-comp\": {\"res\": \"cg-comp-g1-res\", \"req\": \"cg-comp-g1-req\"}, \"cg-keygen\": {\"res\": \"cg-keygen-g1-res\", \"req\": \"cg-keygen-g1-req\"}}, \"v1\": {\"cv-comp\": {\"res\": \"cv-comp-v1-res\", \"req\": \"cv-comp-v1-req\"}, \"cv-dec\": {\"res\": \"cv-dec-v1-res\", \"req\": \"cv-dec-v1-req\"}}, \"v2\": {\"cv-comp\": {\"res\": \"cv-comp-v2-res\", \"req\": \"cv-comp-v2-req\"}, \"cv-dec\": {\"res\": \"cv-dec-v2-res\", \"req\": \"cv-dec-v2-req\"}}, \"v3\": {\"cv-comp\": {\"res\": \"cv-comp-v3-res\", \"req\": \"cv-comp-v3-req\"}, \"cv-dec\": {\"res\": \"cv-dec-v3-res\", \"req\": \"cv-dec-v3-req\"}}, \"m1\": {\"md-keygen\": {\"res\": \"md-keygen-m1-res\", \"req\": \"md-keygen-m1-req\"}, \"md-mixdec\": {\"res\": \"md-mixdec-m1-res\", \"req\": \"md-mixdec-m1-req\"}}, \"m3\": {\"md-keygen\": {\"res\": \"md-keygen-m3-res\", \"req\": \"md-keygen-m3-req\"}, \"md-mixdec\": {\"res\": \"md-mixdec-m3-res\", \"req\": \"md-mixdec-m3-req\"}}, \"m2\": {\"md-keygen\": {\"res\": \"md-keygen-m2-res\", \"req\": \"md-keygen-m2-req\"}, \"md-mixdec\": {\"res\": \"md-mixdec-m2-res\", \"req\": \"md-mixdec-m2-req\"}}, \"v4\": {\"cv-comp\": {\"res\": \"cv-comp-v4-res\", \"req\": \"cv-comp-v4-req\"}, \"cv-dec\": {\"res\": \"cv-dec-v4-res\", \"req\": \"cv-dec-v4-req\"}}}");
    }

    @After
    public void tearDown() {
        if (ccQueueNames == null) {
            System.clearProperty("cc_queue_names");
        } else {
            System.setProperty("cc_queue_names", ccQueueNames);
        }
    }

    @Test
    @Deprecated // SV-5291
    public void testGetProcessedBallotBoxHappyPath() {
        List<MixDecBallotBox> votes = singletonList(new MixDecBallotBox());
        when(mixDecBallotBoxRepository.findByTenantIdElectionEventIdBallotBoxId(anyString(), anyString(), anyString()))
            .thenReturn(votes);
        assertThat(sut.getProcessedBallotBox(anyString(), anyString(), anyString()), is(votes));
    }

    @Test
    public void testBlankBallotBox() throws ResourceNotFoundException {
        String tenantId = "tenant";
        String electionEventId = "election_event";
        String ballotBoxId = "ballot_box";
        EntityId ballotBoxEntityId = new EntityId();
        ballotBoxEntityId.setId(ballotBoxId);
        List<EntityId> ballotBoxIds = Collections.singletonList(ballotBoxEntityId);
        String trackingId = "tracking";

        // Blank ballot box
        when(mixDecBallotBoxRepository.findByTenantIdElectionEventIdBallotBoxId(tenantId, electionEventId, ballotBoxId))
            .thenReturn(Collections.emptyList());

        MixDecBallotBoxStatus mixDecBallotBoxStatus = mock(MixDecBallotBoxStatus.class);
        when(mixDecBallotBoxStatus.getStatus()).thenReturn(BallotBoxStatus.STATUS_CLOSED);

        when(mixDecBallotBoxStatusRepository.getMixDecBallotBoxStatus(tenantId, electionEventId, ballotBoxId))
            .thenReturn(mixDecBallotBoxStatus);

        Assert.assertNotNull(QueuesConfig.MIX_DEC_COMPUTATION_REQ_QUEUES);

        sut.processBallotBoxes(tenantId, electionEventId, ballotBoxIds, trackingId);
    }

    @Test
    public void testGetOutputPayloadsJson() {
        final BallotBoxId bbid = new BallotBoxIdImpl("tenant", "election", "ballotBox");
        final int voteSetCount = 10;
        // Return a few payloads
        List<String> nodeNames = Arrays.asList("m1", "m2", "m3");
        when(nodeOutputRepository.findForAllNodes(bbid.getTenantId(), bbid.getElectionEventId(), bbid.getId()))
            .thenReturn(getMockNodeOutputs(bbid, nodeNames, voteSetCount));

        List<PayloadJson> payloadJsonItems = sut.getOutputPayloadsJson(bbid).collect(Collectors.toList());

        // Check that the expected number of payloads has been generated.
        assertEquals(voteSetCount * nodeNames.size(), payloadJsonItems.size());

        PayloadJson payloadJson = payloadJsonItems.get(0);
        // Check the contents of the first payload.
        assertEquals(PAYLOAD_JSON, payloadJson.getContents());
        // Check the file name of the first payload.
        assertEquals(String.format("%s-%d-%s.json", bbid, 0, nodeNames.get(0)), payloadJson.getFileName());
    }

    private List<MixDecNodeOutput> getMockNodeOutputs(final BallotBoxId bbid, Collection<String> nodeNames,
            int voteSetCount) {
        List<MixDecNodeOutput> items = new ArrayList<>();

        // For each node...
        nodeNames.stream().forEach(nodeName -> {
            // ... and for each vote set...
            for (int i = 0; i < voteSetCount; i++) {
                // ... create a payload.
                VoteSetId voteSetId = new VoteSetIdImpl(bbid, i);
                MixDecNodeOutputId id = new MixDecNodeOutputId(voteSetId, nodeName);
                items.add(new MixDecNodeOutput(id, PAYLOAD_JSON));
            }
        });

        return items;
    }

}
