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

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

import com.scytl.products.ov.commons.test.CryptoUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.Security;

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

import org.bouncycastle.jce.provider.BouncyCastleProvider;
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.BeforeClass;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.slf4j.Logger;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.certificates.cryptoapi.CryptoAPIX509Certificate;
import com.scytl.products.oscore.logging.core.provider.TransactionInfoProvider;
import com.scytl.products.ov.au.services.domain.model.adminboard.AdminBoardRepository;
import com.scytl.products.ov.au.services.domain.model.authentication.AuthenticationCertsRepository;
import com.scytl.products.ov.au.services.domain.model.authentication.AuthenticationContentRepository;
import com.scytl.products.ov.au.services.domain.model.authentication.ElectionEventData;
import com.scytl.products.ov.au.services.domain.service.ElectionEventService;
import com.scytl.products.ov.commons.beans.AuthenticationContextData;
import com.scytl.products.ov.commons.beans.domain.model.certificate.CertificateEntity;
import com.scytl.products.ov.commons.crypto.configuration.CertificateParameters;
import com.scytl.products.ov.commons.infrastructure.remote.client.RetrofitException;
import com.scytl.products.ov.commons.infrastructure.remote.service.RemoteCertificateService;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;
import com.scytl.products.ov.commons.util.HttpRequestService;
import com.scytl.products.ov.commons.verify.JSONVerifier;

import mockit.Deencapsulation;

public class ElectionEventDataResourceTest extends JerseyTest {

    public static final String PARAM_TENANT_ID = "tenantId";

    public static final String TENANT_ID = "100";

    public static final String PARAM_ELECTION_EVENT_ID = "electionEventId";

    public static final String ELECTION_EVENT_ID = "100";

    public static final String ADMIN_BOARD_ID = "100";

    public static final String PARAM_ADMIN_BOARD_ID = "adminBoardId";

    // An instance of the authentication content repository
    @Mock
    private AuthenticationContentRepository authenticationContentRepository;

    // An instance of the authentication certificates repository
    @Mock
    private AuthenticationCertsRepository authenticationCertsRepository;

    @Mock
    private Logger LOG;

    @Mock
    private TransactionInfoProvider transactionInfoProvider;

    @Mock
    private HttpRequestService httpRequestService;

    @Mock
    private RemoteCertificateService remoteCertificateService;

    @Mock
    private AdminBoardRepository adminBoardRepository;

    // The track id instance
    @Mock
    private TrackIdInstance trackIdInstance;

    private ElectionEventData electionEventData;

    @Mock
    private CertificateEntity certificateEntity;

    @Mock
    private AuthenticationContextData authenticationContextData;

    @Mock
    private ElectionEventService electionEventService;

    @Mock
    private JSONVerifier jsonVerifier;

    private ElectionEventDataResource sut;
    
    private ElectionEventService serviceSut;

    private final static String PATH_POST =
        "/electioneventdata/tenant/{tenantId}/electionevent/{electionEventId}/adminboard/{adminBoardId}";

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

    @Mock
    HttpServletRequest servletRequest;

    @Test
    public void uploadElectionEventData() throws IOException, GeneralCryptoLibException, RetrofitException {

        int expectedStatus = 200;
        commonPreparation();
        prepareData();
        final KeyPair keyPairForSigning = CryptoUtils.getKeyPairForSigning();
        final CryptoAPIX509Certificate adminBoardCert = CryptoUtils.createCryptoAPIx509Certificate("adminBoard",
            CertificateParameters.Type.SIGN, keyPairForSigning);

        when(remoteCertificateService.getAdminBoardCertificate(anyString())).thenReturn(certificateEntity);
        when(certificateEntity.getCertificateContent())
            .thenReturn(new String(adminBoardCert.getPemEncoded(), StandardCharsets.UTF_8));

        final Response post = target().path(PATH_POST).resolveTemplate(PARAM_TENANT_ID, TENANT_ID)
            .resolveTemplate(PARAM_ELECTION_EVENT_ID, ELECTION_EVENT_ID)
            .resolveTemplate(PARAM_ADMIN_BOARD_ID, ADMIN_BOARD_ID).request()
            .post(Entity.entity(electionEventData, MediaType.APPLICATION_JSON_TYPE));

        assertThat(post.getStatus(), is(expectedStatus));
    }

    private void commonPreparation() {
        when(servletRequest.getHeader("X-Request-ID")).thenReturn("request");
        when(servletRequest.getRemoteAddr()).thenReturn("");
        when(servletRequest.getLocalAddr()).thenReturn("");
    }

    private void prepareData() {

        electionEventData = new ElectionEventData();
        electionEventData.setAuthenticationContextData("{\"signature\":\"authenticationContextData\"}");
        electionEventData.setAuthenticationVoterData("{\"signature\":\"authenticationVoterData\"}");

    }

    @Override
    protected Application configure() {

        MockitoAnnotations.initMocks(this);

        AbstractBinder binder = new AbstractBinder() {
            @Override
            protected void configure() {

                bind(authenticationCertsRepository).to(AuthenticationCertsRepository.class);
                bind(authenticationContentRepository).to(AuthenticationContentRepository.class);
                bind(transactionInfoProvider).to(TransactionInfoProvider.class);
                bind(httpRequestService).to(HttpRequestService.class);
                bind(remoteCertificateService).to(RemoteCertificateService.class);
                bind(adminBoardRepository).to(AdminBoardRepository.class);
                bind(trackIdInstance).to(TrackIdInstance.class);
                bind(servletRequest).to(ServletRequest.class);
                bind(jsonVerifier).to(JSONVerifier.class);
                bind(electionEventService).to(ElectionEventService.class);
                bind(LOG).to(Logger.class);
                bind(authenticationContextData).to(AuthenticationContextData.class);
            }
        };

        sut = new ElectionEventDataResource();
        serviceSut = new ElectionEventService();

        Deencapsulation.setField(sut, "electionEventService", serviceSut);
        forceSet(TestProperties.CONTAINER_PORT, "0");
        return new ResourceConfig().register(sut).register(binder);

    }
}
