/*
 * 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.vw.services.domain.service;

import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.ov.commons.beans.domain.model.vote.Vote;
import com.scytl.products.ov.commons.beans.exceptions.ApplicationException;
import com.scytl.products.ov.commons.beans.exceptions.DuplicateEntryException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.dto.ChoiceCodeAndComputeResults;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;
import com.scytl.products.ov.vw.infrastructure.log.SecureLoggerInformation;
import com.scytl.products.ov.vw.services.domain.model.choicecode.ChoiceCodeRepository;
import com.scytl.products.ov.vw.services.domain.model.state.VotingCardState;
import com.scytl.products.ov.vw.services.domain.model.state.VotingCardStates;
import com.scytl.products.ov.vw.services.domain.model.validation.ValidationRepository;
import com.scytl.products.ov.vw.services.domain.model.vote.ValidationVoteResult;
import com.scytl.products.ov.vw.services.domain.model.vote.VoteRepository;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.BeforeClass;
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 java.io.IOException;
import java.security.Security;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class VoteServiceTest {

    @InjectMocks
    private VoteService sut = new VoteServiceImpl();

    @Mock
    private VotingCardStateService votingCardStateService;

    @Mock
    private ChoiceCodeRepository choiceCodeRepository;

    @Mock
    private ValidationRepository validationRepository;

    @Mock
    private SecureLoggerInformation secureLoggerInformation;

    @Mock
    private SecureLoggingWriter secureLoggerWriter;

    @Mock
    private VoteRepository voteRepository;

    @Mock
    private TrackIdInstance trackId;

    @Mock
    private Logger LOG;

    private final String TENANT_ID = "100";

    private final String ELECTION_EVENT_ID = "1";

    private final String VOTING_CARD_ID = "3cc7e2a0dd394fae8d9bd2ebd4fa4b95";

    private final String VERIFICATION_CARD_ID = "4dffac3879e443d3a3634929f6a2eb07";

    private final String CHOICE_CODES = "THECHOICECODES";
    
    private final String CHOICE_CODES_COMPUTATION_RESULTS = "THECOMPUTATIONRESULTS";
    
    private final String CHOICE_CODES_DECRYPTION_RESULTS = "THEDECRYPTIONRESULTS";

    @BeforeClass
    public static void setup() {
        MockitoAnnotations.initMocks(VoteCastCodeServiceTest.class);
        Security.addProvider(new BouncyCastleProvider());
    }

    @Test
    public void testValidateAndStoreVoteHappyPath()
            throws ApplicationException, IOException, DuplicateEntryException, ResourceNotFoundException {

        VotingCardState votingCardState = new VotingCardState();
        votingCardState.setState(VotingCardStates.NOT_SENT);

        ChoiceCodeAndComputeResults choiceCode = new ChoiceCodeAndComputeResults();
        choiceCode.setChoiceCodes(CHOICE_CODES);
        choiceCode.setComputationResults(CHOICE_CODES_COMPUTATION_RESULTS);
        choiceCode.setDecryptionResults(CHOICE_CODES_DECRYPTION_RESULTS);

        ValidationVoteResult successValidationVoteResult = new ValidationVoteResult();
        successValidationVoteResult.setValid(true);

        Vote vote = mock(Vote.class);

        when(votingCardStateService.getVotingCardState(anyString(), anyString(), anyString()))
            .thenReturn(votingCardState);

        when(validationRepository.validateVote(anyString(), anyString(), any()))
            .thenReturn(successValidationVoteResult);

        when(choiceCodeRepository.generateChoiceCodes(anyString(), anyString(), anyString(), any()))
            .thenReturn(choiceCode);

        ValidationVoteResult validationVoteResult =
            sut.validateVoteAndStore(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID, VERIFICATION_CARD_ID, vote, "");

        assertThat(validationVoteResult.isValid(), is(true));

    }

    @Test
    public void testValidateAndStoreVoteWhenVotingCardIsBlockedAndGetNotValidResult()
            throws ApplicationException, IOException, DuplicateEntryException, ResourceNotFoundException {

        VotingCardState votingCardState = new VotingCardState();
        votingCardState.setState(VotingCardStates.BLOCKED);

        Vote vote = mock(Vote.class);

        when(votingCardStateService.getVotingCardState(anyString(), anyString(), anyString()))
            .thenReturn(votingCardState);

        ValidationVoteResult validationVoteResult =
            sut.validateVoteAndStore(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID, VERIFICATION_CARD_ID, vote, "");

        String[] errorArgs = validationVoteResult.getValidationError().getErrorArgs();

        assertThat(validationVoteResult.isValid(), is(false));
        assertThat((VotingCardStates.BLOCKED.name()), is(errorArgs[0]));

    }

    @Test
    public void testValidateAndStoreVoteWhenVotingCardDidNotVoteAndGetValidResult()
            throws ApplicationException, IOException, DuplicateEntryException, ResourceNotFoundException {

        VotingCardState votingCardState = new VotingCardState();
        votingCardState.setState(VotingCardStates.NOT_SENT);

        Vote vote = mock(Vote.class);

        ChoiceCodeAndComputeResults choiceCode = new ChoiceCodeAndComputeResults();
        choiceCode.setChoiceCodes(CHOICE_CODES);
        choiceCode.setComputationResults(CHOICE_CODES_COMPUTATION_RESULTS);
        choiceCode.setDecryptionResults(CHOICE_CODES_DECRYPTION_RESULTS);

        ValidationVoteResult successValidationVoteResult = new ValidationVoteResult();
        successValidationVoteResult.setValid(true);

        when(validationRepository.validateVote(anyString(), anyString(), any()))
            .thenReturn(successValidationVoteResult);
        when(choiceCodeRepository.generateChoiceCodes(anyString(), anyString(), anyString(), any()))
            .thenReturn(choiceCode);
        when(votingCardStateService.getVotingCardState(anyString(), anyString(), anyString()))
            .thenReturn(votingCardState);

        ValidationVoteResult validationVoteResult =
            sut.validateVoteAndStore(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID, VERIFICATION_CARD_ID, vote, "");

        assertThat(validationVoteResult.isValid(), is(true));

    }

    @Test
    public void testValidateAndStoreVoteWhenVotingCardDidNotVoteAndGetNotValidResult()
            throws ApplicationException, IOException, DuplicateEntryException, ResourceNotFoundException {

        VotingCardState votingCardState = new VotingCardState();
        votingCardState.setState(VotingCardStates.NOT_SENT);

        Vote vote = mock(Vote.class);

        ChoiceCodeAndComputeResults choiceCode = new ChoiceCodeAndComputeResults();
        choiceCode.setChoiceCodes(CHOICE_CODES);

        ValidationVoteResult successValidationVoteResult = new ValidationVoteResult();
        successValidationVoteResult.setValid(false);

        when(validationRepository.validateVote(anyString(), anyString(), any()))
            .thenReturn(successValidationVoteResult);
        when(choiceCodeRepository.generateChoiceCodes(anyString(), anyString(), anyString(), any()))
            .thenReturn(choiceCode);
        when(votingCardStateService.getVotingCardState(anyString(), anyString(), anyString()))
            .thenReturn(votingCardState);

        ValidationVoteResult validationVoteResult =
            sut.validateVoteAndStore(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID, VERIFICATION_CARD_ID, vote, "");

        assertThat(validationVoteResult.isValid(), is(false));

    }

    @Test
    public void testValidateAndStoreVoteWhenVotingCardAlreadyVotedAndGetNotValidResult()
            throws ApplicationException, IOException, DuplicateEntryException, ResourceNotFoundException {

        VotingCardState votingCardState = new VotingCardState();
        votingCardState.setState(VotingCardStates.CAST);

        Vote vote = mock(Vote.class);

        when(votingCardStateService.getVotingCardState(anyString(), anyString(), anyString()))
            .thenReturn(votingCardState);

        ValidationVoteResult validationVoteResult =
            sut.validateVoteAndStore(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID, VERIFICATION_CARD_ID, vote, "");

        assertThat(validationVoteResult.isValid(), is(false));

    }

}
