/*
 * 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.sdm.domain.service.impl;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.security.GeneralSecurityException;

import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response;

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.Mockito;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.products.ov.commons.path.PathResolver;
import com.scytl.products.ov.sdm.domain.model.ballotbox.BallotBoxRepository;
import com.scytl.products.ov.sdm.domain.model.electionevent.ElectionEventRepository;
import com.scytl.products.ov.sdm.domain.model.generator.DataGeneratorResponse;
import com.scytl.products.ov.sdm.domain.service.utils.SystemTenantPublicKeyLoader;
import com.scytl.products.ov.sdm.infrastructure.JsonConstants;

/**
 * JUnit for the {@linkBallotBoxDataGeneratorServiceImpl}.
 */
@RunWith(MockitoJUnitRunner.class)
public class BallotBoxDataGeneratorServiceImplTest {

    @InjectMocks
    @Spy
    private final BallotBoxDataGeneratorServiceImpl ballotBoxDataGeneratorService =
        new BallotBoxDataGeneratorServiceImpl();

    @Mock
    private BallotBoxRepository ballotBoxRepositoryMock;

    @Mock
    private ElectionEventRepository electionEventRepository;

    @Mock
    private PathResolver pathResolverMock;

    @Mock
    private SystemTenantPublicKeyLoader systemTenantPublicKeyLoader;

    @Mock
    private Path pathMock;

    @Mock
    private Path configPathMock;

    @Mock
    private Path mockedKeysPath;

    @Mock
    private WebTarget webTarget;

    @Mock
    private Invocation.Builder builderMock;

    @Mock
    private Response responseMock;

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

    private final String configPath = "";

    private final String configPropertiesPath = "";

    private String ballotBoxId;

    private String electionEventId;

    private final String ballotBoxAsJson =
        "{ \"id\": \"1234aa1d3f194c11aac03e421472b6bb\", \"defaultTitle\": \"Ballot Box Title\", \"defaultDescription\": \"Ballot Box Description\", \"alias\": \"Ballot Box Alias\", \"dateFrom\": \"12/12/2012\", \"dateTo\": \"14/12/2012\",\"gracePeriod\": \"142\","
            + "\"electionEvent\": { \"id\": \"314bd34dcf6e4de4b771a92fa3849d3d\"},"
            + "\"ballot\": { \"id\": \"dd5bd34dcf6e4de4b771a92fa38abc11\"},"
            + "\"electoralAuthority\": { \"id\": \"hhhbd34dcf6e4de4b771a92fa38abhhh\"}, \"test\": \"false\", \"confirmationRequired\": \"true\"}";

    private final String electionEventAsJson =
        "{ \"id\": \"1234aa1d3f194c11aac03e421472b6bb\", \"dateFrom\": \"12/12/2012\", \"dateTo\": \"14/12/2012\", \"settings\":{\"certificatesValidityPeriod\":1, \"writeInAlphabet\" : \"\"}}";

    @Test
    public void generateWithIdNull() throws IOException {
        DataGeneratorResponse result = ballotBoxDataGeneratorService.generate(ballotBoxId, electionEventId);
        assertFalse(result.isSuccessful());
    }

    @Test
    public void generateWithIdEmpty() throws IOException {
        ballotBoxId = "";
        DataGeneratorResponse result = ballotBoxDataGeneratorService.generate(ballotBoxId, electionEventId);
        assertFalse(result.isSuccessful());
    }

    @Test
    public void generateWithIdNotFound() throws IOException {
        ballotBoxId = "123456";

        when(ballotBoxRepositoryMock.find(ballotBoxId)).thenReturn(JsonConstants.JSON_EMPTY_OBJECT);

        DataGeneratorResponse result = ballotBoxDataGeneratorService.generate(ballotBoxId, electionEventId);
        assertFalse(result.isSuccessful());
    }

    @Test
    public void generateThrowGeneralSecurityException()
            throws IOException, URISyntaxException, GeneralCryptoLibException, GeneralSecurityException {

        setFieldThatSpecifiesThePathOfCertProperties();

        ballotBoxId = "1234aa1d3f194c11aac03e421472b6bb";
        electionEventId = "1234aa1d3f194c11aac03e421444b6bb";
        when(ballotBoxRepositoryMock.find(ballotBoxId)).thenReturn(ballotBoxAsJson);
        when(electionEventRepository.find(electionEventId)).thenReturn(electionEventAsJson);
        when(pathResolverMock.resolve(Mockito.anyString())).thenReturn(pathMock);
        when(pathMock.toString()).thenReturn(configPath);
        when(pathMock.resolve(Mockito.anyString())).thenReturn(configPathMock);
        when(configPathMock.toString()).thenReturn(configPropertiesPath);
        doThrow(GeneralSecurityException.class).when(webTarget).request();
        doReturn(webTarget).when(ballotBoxDataGeneratorService).createWebClient();
        expectedException.expect(GeneralSecurityException.class);
        ballotBoxDataGeneratorService.generate(ballotBoxId, electionEventId);

    }

    @Test
    public void generateThrowGeneralCryptoLibException()
            throws IOException, URISyntaxException, GeneralCryptoLibException, GeneralSecurityException {

        setFieldThatSpecifiesThePathOfCertProperties();

        ballotBoxId = "1234aa1d3f194c11aac03e421472b6bb";
        electionEventId = "1234aa1d3f194c11aac03e421444b6bb";
        when(ballotBoxRepositoryMock.find(ballotBoxId)).thenReturn(ballotBoxAsJson);
        when(electionEventRepository.find(electionEventId)).thenReturn(electionEventAsJson);
        when(ballotBoxRepositoryMock.find(ballotBoxId)).thenReturn(ballotBoxAsJson);
        when(pathResolverMock.resolve(Mockito.anyString())).thenReturn(pathMock);
        when(pathMock.toString()).thenReturn(configPath);
        when(pathMock.resolve(Mockito.anyString())).thenReturn(configPathMock);
        when(configPathMock.toString()).thenReturn(configPropertiesPath);
        doThrow(GeneralCryptoLibException.class).when(webTarget).request();
        doReturn(webTarget).when(ballotBoxDataGeneratorService).createWebClient();

        expectedException.expect(GeneralCryptoLibException.class);
        ballotBoxDataGeneratorService.generate(ballotBoxId, electionEventId);

    }

