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

import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;

import javax.enterprise.inject.Any;
import javax.enterprise.inject.Instance;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import org.slf4j.Logger;

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.ApplicationExceptionMessages;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.validation.ValidationErrorType;
import com.scytl.products.ov.commons.domain.model.rule.AbstractRule;
import com.scytl.products.ov.commons.domain.service.RuleExecutor;
import com.scytl.products.ov.commons.logging.service.VoteHashService;
import com.scytl.products.ov.commons.validation.ValidationError;
import com.scytl.products.ov.commons.validation.ValidationResult;
import com.scytl.products.ov.ei.services.domain.model.ballot.Ballot;
import com.scytl.products.ov.ei.services.domain.model.ballot.BallotElection;
import com.scytl.products.ov.ei.services.domain.model.ballot.BallotRepository;
import com.scytl.products.ov.ei.services.domain.model.ballot.Election;
import com.scytl.products.ov.ei.services.domain.model.validation.VoteValidationRepository;
import com.scytl.products.ov.ei.services.domain.service.vote.VoteValidationServiceImpl;

/**
 * Junit tests for the class {@link VoteValidationServiceImpl}.
 */
@RunWith(MockitoJUnitRunner.class)
public class VoteValidationServiceTest {

    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    @Mock
    private Vote voteMock;

    @Mock
    private BallotRepository ballotRepository;

    @Mock
    private RuleExecutor<Vote> ruleExecutor;

    @Mock
    @Any
    private Instance<AbstractRule<Vote>> ruleList;

    @InjectMocks
    @Spy
    private VoteValidationServiceImpl validationService;

    @Mock
    private Logger logger;

    @Mock
    private VoteHashService voteHashService;

    @Mock
    private VoteValidationRepository voteValidationRepository;

    private String tenantIdMock = "123";

    private final String electionEventIdMock = "1";

    private String ballotIdMock = "321";

    private final String electionIdMock = "1";

    private final String jsonEscapedBallotMock =
        new String("{\"id\":\"100\", \"elections\":[{\"id\":\"100\", \"type\":\"election\"}]}");

    private Ballot ballotMock;

    @Before
    public void setup() throws IOException {
        when(ruleList.iterator()).thenReturn(Collections.emptyIterator());

        ballotMock = new Ballot();
        ballotMock.setId(100);
        ballotMock.setBallotId("100");
        ballotMock.setTenantId("100");
        ballotMock.setJson(jsonEscapedBallotMock);

        BallotElection ballotElection = new BallotElection();
        HashSet<Election> electionsSet = new HashSet<Election>();
        Election election = new Election();
        election.setId(electionIdMock);
        electionsSet.add(election);
        ballotElection.setContests(electionsSet);
    }

    @Test
    public void validateVoteWithVoteNullThrowApplicationException()
            throws ApplicationException, ResourceNotFoundException {
        voteMock = null;
        expectedException.expect(ApplicationException.class);
        expectedException.expectMessage(ApplicationExceptionMessages.EXCEPTION_MESSAGE_VOTE_IS_NULL);

        validationService.validate(voteMock, tenantIdMock, electionEventIdMock, ballotIdMock);
    }

    @Test
    public void validateVoteWithtenantIdNullThrowApplicationException()
            throws ApplicationException, ResourceNotFoundException {
        tenantIdMock = null;
        expectedException.expect(ApplicationException.class);
        expectedException.expectMessage(ApplicationExceptionMessages.EXCEPTION_MESSAGE_TENANT_ID_IS_NULL);

        validationService.validate(voteMock, tenantIdMock, electionEventIdMock, ballotIdMock);
    }

    @Test
    public void validateVoteWithElectionIdNullThrowApplicationException()
            throws ApplicationException, ResourceNotFoundException {
        ballotIdMock = null;
        expectedException.expect(ApplicationException.class);
        expectedException.expectMessage(ApplicationExceptionMessages.EXCEPTION_MESSAGE_BALLOT_ID_IS_NULL);

        validationService.validate(voteMock, tenantIdMock, electionEventIdMock, ballotIdMock);
    }

    @Test
    public void validateVoteThrowRightApplicationException() throws ApplicationException, ResourceNotFoundException {
        when(ballotRepository.findByTenantIdElectionEventIdBallotId(tenantIdMock, electionEventIdMock, ballotIdMock))
            .thenReturn(ballotMock);
        final String exceptionMessage = "Test message";
        when(ruleExecutor.execute(any(), eq(voteMock))).thenThrow(new ApplicationException(exceptionMessage));
        expectedException.expect(ApplicationException.class);
        expectedException.expectMessage(exceptionMessage);

        validationService.validate(voteMock, tenantIdMock, electionEventIdMock, ballotIdMock);
    }

    @Test
    public void validateVoteFailedRulesListEmpty() throws ApplicationException, IOException, ResourceNotFoundException {
        when(ballotRepository.findByTenantIdElectionEventIdBallotId(tenantIdMock, electionEventIdMock, ballotIdMock))
            .thenReturn(ballotMock);
        ValidationError validationError = new ValidationError();
        validationError.setValidationErrorType(ValidationErrorType.SUCCESS);
        when(ruleExecutor.execute(any(), any(Vote.class))).thenReturn(validationError);

        ValidationResult result = validationService.validate(voteMock, tenantIdMock, electionEventIdMock, ballotIdMock);
        assertTrue(result.isResult());
    }

}
