/*
 * 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.config.spring.batch;

import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_BALLOTBOX_ID;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_BALLOT_ID;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_BASE_PATH;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_ELECTIONEVENT_ID;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_ELECTORALAUTHORITY_ID;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_JOB_INSTANCE_ID;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_KEY_FOR_PROTECTING_KEYSTORE_PW;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_NUMBER_VOTING_CARDS;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_SALT_CREDENTIAL_ID;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_SALT_PIN;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_TENANT_ID;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_VALIDITY_PERIOD_END;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_VALIDITY_PERIOD_START;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_VERIFICATIONCARDSET_ID;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_VOTINGCARDSET_ID;
import static com.scytl.products.ov.config.commons.Constants.JOB_IN_PARAM_VOTING_CARD_SET_NAME;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.UUID;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.test.ExecutionContextTestUtils;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;

import com.scytl.products.ov.config.commands.voters.VotersParametersHolder;
import com.scytl.products.ov.datapacks.beans.CreateVotingCardSetCertificatePropertiesContainer;

/**
 * This tests a specific batch job step. By default, JobLauncherTestUtils expects to find _only one_ Job bean in the
 * context, that's why i made the batch configuration class an inner class and not a "shared" class for all tests. We
 * could extract the class into a different file but i don't see any advantage
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class PrepareVotingCardGenerationDataTest {

    private static final String STEP_IN_TEST = "prepareVotingCardGenerationDataStep";

    @Configuration
    @EnableBatchProcessing
    @Import(TestConfigServices.class)
    static class JobConfiguration {

        @Autowired
        JobBuilderFactory jobBuilder;

        @Autowired
        StepBuilderFactory stepBuilder;

        @Bean
        JobLauncherTestUtils testUtils() {
            return new JobLauncherTestUtils();
        }

        @Bean
        public Step step(Tasklet tasklet) {
            return stepBuilder.get(STEP_IN_TEST).tasklet(tasklet).build();
        }

        @Bean
        public Tasklet tasklet() {
            return new PrepareVotingCardGenerationDataTasklet();
        }

        @Bean
        Job job(Step step) {
            return jobBuilder.get("job").start(step).build();
        }

        @Bean
        VotersParametersHolder holder() throws IOException {
            VotersParametersHolder holder = mock(VotersParametersHolder.class);
            when(holder.getCreateVotingCardSetCertificateProperties())
                .thenReturn(getCreateVotingCardSetCertificateProperties());
            return holder;
        }
    }

    @Autowired
    private JobLauncherTestUtils jobLauncherTestUtils;

    @Test
    public void generateAndAddSaltParametersToJobExecutionContext() throws Exception {

        // given
        JobParameters jobParameters = getJobInputParameters();

        // when
        JobExecution jobExecution = jobLauncherTestUtils.launchStep(STEP_IN_TEST, jobParameters);

        // then (we want to know that the "new" job parameters are generated and stored in the context)
        Assert.assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());

        final String saltCredentialId =
            ExecutionContextTestUtils.getValueFromJob(jobExecution, JOB_IN_PARAM_SALT_CREDENTIAL_ID);
        Assert.assertNotNull(saltCredentialId);

        final String saltPin = ExecutionContextTestUtils.getValueFromJob(jobExecution, JOB_IN_PARAM_SALT_PIN);
        Assert.assertNotNull(saltPin);
    }

    private JobParameters getJobInputParameters() {
        JobParametersBuilder jobParametersBuilder = new JobParametersBuilder();
        jobParametersBuilder.addString(JOB_IN_PARAM_JOB_INSTANCE_ID, UUID.randomUUID().toString(), true);
        jobParametersBuilder.addString(JOB_IN_PARAM_TENANT_ID, "tenantId", true);
        jobParametersBuilder.addString(JOB_IN_PARAM_ELECTIONEVENT_ID, "electionEventId", true);
        jobParametersBuilder.addString(JOB_IN_PARAM_BALLOTBOX_ID, "ballotBoxId", true);
        jobParametersBuilder.addString(JOB_IN_PARAM_BALLOT_ID, "ballotId");
        jobParametersBuilder.addString(JOB_IN_PARAM_ELECTORALAUTHORITY_ID, "electoralAuthorityId");
        jobParametersBuilder.addString(JOB_IN_PARAM_VOTINGCARDSET_ID, "votingCardSetId");
        jobParametersBuilder.addString(JOB_IN_PARAM_NUMBER_VOTING_CARDS, "10");
        jobParametersBuilder.addString(JOB_IN_PARAM_VOTING_CARD_SET_NAME, "votingCardSetAlias");
        jobParametersBuilder.addString(JOB_IN_PARAM_VALIDITY_PERIOD_START, "2017-02-15");
        jobParametersBuilder.addString(JOB_IN_PARAM_VALIDITY_PERIOD_END, "2018-02-15");
        jobParametersBuilder.addString(JOB_IN_PARAM_KEY_FOR_PROTECTING_KEYSTORE_PW, "password");
        jobParametersBuilder.addString(JOB_IN_PARAM_BASE_PATH, "absoluteOutputPath");
        jobParametersBuilder.addString(JOB_IN_PARAM_VERIFICATIONCARDSET_ID, "verificationCardSetId");
        return jobParametersBuilder.toJobParameters();
    }

    private static CreateVotingCardSetCertificatePropertiesContainer getCreateVotingCardSetCertificateProperties()
            throws IOException {

        CreateVotingCardSetCertificatePropertiesContainer createVotingCardSetCertificateProperties =
            new CreateVotingCardSetCertificatePropertiesContainer();

        String votingCardSetCertificatePropertiesPath = "properties/votingCardSetX509Certificate.properties";
        Properties loadedVotingCardSetCertificateProperties =
            getCertificateParameters(votingCardSetCertificatePropertiesPath);

        String verificationCardSetCertificatePropertiesPath =
            "properties/verificationCardSetX509Certificate.properties";
        Properties loadedVerificationCardSetCertificateProperties =
            getCertificateParameters(verificationCardSetCertificatePropertiesPath);

        String credentailSignCertificatePropertiesPath = "properties/credentialSignX509Certificate.properties";
        Properties loadedcredentailSignCertificateProperties =
            getCertificateParameters(credentailSignCertificatePropertiesPath);

        String credentailAuthCertificatePropertiesPath = "properties/credentialSignX509Certificate.properties";
        Properties loadedCredentailAuthCertificateProperties =
            getCertificateParameters(credentailAuthCertificatePropertiesPath);

        createVotingCardSetCertificateProperties
            .setVotingCardSetCertificateProperties(loadedVotingCardSetCertificateProperties);
        createVotingCardSetCertificateProperties
            .setVerificationCardSetCertificateProperties(loadedVerificationCardSetCertificateProperties);
        createVotingCardSetCertificateProperties
            .setCredentialSignCertificateProperties(loadedcredentailSignCertificateProperties);
        createVotingCardSetCertificateProperties
            .setCredentialAuthCertificateProperties(loadedCredentailAuthCertificateProperties);

        return createVotingCardSetCertificateProperties;
    }

    private static Properties getCertificateParameters(String path) throws IOException {

        final Properties props = new Properties();

        try (InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(path)) {
            props.load(input);
        }

        return props;
    }
}