    @Test
    public void generateThrowURISyntaxException()
            throws IOException, URISyntaxException, GeneralCryptoLibException, GeneralSecurityException,
            NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {

        setFieldThatSpecifiesThePathOfCertProperties();

        ballotBoxId = "1234aa1d3f194c11aac03e421472b6bb";
        electionEventId = "1234aa1d3f194c11aac03e431472b6bb";

        when(ballotBoxRepositoryMock.find(ballotBoxId)).thenReturn(ballotBoxAsJson);
        when(pathResolverMock.resolve(Mockito.anyString())).thenReturn(pathMock);
        when(pathMock.toString()).thenReturn(configPath);
        when(pathMock.resolve(Mockito.anyString())).thenReturn(configPathMock);
        when(configPathMock.toString()).thenReturn(configPropertiesPath);
        when(systemTenantPublicKeyLoader.load(Mockito.anyString(), Mockito.anyString(), Mockito.any()))
            .thenReturn("XXXXXXXXXXXXXXXXXXXXXXXXXX");

        when(pathResolverMock.resolve("/sdm/systemKeys/100_AU_ENCRYPT.pem")).thenReturn(mockedKeysPath);
        when(pathMock.toAbsolutePath()).thenReturn(pathMock);
        when(pathMock.toString()).thenReturn("mockedPath");

        when(electionEventRepository.find(electionEventId)).thenReturn(electionEventAsJson);
        doReturn(webTarget).when(ballotBoxDataGeneratorService).createWebClient();
        when(webTarget.request()).thenThrow(URISyntaxException.class);
        expectedException.expect(URISyntaxException.class);

        Field field = BallotBoxDataGeneratorServiceImpl.class.getDeclaredField("tenantId");
        field.setAccessible(true);
        field.set(ballotBoxDataGeneratorService, "100");

        ballotBoxDataGeneratorService.generate(ballotBoxId, electionEventId);
    }

    @Test
    public void generateThrowIOException()
            throws IOException, URISyntaxException, GeneralCryptoLibException, GeneralSecurityException {

        setFieldThatSpecifiesThePathOfCertProperties();

        ballotBoxId = "1234aa1d3f194c11aac03e421472b6bb";
        electionEventId = "1234aa1d3f194c11aac03e421444b6bb";
        when(electionEventRepository.find(electionEventId)).thenReturn(electionEventAsJson);
        when(ballotBoxRepositoryMock.find(ballotBoxId)).thenReturn(ballotBoxAsJson);
        when(pathResolverMock.resolve(Mockito.anyString())).thenReturn(pathMock);
        when(pathMock.toString()).thenReturn(configPath);
        when(pathMock.resolve(Mockito.anyString())).thenReturn(configPathMock);
        when(configPathMock.toString()).thenReturn(configPropertiesPath);

        doReturn(webTarget).when(ballotBoxDataGeneratorService).createWebClient();
        when(webTarget.request()).thenThrow(IOException.class);
        expectedException.expect(IOException.class);
        ballotBoxDataGeneratorService.generate(ballotBoxId, electionEventId);

    }

    @Test
    public void generateWithValidId()
            throws IOException, URISyntaxException, GeneralCryptoLibException, GeneralSecurityException {
        ballotBoxId = "1234aa1d3f194c11aac03e421472b6bb";
        electionEventId = "1234aa1d3f194c11aac03e421444b6bb";
        /*
         * when(electionEventRepository.findByElectionEvent(electionEventId)).thenReturn(electionEventAsJson);
         * when(ballotBoxRepositoryMock.find(ballotBoxId)).thenReturn(ballotBoxAsJson);
         * when(ballotBoxRepositoryMock.find(ballotBoxId)).thenReturn(ballotBoxAsJson);
         * when(pathResolverMock.resolve(Mockito.anyString())).thenReturn(pathMock);
         * when(pathMock.toString()).thenReturn(configPath);
         * when(pathMock.resolve(Mockito.anyString())).thenReturn(configPathMock);
         * when(configPathMock.toString()).thenReturn(configPropertiesPath); javax.ws.rs.client.Entity<String> argument
         * = javax.ws.rs.client.Entity.entity("", MediaType.APPLICATION_JSON);
         * doReturn(webTarget).when(ballotBoxDataGeneratorService).createWebClient();
         * when(webTarget.request()).thenReturn(builderMock);
         * when(when(builderMock.post(argument)).thenReturn(responseMock));
         */
        DataGeneratorResponse response = new DataGeneratorResponse();
        doReturn(response).when(ballotBoxDataGeneratorService).generate(ballotBoxId, electionEventId);

        DataGeneratorResponse result = ballotBoxDataGeneratorService.generate(ballotBoxId, electionEventId);
        assertTrue(result.isSuccessful());
    }

    private void setFieldThatSpecifiesThePathOfCertProperties() throws URISyntaxException {

        ClassLoader classLoader = getClass().getClassLoader();
        String pathAsString = classLoader.getResource("properties/ballotBoxX509Certificate.properties").getPath();
        ReflectionTestUtils.setField(ballotBoxDataGeneratorService, "ballotBoxCertificateProperties", pathAsString);
    }
}
