/*
 * 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.vw.services.infrastructure.remote;

import javax.decorator.Decorator;
import javax.decorator.Delegate;
import javax.inject.Inject;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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.commons.beans.domain.model.vote.Vote;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.validation.ValidationErrorType;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;
import com.scytl.products.ov.commons.validation.ValidationError;
import com.scytl.products.ov.commons.validation.ValidationResult;
import com.scytl.products.ov.vw.infrastructure.log.SecureLoggerInformation;
import com.scytl.products.ov.vw.infrastructure.log.VotingWorkflowLogConstants;
import com.scytl.products.ov.vw.infrastructure.log.VotingWorkflowLogEvents;
import com.scytl.products.ov.vw.services.domain.model.validation.ValidationRepository;
import com.scytl.products.ov.vw.services.domain.model.vote.ValidationVoteResult;

import com.scytl.products.ov.commons.infrastructure.remote.client.RetrofitException;

import static com.scytl.products.oscore.logging.api.domain.Level.INFO;

/**
 * Decorator of the validation repository.
 */
@Decorator
public abstract class ValidationRepositoryDecorator implements ValidationRepository {

    @Inject
    @Delegate
    private ValidationRepository validationRepository;

    @Inject
    private TrackIdInstance trackId;

    @Inject
    private SecureLoggingWriter secureLoggerWriter;

    @Inject
    private SecureLoggerInformation secureLoggerInformation;

    private static final Logger LOG = LoggerFactory.getLogger("std");
    
