/*
 * 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.ag.ui.ws.rs.operation.admin;

import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.RestoreSystemProperties;
import org.junit.rules.TestRule;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.slf4j.Logger;

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

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.scytl.products.ov.ag.services.infrastructure.remote.admin.KeyTranslationAdminClient;
import com.scytl.products.ov.commons.beans.domain.model.keytranslation.ElectionEventTranslations;
import com.scytl.products.ov.commons.beans.domain.model.keytranslation.KeyTranslation;
import com.scytl.products.ov.commons.beans.domain.model.keytranslation.VotingCardTranslation;
import com.scytl.products.ov.commons.beans.domain.model.keytranslation.VotingCardTranslationList;
import com.scytl.products.ov.commons.infrastructure.remote.client.RestClientInterceptor;
import com.scytl.products.ov.commons.tracking.TrackIdGenerator;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;

public class KeyTranslationResourceTest extends JerseyTest {

    private static final String ALIAS = "alias";

    private static final String EE_ID = "1e";

    private static final String BB_ID = "1b";
    
    private static final String TRACK_ID = "trackId";

    private static final String TENANT_ID = "100t";

    private static final String X_FORWARDER_VALUE = ",";

    private static final String SIGNATURE = "signature";

    private static final String ORIGINATOR = "originator";
    
    private static final String BASE = KeyTranslationResource.RESOURCE_PATH;

    private static final String URL_GET_EE_ID_CONTEXT_DATA = BASE + KeyTranslationResource.GET_ELECTION_EVENT_ID;
    
    private static final String URL_GET_EE_ALIAS_CONTEXT_DATA = BASE + KeyTranslationResource.GET_ELECTION_EVENT_ALIAS;
    
    private static final String URL_GET_VC_ALIAS_CONTEXT_DATA = BASE + KeyTranslationResource.GET_VOTING_CARD_ID;

    private static final String URL_GET_BB_ID_CONTEXT_DATA = BASE + KeyTranslationResource.GET_BALLOT_BOX_TRANSLATION_BY_ALIAS;
    
    private static final String URL_GET_BB_ALIAS_CONTEXT_DATA = BASE + KeyTranslationResource.GET_BALLOT_BOX_TRANSLATION_BY_ID;

    private static final String URL_SAVE_EE_CONTEXT_DATA = BASE + KeyTranslationResource.UPLOAD_ELECTION_EVENT_TRANSLATION;
    
    private static final String URL_SAVE_VC_CONTEXT_DATA = BASE + KeyTranslationResource.UPLOAD_VOTING_CARD_TRANSLATION;
    
    private static final String URL_SAVE_BB_CONTEXT_DATA = BASE + KeyTranslationResource.UPLOAD_BALLOT_BOX_TRANSLATION;
    
    @Rule
    public TestRule restoreSystemProperties = new RestoreSystemProperties();

    @Mock
    Logger logger;
    
    @Mock
    TrackIdGenerator trackIdGenerator;

    @Mock
    HttpServletRequest servletRequest;

    @Mock
    KeyTranslationAdminClient keyTranslationAdminClient;

    KeyTranslationResource sut;

    @Test
    public void getElectionEventId() throws IOException {

        int mockedInvocationStatus = 200;
        String expectedResponse = "{\"ee\" : \"alias\"}";
        
        @SuppressWarnings("unchecked")
		Call<ResponseBody> callMock = (Call<ResponseBody>) Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.success(ResponseBody.create(okhttp3.MediaType.parse("application/json"), expectedResponse.getBytes())));

        commonPreparation();
        when(keyTranslationAdminClient.getElectionEventTranslation(eq(KeyTranslationResource.PATH_ELECTION_EVENT), 
            eq(TENANT_ID), eq(ALIAS), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
                .thenReturn(callMock);

        javax.ws.rs.core.Response actualResponse = target(URL_GET_EE_ID_CONTEXT_DATA).resolveTemplate("tenantId", TENANT_ID).resolveTemplate("alias", ALIAS)
                .request()
                .get();
        int status = actualResponse.getStatus();

        Assert.assertEquals(mockedInvocationStatus, status);
        Assert.assertEquals(expectedResponse, actualResponse.readEntity(String.class));
    }
    
    @Test
    public void getElectionEventIdWithNonExistentElection() throws IOException {

        int mockedInvocationStatus = 200;

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

        commonPreparation();
        when(keyTranslationAdminClient.getElectionEventTranslation(eq(KeyTranslationResource.PATH_ELECTION_EVENT), 
            eq(TENANT_ID), eq(ALIAS), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
                .thenReturn(callMock);

        int status = target(URL_GET_EE_ID_CONTEXT_DATA).resolveTemplate("tenantId", TENANT_ID).resolveTemplate("alias", ALIAS)
                .request()
                .get().getStatus();
        
        Assert.assertEquals(mockedInvocationStatus, status);
    }
    
    @Test
    public void getElectionEventIdWithServiceNotavailable() throws IOException {

        int mockedInvocationStatus = 404;

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

        commonPreparation();
        when(keyTranslationAdminClient.getElectionEventTranslation(eq(KeyTranslationResource.PATH_ELECTION_EVENT), 
            eq(TENANT_ID), eq(ALIAS), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
            .thenReturn(callMock);

        int status = target(URL_GET_EE_ID_CONTEXT_DATA).resolveTemplate("tenantId", TENANT_ID).resolveTemplate("alias", ALIAS)
                .request()
                .get().getStatus();
        
        Assert.assertEquals(mockedInvocationStatus, status);
    }
    
    @Test
    public void uploadElectionEventTranslation() throws IOException {

        int mockedInvocationStatus = 200;

        commonPreparation();
        @SuppressWarnings("unchecked")
		Call<ResponseBody> callMock = (Call<ResponseBody>) Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.success(ResponseBody.create(okhttp3.MediaType.parse("application/json"), new byte[0])));

        when(keyTranslationAdminClient.uploadElectionEventTranslation(eq(KeyTranslationResource.PATH_ELECTION_EVENT), 
            eq(TENANT_ID), eq(X_FORWARDER_VALUE), eq(TRACK_ID), eq(ORIGINATOR), eq(SIGNATURE), any()))
                .thenReturn(callMock);

        ElectionEventTranslations electionEventTranslations = new ElectionEventTranslations();
        List<KeyTranslation> translations = new ArrayList<>();
        KeyTranslation kt = new KeyTranslation();
        kt.setTenantId(TENANT_ID);
        kt.setElectionEventId(EE_ID);
        kt.setAlias(ALIAS);
        translations.add(kt);
        electionEventTranslations.setElectionEventTranslations(translations);
        int status = target(URL_SAVE_EE_CONTEXT_DATA).resolveTemplate("tenantId", TENANT_ID)
                .request()
                .header(RestClientInterceptor.HEADER_ORIGINATOR, ORIGINATOR)
                .header(RestClientInterceptor.HEADER_SIGNATURE, SIGNATURE)
                .post(Entity.entity(electionEventTranslations, MediaType.APPLICATION_JSON_TYPE)).getStatus();

        Assert.assertEquals(mockedInvocationStatus, status);
    }
    
    @Test
    public void getElectionEventAlias() throws IOException {

        int mockedInvocationStatus = 200;
        String expectedResponse = "{\"alias\" : \"ee\"}";
        
        @SuppressWarnings("unchecked")
		Call<ResponseBody> callMock = (Call<ResponseBody>) Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.success(ResponseBody.create(okhttp3.MediaType.parse("application/json"), expectedResponse.getBytes())));

        commonPreparation();
        when(keyTranslationAdminClient.getElectionEventAliasTranslation(eq(KeyTranslationResource.PATH_ELECTION_EVENT), 
            eq(TENANT_ID), eq(EE_ID), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
                .thenReturn(callMock);

        javax.ws.rs.core.Response actualResponse = target(URL_GET_EE_ALIAS_CONTEXT_DATA).resolveTemplate("tenantId", TENANT_ID).
                resolveTemplate("electionEventId", EE_ID)
                .request()
                .get();
        int status = actualResponse.getStatus();

        Assert.assertEquals(mockedInvocationStatus, status);
        Assert.assertEquals(expectedResponse, actualResponse.readEntity(String.class));
    }
    
    @Test
    public void getElectionEventAliasWithServiceNotavailable() throws IOException {

        int mockedInvocationStatus = 404;

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

        commonPreparation();
        when(keyTranslationAdminClient.getElectionEventAliasTranslation(eq(KeyTranslationResource.PATH_ELECTION_EVENT), 
            eq(TENANT_ID), eq(EE_ID), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
        	.thenReturn(callMock);

        int status = target(URL_GET_EE_ALIAS_CONTEXT_DATA).resolveTemplate("tenantId", TENANT_ID).resolveTemplate("electionEventId", EE_ID)
                .request()
                .get().getStatus();
        
        Assert.assertEquals(mockedInvocationStatus, status);
    }
    
    @Test
    public void getVotingCardAlias() throws IOException {

        int mockedInvocationStatus = 200;
        String expectedResponse = "{\"alias\" : \"vc\"}";
        
        @SuppressWarnings("unchecked")
		Call<ResponseBody> callMock = (Call<ResponseBody>) Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.success(ResponseBody.create(okhttp3.MediaType.parse("application/json"), expectedResponse.getBytes())));

        commonPreparation();
        when(keyTranslationAdminClient.getVotingCardTranslation(eq(KeyTranslationResource.PATH_VOTING_CARD), 
            eq(TENANT_ID), eq(EE_ID), eq(ALIAS), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
                .thenReturn(callMock);

        javax.ws.rs.core.Response actualResponse = target(URL_GET_VC_ALIAS_CONTEXT_DATA)
                .resolveTemplate("tenantId", TENANT_ID)
                .resolveTemplate("electionEventId", EE_ID)
                .resolveTemplate("alias", ALIAS)
                .request()
                .get();
        int status = actualResponse.getStatus();

        Assert.assertEquals(mockedInvocationStatus, status);
        Assert.assertEquals(expectedResponse, actualResponse.readEntity(String.class));
    }
    
    @Test
    public void getVotingCardAliasWithServiceNotavailable() throws IOException {

        int mockedInvocationStatus = 404;

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

        commonPreparation();
        when(keyTranslationAdminClient.getVotingCardTranslation(eq(KeyTranslationResource.PATH_VOTING_CARD), 
            eq(TENANT_ID), eq(EE_ID), eq(ALIAS), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
        	.thenReturn(callMock);

        int status = target(URL_GET_VC_ALIAS_CONTEXT_DATA)
                .resolveTemplate("tenantId", TENANT_ID)
                .resolveTemplate("electionEventId", EE_ID)
                .resolveTemplate("alias", ALIAS)
                .request()
                .get().getStatus();
        
        Assert.assertEquals(mockedInvocationStatus, status);
    }

    @Test
    public void uploadVotingCardTranslation() throws IOException {

        int mockedInvocationStatus = 200;

        commonPreparation();

        @SuppressWarnings("unchecked")
		Call<ResponseBody> callMock = (Call<ResponseBody>) Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.success(ResponseBody.create(okhttp3.MediaType.parse("application/json"), new byte[0])));
        
        when(keyTranslationAdminClient.uploadVotingCardTranslation(eq(KeyTranslationResource.PATH_VOTING_CARD), 
            eq(TENANT_ID), eq(X_FORWARDER_VALUE), eq(TRACK_ID), eq(ORIGINATOR), eq(SIGNATURE), any()))
                .thenReturn(callMock);

        VotingCardTranslationList votingCardTranslationList = new VotingCardTranslationList();
        List<VotingCardTranslation> votingCardTranslations = new ArrayList<>();
        VotingCardTranslation vct = new VotingCardTranslation();
        vct.setAlias("test");
        vct.setElectionEventId("test");
        vct.setId(1);
        vct.setTenantId("test");
        vct.setVerificationCardId("test");
        vct.setVotingCardId("test");
        vct.setVotingCardSetAlias("test");
		votingCardTranslations.add(vct);
		votingCardTranslationList.setVotingCardTranslations(votingCardTranslations);
		int status = target(URL_SAVE_VC_CONTEXT_DATA).resolveTemplate("tenantId", TENANT_ID)
                .request()
                .header(RestClientInterceptor.HEADER_ORIGINATOR, ORIGINATOR)
                .header(RestClientInterceptor.HEADER_SIGNATURE, SIGNATURE)
                .post(Entity.entity(votingCardTranslationList, MediaType.APPLICATION_JSON_TYPE)).getStatus();

        Assert.assertEquals(mockedInvocationStatus, status);
    }

    @Test
    public void getBallotBoxTranslationByAlias() throws IOException {

        int mockedInvocationStatus = 200;
        String expectedResponse = "{\"ee\" : \"alias\"}";
        
        @SuppressWarnings("unchecked")
		Call<ResponseBody> callMock = (Call<ResponseBody>) Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.success(ResponseBody.create(okhttp3.MediaType.parse("application/json"), expectedResponse.getBytes())));

        commonPreparation();
        when(keyTranslationAdminClient.getBallotBoxTranslationByAlias(eq(KeyTranslationResource.PATH_BALLOT_BOX), 
            eq(TENANT_ID), eq(EE_ID), eq(ALIAS), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
                .thenReturn(callMock);

        javax.ws.rs.core.Response actualResponse = target(URL_GET_BB_ID_CONTEXT_DATA)
                .resolveTemplate("tenantId", TENANT_ID)
                .resolveTemplate("alias", ALIAS)
                .resolveTemplate("electionEventId", EE_ID)
                .request()
                .get();
        int status = actualResponse.getStatus();

        Assert.assertEquals(mockedInvocationStatus, status);
        Assert.assertEquals(expectedResponse, actualResponse.readEntity(String.class));
    }
    
    @Test
    public void getBallotBoxTranslationByAliasWithNonExistentElection() throws IOException {

        int mockedInvocationStatus = 404;

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

        commonPreparation();
        when(keyTranslationAdminClient.getBallotBoxTranslationByAlias(eq(KeyTranslationResource.PATH_BALLOT_BOX), 
            eq(TENANT_ID), eq(EE_ID), eq(ALIAS), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
        	.thenReturn(callMock);

        int status = target(URL_GET_BB_ID_CONTEXT_DATA)
                .resolveTemplate("tenantId", TENANT_ID)
                .resolveTemplate("alias", ALIAS)
                .resolveTemplate("electionEventId", EE_ID)
                .request()
                .get().getStatus();
        
        Assert.assertEquals(mockedInvocationStatus, status);
    }
    
    @Test
    public void uploadBallotBoxTranslation() throws IOException {

        int mockedInvocationStatus = 200;

        commonPreparation();

        @SuppressWarnings("unchecked")
		Call<ResponseBody> callMock = (Call<ResponseBody>) Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.success(ResponseBody.create(okhttp3.MediaType.parse("application/json"), new byte[0])));
        
        when(keyTranslationAdminClient.uploadBallotBoxTranslation(eq(KeyTranslationResource.PATH_BALLOT_BOX), 
            eq(TENANT_ID), eq(X_FORWARDER_VALUE), eq(TRACK_ID), eq(ORIGINATOR), eq(SIGNATURE), any()))
                .thenReturn(callMock);

        int status = target(URL_SAVE_BB_CONTEXT_DATA).resolveTemplate("tenantId", TENANT_ID)
                .request()
                .header(RestClientInterceptor.HEADER_ORIGINATOR, ORIGINATOR)
                .header(RestClientInterceptor.HEADER_SIGNATURE, SIGNATURE)
                .post(Entity.entity(new String("voting card csv"), MediaType.APPLICATION_JSON_TYPE)).getStatus();

        Assert.assertEquals(mockedInvocationStatus, status);
    }
    
    @Test
    public void getBallotBoxTranslationById() throws IOException {

        int mockedInvocationStatus = 200;
        String expectedResponse = "{\"alias\" : \"ee\"}";
        
        @SuppressWarnings("unchecked")
		Call<ResponseBody> callMock = (Call<ResponseBody>) Mockito.mock(Call.class);
        when(callMock.execute()).thenReturn(Response.success(ResponseBody.create(okhttp3.MediaType.parse("application/json"), expectedResponse.getBytes())));

        commonPreparation();
        when(keyTranslationAdminClient.getBallotBoxTranslationByBallotBoxId(eq(KeyTranslationResource.PATH_BALLOT_BOX), 
            eq(TENANT_ID), eq(EE_ID), eq(BB_ID), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
                .thenReturn(callMock);

        javax.ws.rs.core.Response actualResponse = target(URL_GET_BB_ALIAS_CONTEXT_DATA)
                .resolveTemplate("tenantId", TENANT_ID)
                .resolveTemplate("electionEventId", EE_ID)
                .resolveTemplate("bbid", BB_ID)
                .request()
                .get();
        int status = actualResponse.getStatus();

        Assert.assertEquals(mockedInvocationStatus, status);
        Assert.assertEquals(expectedResponse, actualResponse.readEntity(String.class));
    }
    
    @Test
    public void getBallotBoxTranslationByIdWithServiceNotavailable() throws IOException {

        int mockedInvocationStatus = 404;

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

        commonPreparation();
        when(keyTranslationAdminClient.getBallotBoxTranslationByBallotBoxId(eq(KeyTranslationResource.PATH_BALLOT_BOX), 
            eq(TENANT_ID), eq(EE_ID), eq(BB_ID), eq(X_FORWARDER_VALUE), eq(TRACK_ID)))
        	.thenReturn(callMock);

        int status = target(URL_GET_BB_ALIAS_CONTEXT_DATA)
                .resolveTemplate("tenantId", TENANT_ID)
                .resolveTemplate("electionEventId", EE_ID)
                .resolveTemplate("bbid", BB_ID)
                .request()
                .get().getStatus();
        
        Assert.assertEquals(mockedInvocationStatus, status);
    }

    private void commonPreparation() {

        when(servletRequest.getHeader(eq(RestClientInterceptor.HEADER_ORIGINATOR))).thenReturn(ORIGINATOR);
        when(servletRequest.getHeader(eq(RestClientInterceptor.HEADER_SIGNATURE))).thenReturn(SIGNATURE);
        
        when(servletRequest.getRemoteAddr()).thenReturn("");
        when(servletRequest.getLocalAddr()).thenReturn("");
        when(trackIdGenerator.generate()).thenReturn(TRACK_ID);
    }

    @Override
    protected Application configure() {

        System.setProperty("KEY_TRANSLATION_CONTEXT_URL", "localhost");
        MockitoAnnotations.initMocks(this);

        AbstractBinder binder = new AbstractBinder() {
            @Override
            protected void configure() {
            	bind(logger).to(Logger.class);
                bind(servletRequest).to(HttpServletRequest.class);
            }
        };
        sut = new KeyTranslationResource(keyTranslationAdminClient, trackIdGenerator);
        forceSet(TestProperties.CONTAINER_PORT, "0");
        return new ResourceConfig().register(sut).register(binder);
    }
}
