/*
 * 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.au.services.domain.model.authentication;

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.domain.LogEvent;
import com.scytl.products.oscore.logging.api.writer.SecureLoggingWriter;
import com.scytl.products.ov.au.services.domain.service.exception.AuthenticationTokenGenerationException;
import com.scytl.products.ov.au.services.domain.service.exception.AuthenticationTokenSigningException;
import com.scytl.products.ov.au.services.infrastructure.log.AuthenticationLogConstants;
import com.scytl.products.ov.au.services.infrastructure.log.AuthenticationLogEvents;
import com.scytl.products.ov.au.services.infrastructure.log.SecureLoggerHelper;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;
import com.scytl.products.ov.commons.validation.ValidationError;

/**
 * Authentication token factory decorator.
 */
@Decorator
public class AuthenticationTokenFactoryDecorator implements AuthenticationTokenFactory {

	@Inject
	@Delegate
	private AuthenticationTokenFactory authenticationTokenFactory;

	@Inject
	private TrackIdInstance trackId;

	@Inject
	private SecureLoggingWriter secureLoggerWriter;

	@Inject
	private SecureLoggerHelper secureLoggerHelper;

	private static final Logger LOG = LoggerFactory.getLogger("std");
	
	/**
	 * @see com.scytl.products.ov.au.services.domain.model.authentication.AuthenticationTokenFactory#buildAuthenticationToken(java.lang.String,
	 *      java.lang.String, java.lang.String)
	 */
	@Override
	public AuthenticationTokenMessage buildAuthenticationToken(String tenantId, String electionEventId,
			String votingCardId) throws AuthenticationTokenGenerationException, AuthenticationTokenSigningException {

		// generate token
		AuthenticationTokenMessage authenticationTokenMessage;
		
		try {
			authenticationTokenMessage = authenticationTokenFactory.buildAuthenticationToken(tenantId, electionEventId,
					votingCardId);
		} catch (AuthenticationTokenSigningException ex) {
			writeSecureLogToken(votingCardId, electionEventId, AuthenticationLogEvents.ERROR_SIGNING_AUTH_TOKEN,
					"Error signing authentication token: " + ExceptionUtils.getRootCauseMessage(ex));
			throw ex;

		} catch (AuthenticationTokenGenerationException ex) {
			writeSecureLogToken(votingCardId, electionEventId, AuthenticationLogEvents.ERROR_AUTH_TOKEN_GENERATION,
					"Error generating authentication token: " + ExceptionUtils.getRootCauseMessage(ex));
			throw ex;
		}

		ValidationError validationError = authenticationTokenMessage.getValidationError();
		
		if (validationError != null) {
			switch (validationError.getValidationErrorType()) {
			case ELECTION_NOT_STARTED:
			case ELECTION_OVER_DATE:
				writeSecureLogElectionOutPeriod(votingCardId, electionEventId, validationError);
				break;
			case SUCCESS:
				break;
			default:
				LOG.error("Unknown validation error type: " + validationError.getValidationErrorType().name());
				break;
			}
		} else {
			// generate log for OK
			AuthenticationToken authenticationToken = authenticationTokenMessage.getAuthenticationToken();
			String authTokenHash = secureLoggerHelper.getAuthTokenHash(authenticationToken);
			String authTokenId = secureLoggerHelper.getAuthTokenId(authenticationToken);
			
			writeSecureLogSuccessful(votingCardId, electionEventId, authTokenHash, authTokenId);			
		}

		return authenticationTokenMessage;
	}
	
	private void writeSecureLogElectionOutPeriod(String votingCardId, String electionEventId,
			ValidationError validationError) {
		// generate log for election dates are over
		secureLoggerWriter.log(Level.ERROR, new LogContent.LogContentBuilder()
				.logEvent(AuthenticationLogEvents.ELECTION_OUT_PERIOD).objectId(votingCardId).user(votingCardId)
				.electionEvent(electionEventId)
				.additionalInfo(AuthenticationLogConstants.INFO_TRACK_ID, trackId.getTrackId())
				.additionalInfo(AuthenticationLogConstants.INFO_ERR_DESC,
						validationError.getValidationErrorType().name())
				.additionalInfo(AuthenticationLogConstants.INFO_ELECTION_DATES, validationError.getErrorArgs()[0])
				.createLogInfo());
	}

	private void writeSecureLogToken(String votingCardId, String electionEventId, LogEvent logEvent,
			String errMessage) {
		secureLoggerWriter.log(Level.ERROR,
				new LogContent.LogContentBuilder().logEvent(logEvent).objectId(votingCardId).user(votingCardId)
						.electionEvent(electionEventId)
						.additionalInfo(AuthenticationLogConstants.INFO_TRACK_ID, trackId.getTrackId())
						.additionalInfo(AuthenticationLogConstants.INFO_ERR_DESC, errMessage).createLogInfo());
	}
	
	private void writeSecureLogSuccessful(String votingCardId, String electionEventId, String authTokenHash,
			String authTokenId) {
		secureLoggerWriter.log(Level.ERROR,
				new LogContent.LogContentBuilder().logEvent(AuthenticationLogEvents.SUCCESSFUL_AUTH_TOKEN_GENERATION)
						.objectId(authTokenHash).user(votingCardId).electionEvent(electionEventId)
						.additionalInfo(AuthenticationLogConstants.INFO_TRACK_ID, trackId.getTrackId())
						.additionalInfo(AuthenticationLogConstants.INFO_AUTH_TOKEN_ID, authTokenId).createLogInfo());
	}
}
