/*
 * 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.config.commands.ballotbox;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;

import java.io.InputStream;
import java.nio.file.Path;
import java.security.PrivateKey;
import java.util.Map;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import com.scytl.cryptolib.certificates.cryptoapi.CryptoAPIX509Certificate;
import com.scytl.products.ov.commons.beans.EncryptionParameters;
import com.scytl.products.ov.constants.Constants;
import com.scytl.products.ov.datapacks.beans.ConfigurationInput;
import com.scytl.products.ov.datapacks.beans.CredentialProperties;
import com.scytl.products.ov.readers.ConfigurationInputReader;
import com.scytl.products.ov.utils.EncryptionParametersLoader;
import com.scytl.products.ov.utils.KeyStoreReader;
import com.scytl.products.ov.utils.X509CertificateLoader;

/**
 * So much mocking is a "smell"
 */
@RunWith(MockitoJUnitRunner.class)
public class BallotBoxHolderInitializerTest {

    @Mock
    ConfigurationInputReader configurationInputReader;

    @Mock
    X509CertificateLoader certificateLoader;

    @Mock
    EncryptionParametersLoader encryptionParametersLoader;

    @Mock
    KeyStoreReader keyStoreReader;

    @InjectMocks
    BallotBoxHolderInitializer sut;

    @Rule
    public TemporaryFolder temporaryFolder = new TemporaryFolder();

    @Test
    public void setKeysConfigurationFromFile() throws Exception {

        // given
        BallotBoxParametersHolder holder = mock(BallotBoxParametersHolder.class);
        when(holder.getOutputPath()).thenReturn(temporaryFolder.newFolder().toPath());

        ConfigurationInput mockInput = mock(ConfigurationInput.class);
        CredentialProperties mockCredentialProperties = mock(CredentialProperties.class);
        Map<String, CredentialProperties> mockProperties = mock(Map.class);
        when(mockProperties.get(Constants.CONFIGURATION_SERVICES_CA_JSON_TAG)).thenReturn(mockCredentialProperties);
        when(mockProperties.get(Constants.CONFIGURATION_ELECTION_CA_JSON_TAG)).thenReturn(mockCredentialProperties);
        when(mockProperties.get(Constants.CONFIGURATION_SERVICES_CA_PRIVATE_KEY_JSON_TAG))
            .thenReturn(mockCredentialProperties);
        when(mockInput.getConfigProperties()).thenReturn(mockProperties);
        when(mockInput.getBallotBox()).thenReturn(mockCredentialProperties);

        when(configurationInputReader.fromStreamToJava(any())).thenReturn(mockInput);
        final CryptoAPIX509Certificate mockCertificate = mock(CryptoAPIX509Certificate.class);
        when(certificateLoader.load(any(Path.class))).thenReturn(mockCertificate);
        final EncryptionParameters mockEncryptionParams = mock(EncryptionParameters.class);
        when(encryptionParametersLoader.load(any())).thenReturn(mockEncryptionParams);
        final PrivateKey mockPrivateKey = mock(PrivateKey.class);
        when(keyStoreReader.getPrivateKey(any(), any(), any(), any())).thenReturn(mockPrivateKey);

        // when
        sut.init(holder, mock(InputStream.class));

        // then
        int numberOfInvocations = 1;
        Mockito.verify(holder, atLeastOnce()).getOutputPath(); // avoid error of
                                                               // no more
                                                               // interactions
        Mockito.verify(holder, times(numberOfInvocations)).setServicesCAPrivateKey(mockPrivateKey);
        Mockito.verify(holder, times(numberOfInvocations)).setBallotBoxCredentialProperties(mockCredentialProperties);
        Mockito.verify(holder, times(numberOfInvocations)).setEncryptionParameters(mockEncryptionParams);
        Mockito.verify(holder, times(numberOfInvocations)).setServicesCACert(any());
        Mockito.verify(holder, times(numberOfInvocations)).setElectionCACert(mockCertificate);
        Mockito.verifyNoMoreInteractions(holder);
    }

    @Test
    public void setKeysConfigurationFromStream() throws Exception {

        // given
        BallotBoxParametersHolder holder = mock(BallotBoxParametersHolder.class);
        when(holder.getOutputPath()).thenReturn(temporaryFolder.newFolder().toPath());

        InputStream configInputStream = mock(InputStream.class);
        ConfigurationInput mockInput = mock(ConfigurationInput.class);
        CredentialProperties mockCredentialProperties = mock(CredentialProperties.class);
        Map<String, CredentialProperties> mockProperties = mock(Map.class);
        when(mockProperties.get(Constants.CONFIGURATION_SERVICES_CA_JSON_TAG)).thenReturn(mockCredentialProperties);
        when(mockProperties.get(Constants.CONFIGURATION_ELECTION_CA_JSON_TAG)).thenReturn(mockCredentialProperties);
        when(mockProperties.get(Constants.CONFIGURATION_SERVICES_CA_PRIVATE_KEY_JSON_TAG))
            .thenReturn(mockCredentialProperties);
        when(mockInput.getConfigProperties()).thenReturn(mockProperties);
        when(mockInput.getBallotBox()).thenReturn(mockCredentialProperties);

        when(configurationInputReader.fromStreamToJava(any())).thenReturn(mockInput);
        final CryptoAPIX509Certificate mockCertificate = mock(CryptoAPIX509Certificate.class);
        when(certificateLoader.load(any(Path.class))).thenReturn(mockCertificate);
        final EncryptionParameters mockEncryptionParams = mock(EncryptionParameters.class);
        when(encryptionParametersLoader.load(any())).thenReturn(mockEncryptionParams);
        final PrivateKey mockPrivateKey = mock(PrivateKey.class);
        when(keyStoreReader.getPrivateKey(any(), any(), any(), any())).thenReturn(mockPrivateKey);

        // when
        sut.init(holder, configInputStream);

        // then
        int numberOfInvocations = 1;
        Mockito.verify(holder, atLeastOnce()).getOutputPath(); // avoid error of
                                                               // no more
                                                               // interactions
        Mockito.verify(holder, times(numberOfInvocations)).setServicesCAPrivateKey(mockPrivateKey);
        Mockito.verify(holder, times(numberOfInvocations)).setBallotBoxCredentialProperties(mockCredentialProperties);
        Mockito.verify(holder, times(numberOfInvocations)).setEncryptionParameters(mockEncryptionParams);
        Mockito.verify(holder, times(numberOfInvocations)).setServicesCACert(any());
        Mockito.verify(holder, times(numberOfInvocations)).setElectionCACert(mockCertificate);
        Mockito.verifyNoMoreInteractions(holder);
    }
}