    /**
     * @see com.scytl.products.ov.vw.services.domain.model.validation.ValidationRepository#validateVoteInVV(String,
     *      String, Vote)
     */
    @Override
    public ValidationResult validateVoteInVV(String tenantId, String electionEventId, Vote vote)
            throws ResourceNotFoundException {
        try {
            ValidationResult validationResultVV =
                validationRepository.validateVoteInVV(tenantId, electionEventId, vote);
            if (!validationResultVV.isResult()) {
                secureLoggerWriter.log(Level.ERROR, new LogContent.LogContentBuilder()
                    .logEvent(VotingWorkflowLogEvents.VOTE_VALIDATION_ERROR_VV)
                    .objectId(secureLoggerInformation.getAuthenticationTokenHash()).user(vote.getVotingCardId())
                    .electionEvent(vote.getElectionEventId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_VOTE_HASH, secureLoggerInformation.getVoteHash())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_VERIFICATION_CARD_ID, vote.getVerificationCardId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_ERR_DESC,
                        "Error validating the vote in vote verification")
                    .createLogInfo());
            }
            return validationResultVV;
        } catch (ResourceNotFoundException e) {
            secureLoggerWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder().logEvent(VotingWorkflowLogEvents.VOTE_VALIDATION_ERROR_VV)
                    .objectId(secureLoggerInformation.getAuthenticationTokenHash()).user(vote.getVotingCardId())
                    .electionEvent(vote.getElectionEventId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_VOTE_HASH, secureLoggerInformation.getVoteHash())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_VERIFICATION_CARD_ID, vote.getVerificationCardId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_ERR_DESC,
                        "Exception occurs validating the vote in vote verification: "
                            + ExceptionUtils.getRootCauseMessage(e))
                    .createLogInfo());
            throw e;
        }
    }

    /**
     * @see com.scytl.products.ov.vw.services.domain.model.validation.ValidationRepository#validateVoteInEI(String,
     *      String, Vote)
     */
    @Override
    public ValidationResult validateVoteInEI(String tenantId, String electionEventId, Vote vote)
            throws ResourceNotFoundException {
        try {
            ValidationResult validationResultEI =
                validationRepository.validateVoteInEI(tenantId, electionEventId, vote);
            if (!validationResultEI.isResult()) {
                // log validation error
                secureLoggerWriter.log(Level.ERROR, new LogContent.LogContentBuilder()
                    .logEvent(VotingWorkflowLogEvents.VOTE_VALIDATION_ERROR_EI)
                    .objectId(secureLoggerInformation.getAuthenticationTokenHash()).user(vote.getVotingCardId())
                    .electionEvent(vote.getElectionEventId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_ERR_DESC,
                        "Error validating the vote in election information: ")
                    .additionalInfo(VotingWorkflowLogConstants.INFO_VOTE_HASH, secureLoggerInformation.getVoteHash())
                    .createLogInfo());
            }
            return validationResultEI;
        } catch (ResourceNotFoundException e) {
            secureLoggerWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder().logEvent(VotingWorkflowLogEvents.VOTE_VALIDATION_ERROR_EI)
                    .objectId(secureLoggerInformation.getAuthenticationTokenHash()).user(vote.getVotingCardId())
                    .electionEvent(vote.getElectionEventId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_VOTE_HASH, secureLoggerInformation.getVoteHash())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_ERR_DESC,
                        "Exception occurs validating the vote in election information: "
                            + ExceptionUtils.getRootCauseMessage(e))
                    .createLogInfo());
            throw e;
        }
    }

    /**
     * @see com.scytl.products.ov.vw.services.domain.model.validation.ValidationRepository#validateElectionDatesInEI(java.lang.String,
     *      java.lang.String, java.lang.String)
     */
    @Override
    public ValidationResult validateElectionDatesInEI(String tenantId, String electionEventId, String ballotBoxId)
            throws ResourceNotFoundException {
        try {
            LOG.info("Validating election dates in authentication");
            ValidationResult ballotBoxStatus =
                validationRepository.validateElectionDatesInEI(tenantId, electionEventId, ballotBoxId);
            LOG.info("Ballot box status is: {}", ballotBoxStatus.getValidationError().getValidationErrorType().name());
            if (ballotBoxStatus.isResult()) {
                secureLoggerWriter.log(Level.INFO, new LogContent.LogContentBuilder()
                    .logEvent(VotingWorkflowLogEvents.ELECTIONEVENT_DATE_VALIDATION_SUCCESS).objectId(ballotBoxId)
                    .electionEvent(electionEventId)
                    .additionalInfo(VotingWorkflowLogConstants.INFO_TRACK_ID, trackId.getTrackId()).createLogInfo());

            } else {
                secureLoggerWriter.log(Level.INFO, new LogContent.LogContentBuilder()
                    .logEvent(VotingWorkflowLogEvents.ELECTIONEVENT_DATE_VALIDATION_ERROR).objectId(ballotBoxId)
                    .electionEvent(electionEventId)
                    .additionalInfo(VotingWorkflowLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_ERR_DESC, "Election event dates failed to validate")
                    .createLogInfo());
            }
            return ballotBoxStatus;
        } catch (ResourceNotFoundException e) {
            secureLoggerWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder().logEvent(VotingWorkflowLogEvents.VOTE_VALIDATION_ERROR_EI)
                    .objectId(ballotBoxId).user("").electionEvent(electionEventId)
                    .additionalInfo(VotingWorkflowLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_ERR_DESC,
                        "Exception occurs validating the election dates: " + ExceptionUtils.getRootCauseMessage(e))
                    .createLogInfo());
            LOG.error("Error validating dates", e);
            throw e;
        }
    }

    /**
     * @see com.scytl.products.ov.vw.services.domain.model.validation.ValidationRepository#validateVote(String,
     *      String, Vote)
     */
    @Override
    public ValidationVoteResult validateVote(String tenantId, String electionEventId, Vote vote)
            throws ResourceNotFoundException {
        ValidationVoteResult validationVoteResult;
        try {
            validationVoteResult = validationRepository.validateVote(tenantId, electionEventId, vote);
            if (validationVoteResult.isValid()) {
                secureLoggerWriter.log(Level.INFO, new LogContent.LogContentBuilder()
                    .logEvent(VotingWorkflowLogEvents.VOTE_VALIDATION_OK)
                    .objectId(secureLoggerInformation.getAuthenticationTokenHash()).user(vote.getVotingCardId())
                    .electionEvent(vote.getElectionEventId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                    .additionalInfo(VotingWorkflowLogConstants.INFO_VOTE_HASH, secureLoggerInformation.getVoteHash())
                    .createLogInfo());
            } else {
                secureLoggerWriter.log(INFO,
                    new LogContent.LogContentBuilder().logEvent(VotingWorkflowLogEvents.STOP_VOTING_PROCESS)
                        .objectId(secureLoggerInformation.getAuthenticationTokenHash()).user(vote.getVotingCardId())
                        .electionEvent(electionEventId)
                        .additionalInfo(VotingWorkflowLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                        .additionalInfo(VotingWorkflowLogConstants.INFO_ERR_DESC, "Error validating the vote")
                        .createLogInfo());
            }
            return validationVoteResult;
        } catch (RetrofitException e) {
            // already secure logged
            LOG.error("Error validating vote: ", e);
            if (e.getHttpCode() == 404) {
                throw new ResourceNotFoundException("Vote was not found", e);
            } else {
                validationVoteResult = new ValidationVoteResult();
                validationVoteResult.setValid(false);
                final ValidationError validationError = new ValidationError(ValidationErrorType.FAILED);
                validationError.setErrorArgs(new String[] {e.getMessage() });
                validationVoteResult.setValidationError(validationError);
            }
        }
        return validationVoteResult;
    }
}
