/*
 * 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.infrastructure.remote;

import static org.mockito.Mockito.when;

import java.io.IOException;
import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.slf4j.Logger;

import com.scytl.products.ov.commons.beans.domain.model.receipt.Receipt;
import com.scytl.products.ov.commons.beans.exceptions.ApplicationException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.exceptions.VoteRepositoryException;
import com.scytl.products.ov.commons.dto.VoteAndComputeResults;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;

import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;

@RunWith(MockitoJUnitRunner.class)
public class VoteRepositoryImplTest {

    private final String TENANT_ID = "100";

    private final String ELECTION_EVENT_ID = "1";

    private final String VOTING_CARD_ID = "1";

    private final String TRACK_ID = "1";

    private final VoteAndComputeResults VOTE = new VoteAndComputeResults();

    // has to correspond with Application.properties:PATH_VOTES
    private final String PATH_VOTES_VALUE = "votes";

    private final String PATH_RECEIPTS_VALUE = "receipts";

    @Mock
    private TrackIdInstance trackId;

    @Mock
    private ElectionInformationClient electionInformationClient;

    // Instance of the secure logger
    @Mock
    private Logger LOG;

    @InjectMocks
    VoteRepositoryImpl rut = new VoteRepositoryImpl(electionInformationClient);

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

    @Before
    public void init() {
        when(trackId.getTrackId()).thenReturn(TRACK_ID);
    }

    @Test
    public void testfindVotingCardIdSuccessful()
            throws ResourceNotFoundException, IOException, VoteRepositoryException {
        @SuppressWarnings("unchecked")
        Call<VoteAndComputeResults> callMock = Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.success(VOTE));

        when(
            electionInformationClient.getVote(TRACK_ID, PATH_VOTES_VALUE, TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID))
                .thenReturn(callMock);

        VoteAndComputeResults vote =
            rut.findByTenantIdElectionEventIdVotingCardId(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);

        Assert.assertEquals(VOTE, vote);
    }

    @Test(expected = ResourceNotFoundException.class)
    public void testfindVotingCardIdError404() throws ResourceNotFoundException, IOException, VoteRepositoryException {
        @SuppressWarnings("unchecked")
        Call<VoteAndComputeResults> callMock = Mockito.mock(Call.class);
        when(callMock.execute())
            .thenReturn(Response.error(404, ResponseBody.create(okhttp3.MediaType.parse("text/html"), new byte[0])));

        when(
            electionInformationClient.getVote(TRACK_ID, PATH_VOTES_VALUE, TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID))
                .thenReturn(callMock);

        rut.findByTenantIdElectionEventIdVotingCardId(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);
    }

    @Test(expected = Exception.class)
    public void testfindVotingCardIdError500() throws ResourceNotFoundException, IOException, VoteRepositoryException {
        @SuppressWarnings("unchecked")
        Call<VoteAndComputeResults> callMock = Mockito.mock(Call.class);
        when(callMock.execute())
            .thenReturn(Response.error(500, ResponseBody.create(okhttp3.MediaType.parse("text/html"), new byte[0])));

        when(
            electionInformationClient.getVote(TRACK_ID, PATH_VOTES_VALUE, TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID))
                .thenReturn(callMock);

        rut.findByTenantIdElectionEventIdVotingCardId(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);
    }

    @Test
    public void testGetReceiptSuccessful()
            throws ResourceNotFoundException, ApplicationException, IOException, VoteRepositoryException {
        Receipt receiptMock = new Receipt();
        receiptMock.setReceipt("1");
        receiptMock.setSignature("2");

        @SuppressWarnings("unchecked")
        Call<Receipt> callMock = Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.success(receiptMock));

        when(electionInformationClient.getReceipt(TRACK_ID, PATH_RECEIPTS_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

        Receipt receipt = rut.getReceiptByVotingCardId(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);

        Assert.assertEquals(receiptMock, receipt);
    }

    @Test(expected = ResourceNotFoundException.class)
    public void testGetReceiptError404()
            throws ResourceNotFoundException, ApplicationException, IOException, VoteRepositoryException {
        @SuppressWarnings("unchecked")
        Call<Receipt> callMock = Mockito.mock(Call.class);
        when(callMock.execute())
            .thenReturn(Response.error(404, ResponseBody.create(okhttp3.MediaType.parse("text/html"), new byte[0])));

        when(electionInformationClient.getReceipt(TRACK_ID, PATH_RECEIPTS_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

        rut.getReceiptByVotingCardId(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);
    }

    @Test(expected = ApplicationException.class)
    public void testGetReceiptError422()
            throws ResourceNotFoundException, ApplicationException, IOException, VoteRepositoryException {
        @SuppressWarnings("unchecked")
        Call<Receipt> callMock = Mockito.mock(Call.class);
        when(callMock.execute())
            .thenReturn(Response.error(422, ResponseBody.create(okhttp3.MediaType.parse("text/html"), new byte[0])));

        when(electionInformationClient.getReceipt(TRACK_ID, PATH_RECEIPTS_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

        rut.getReceiptByVotingCardId(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);
    }

    @Test(expected = Exception.class)
    public void testGetReceiptError500()
            throws ResourceNotFoundException, ApplicationException, IOException, VoteRepositoryException {
        @SuppressWarnings("unchecked")
        Call<Receipt> callMock = Mockito.mock(Call.class);
        when(callMock.execute())
            .thenReturn(Response.error(500, ResponseBody.create(okhttp3.MediaType.parse("text/html"), new byte[0])));

        when(electionInformationClient.getReceipt(TRACK_ID, PATH_RECEIPTS_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

        rut.getReceiptByVotingCardId(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);
    }

    @Test
    public void testSaveSuccessful() throws ApplicationException, IOException, ResourceNotFoundException {
        VoteAndComputeResults voteMock = new VoteAndComputeResults();
        Receipt receipt = new Receipt();
        @SuppressWarnings("unchecked")
        Call<Receipt> callMock = Mockito.mock(Call.class);

        when(callMock.execute()).thenReturn(Response.success(receipt));
        String authenticationTokenJsonString = "";

        when(electionInformationClient.saveVote(trackId.getTrackId(), PATH_VOTES_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            voteMock, authenticationTokenJsonString)).thenReturn(callMock);
        rut.save(TENANT_ID, ELECTION_EVENT_ID, voteMock, authenticationTokenJsonString);
    }

    @Test
    public void testVoteExistsSuccessful() throws IOException, VoteRepositoryException {
        @SuppressWarnings("unchecked")
        Call<ResponseBody> callMock = Mockito.mock(Call.class);
        when(callMock.execute())
            .thenReturn(Response.success(ResponseBody.create(okhttp3.MediaType.parse("text/html"), new byte[0])));

        when(electionInformationClient.checkVote(trackId.getTrackId(), PATH_VOTES_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

        boolean result = rut.voteExists(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);

        Assert.assertTrue(result);
    }

    @Test
    public void testVoteExists404NotFound() throws IOException, VoteRepositoryException {
        @SuppressWarnings("unchecked")
        Call<ResponseBody> callMock = Mockito.mock(Call.class);
        when(callMock.execute())
            .thenReturn(Response.error(404, ResponseBody.create(okhttp3.MediaType.parse("text/html"), new byte[0])));

        when(electionInformationClient.checkVote(trackId.getTrackId(), PATH_VOTES_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

        boolean result = rut.voteExists(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);

        Assert.assertFalse(result);
    }

    @Test(expected = Exception.class)
    public void testVoteExists500Error() throws IOException, VoteRepositoryException {
        @SuppressWarnings("unchecked")
        Call<ResponseBody> callMock = Mockito.mock(Call.class);
        when(callMock.execute())
            .thenReturn(Response.error(500, ResponseBody.create(okhttp3.MediaType.parse("text/html"), new byte[0])));

        when(electionInformationClient.checkVote(trackId.getTrackId(), PATH_VOTES_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

        rut.voteExists(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);
    }
}
