/*
 * 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.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;

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

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Matchers;
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.confirmation.TraceableConfirmationMessage;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.exceptions.VoteCastCodeRepositoryException;
import com.scytl.products.ov.commons.beans.validation.CryptographicOperationException;
import com.scytl.products.ov.commons.dto.CastCodeAndComputeResults;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;
import com.scytl.products.ov.commons.validation.ValidationResult;

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

@RunWith(MockitoJUnitRunner.class)
public class VoteCastCodeRepositoryImplTest {

    private static final String AUTHENTICATION_TOKEN_SIGNATURE = "signature";

    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 TRACK_ID = "2";

    private final String PATH_CAST_CODE_VALUE = "castcodes";

    @Mock
    private TrackIdInstance trackId;

    @Mock
    private VerificationClient verificationClient;

    @Mock
    private ElectionInformationClient electionInformationClient;

    @Mock
    private Logger LOG;

    @InjectMocks
    VoteCastCodeRepositoryImpl rut = new VoteCastCodeRepositoryImpl(verificationClient, electionInformationClient);

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

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

    @Test
    public void testGenerateCastCodeSuccessful() throws CryptographicOperationException, IOException {
        TraceableConfirmationMessage confirmationMessage = new TraceableConfirmationMessage();
        confirmationMessage.setConfirmationKey("1");

        CastCodeAndComputeResults voteCastMessageMock = new CastCodeAndComputeResults();
        voteCastMessageMock.setSignature("signature");

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

        when(verificationClient.generateCastCode(Matchers.eq(TRACK_ID), Matchers.eq(PATH_CAST_CODE_VALUE),
            Matchers.eq(TENANT_ID), Matchers.eq(ELECTION_EVENT_ID), Matchers.eq(VERIFICATION_CARD_ID),
            Matchers.anyObject())).thenReturn(callMock);

        CastCodeAndComputeResults voteCastMessage = rut.generateCastCode(TENANT_ID, ELECTION_EVENT_ID,
            VERIFICATION_CARD_ID, VOTING_CARD_ID, AUTHENTICATION_TOKEN_SIGNATURE, confirmationMessage);

        assertEquals(voteCastMessageMock, voteCastMessage);
    }

    @Test(expected = CryptographicOperationException.class)
    public void testGenerateCastCodeRetrofitError() throws CryptographicOperationException, IOException {
        TraceableConfirmationMessage confirmationMessage = new TraceableConfirmationMessage();
        confirmationMessage.setConfirmationKey("1");

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

        when(verificationClient.generateCastCode(Matchers.eq(TRACK_ID), Matchers.eq(PATH_CAST_CODE_VALUE),
            Matchers.eq(TENANT_ID), Matchers.eq(ELECTION_EVENT_ID), Matchers.eq(VERIFICATION_CARD_ID),
            Matchers.anyObject())).thenReturn(callMock);

        rut.generateCastCode(TENANT_ID, ELECTION_EVENT_ID, VERIFICATION_CARD_ID, VOTING_CARD_ID,
            AUTHENTICATION_TOKEN_SIGNATURE, confirmationMessage);
    }

    @Test
    public void testGetCastcodeSuccessful() throws ResourceNotFoundException, IOException {
        CastCodeAndComputeResults voteCastMessageMock = new CastCodeAndComputeResults();
        voteCastMessageMock.setSignature("signature");

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

        when(electionInformationClient.getVoteCastCode(TRACK_ID, PATH_CAST_CODE_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

        CastCodeAndComputeResults voteCastMessage = rut.getCastCode(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);

        assertEquals(voteCastMessageMock, voteCastMessage);
    }

    @Test
    public void testStoresCastCodeSuccessful() throws ResourceNotFoundException, IOException {
        CastCodeAndComputeResults voteCastMessageMock = new CastCodeAndComputeResults();
        voteCastMessageMock.setSignature("signature");

        ValidationResult validationResultMock = new ValidationResult();
        validationResultMock.setResult(true);

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

        when(electionInformationClient.storeCastCode(TRACK_ID, PATH_CAST_CODE_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID, voteCastMessageMock)).thenReturn(callMock);

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

        assertEquals(validationResultMock.isResult(), result);
    }

    @Test
    public void testVoteCastCodeExistsSuccessful() throws IOException, VoteCastCodeRepositoryException {
        @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.checkVoteCastCode(TRACK_ID, PATH_CAST_CODE_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

        boolean result = rut.voteCastCodeExists(TENANT_ID, ELECTION_EVENT_ID, VOTING_CARD_ID);
        assertEquals(true, result);
    }

    @Test
    public void testVoteCastCodeExists404NotFound() throws IOException, VoteCastCodeRepositoryException {
        @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.checkVoteCastCode(TRACK_ID, PATH_CAST_CODE_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

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

        assertEquals(false, result);
    }

    @Test(expected = Exception.class)
    public void testVoteCastCodeExists500Error() throws IOException, VoteCastCodeRepositoryException {
        @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.checkVoteCastCode(TRACK_ID, PATH_CAST_CODE_VALUE, TENANT_ID, ELECTION_EVENT_ID,
            VOTING_CARD_ID)).thenReturn(callMock);

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

}
