/*
 * 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.listeners;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.ItemWriteListener;
import org.springframework.batch.item.ExecutionContext;

import java.util.List;
import java.util.UUID;

import com.scytl.products.oscore.logging.api.domain.Level;
import com.scytl.products.oscore.logging.api.domain.LogContent;
import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.ov.config.commands.progress.ProgressManager;
import com.scytl.products.ov.config.logevents.ConfigGeneratorLogEvents;
import com.scytl.products.ov.sdm.config.spring.batch.GeneratedVotingCardOutput;
import com.scytl.products.ov.sdm.config.spring.batch.VotingCardGenerationJobExecutionContext;
import com.scytl.products.ov.sdm.config.spring.batch.writers.CompositeOutputWriter;

public class VotingCardGeneratedOutputWriterListener implements ItemWriteListener<GeneratedVotingCardOutput> {

    private static final Logger LOG = LoggerFactory.getLogger("std");

    private SecureLoggingWriter secureLogger;

    private ProgressManager progressManager;

    private UUID jobId;
    private VotingCardGenerationJobExecutionContext jobExecutionContext;

    private VotingCardGeneratedOutputWriterListener() {
    	
    }

    public VotingCardGeneratedOutputWriterListener(final UUID jobId, final ExecutionContext executionContext,
            final SecureLoggingWriter loggingWriter, final ProgressManager progressManager) {
        this.jobId = jobId;
        this.jobExecutionContext = new VotingCardGenerationJobExecutionContext(executionContext);
        this.secureLogger = loggingWriter;
        this.progressManager = progressManager;
    }

    /**
     * before writing an item, verify if the item is in error state. if it is, increment the error count and log the error
     * we will not filter the item here.
     * The 'root' writer {@link CompositeOutputWriter#write}
     * will make sure that we do not try to write an invalid output
     * @param items
     */
    @Override
    public void beforeWrite(final List<? extends GeneratedVotingCardOutput> items) {
        items.forEach(item -> {
            if(item.isError()) {
                int errorCount = jobExecutionContext.getErrorCount();
                jobExecutionContext.setErrorCount(errorCount + 1);

                LOG.warn("Generated output " + item.getVotingCardId() + " is in error due to an 'expected' exception. " +
                        "The output will not be written.", item.getError());

                progressManager.updateProgress(jobId, 1);
            }
        });
    }

    /**
     * after successfully writing an item (which must have been valid), increment the generated counter.
     * @param items
     */
    @Override
    public void afterWrite(final List<? extends GeneratedVotingCardOutput> items) {
        items.forEach( item -> {
            if(!item.isError()) {
                int generatedCount = jobExecutionContext.getGeneratedCardCount();
                jobExecutionContext.setGeneratedCardCount(generatedCount + 1);

                progressManager.updateProgress(jobId, 1);

                //clear passwords (FIX: 6870)
                item.getVerificationCardCredentialDataPack().clearPassword();
                item.getVoterCredentialDataPack().clearPassword();

                secureLogger.log(Level.INFO, new LogContent.LogContentBuilder()
                        .logEvent(ConfigGeneratorLogEvents.GENVCC_SUCCESS_CHOICECODES_STORED)
                        .electionEvent(item.getElectionEventId()).user("adminID").objectId(item.getVerificationCardSetId())
                        .additionalInfo("verifc_id", item.getVerificationCardId()).additionalInfo("num_cc", Integer.toString(
                                item.getVerificationCardCodesDataPack().getMapChoiceCodesToVotingOption().size()))
                        .createLogInfo());

            } else {
                // error storing the Choice Codes in the Code - Mapping Table
                secureLogger.log(Level.ERROR,
                        new LogContent.LogContentBuilder().logEvent(ConfigGeneratorLogEvents.GENVCC_ERROR_STORING_CHOICECODES)
                                .electionEvent(item.getElectionEventId()).objectId(item.getVerificationCardSetId())
                                .user("adminID").additionalInfo("verifc_id", item.getVerificationCardId())
                                .additionalInfo("err_desc", item.getError().getMessage()).createLogInfo());            }
        });
    }

    /**
     * in case any of the writers failed to write the output, log it here
     * @param exception the error
     * @param items the outputs that failed to be written.
     */
    @Override
    public void onWriteError(final Exception exception, final List<? extends GeneratedVotingCardOutput> items) {
        LOG.warn("Failed to write output values. Verify the output destination is valid and/or the application has " +
                "enough permissions.", ExceptionUtils.getRootCause(exception));

        progressManager.updateProgress(jobId, items.size());
    }
}
