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

import javax.inject.Inject;

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.authentication.AuthenticationContent;
import com.scytl.products.ov.au.services.domain.model.challenge.ChallengeInformation;
import com.scytl.products.ov.au.services.domain.model.validation.ChallengeInformationValidation;
import com.scytl.products.ov.au.services.domain.service.authenticationcontent.AuthenticationContentService;
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.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.logging.service.I18nLoggerMessages;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;

/**
 * This class implements the expiration time validation of the challenge information.
 */
public class ChallengeInformationExpirationTimeValidation implements ChallengeInformationValidation {

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

    private static final I18nLoggerMessages I18N = I18nLoggerMessages.getInstance();

    private static final Long CONSTANT_ZERO = 0l;

    private static final Long MILLIS = 1000l;

    @Inject
    private AuthenticationContentService authenticationContentService;

    @Inject
    private TrackIdInstance trackId;

    @Inject
    private SecureLoggingWriter secureLoggerWriter;

    private int order;

    private static final int RULE_ORDER = 1;


    public ChallengeInformationExpirationTimeValidation() {
        this.order = RULE_ORDER;
    }

    /**
     * This method implements the validation of challenge expiration time. That is, the difference between the current
     * time and the token timestamp is less or equal than the (previously configured) challenge expiration time.
     *
     * @param tenantId
     *            - the tenant identifier.
     * @param electionEventId
     *            - the election event identifier.
     * @param credentialId
     *            - the credential identifier.
     * @param challengeInformation
     *            - the challenge information to be validated.
     * @return true, if successfully validated. Otherwise, false.
     * @throws ResourceNotFoundException
     */
    @Override
    public boolean execute(final String tenantId, final String electionEventId, final String credentialId,
            final ChallengeInformation challengeInformation) throws ResourceNotFoundException {
        // get current time
        long currentTimestamp = System.currentTimeMillis();

        // convert server timestamp to long
		long serverTimestamp = Long.parseLong(challengeInformation.getServerChallengeMessage().getTimestamp());

        LOG.info(I18N.getMessage("ChallengeInformationExpirationTimeValidation.currentTimestamp") + currentTimestamp);
        LOG.info(I18N.getMessage("ChallengeInformationExpirationTimeValidation.serverTimestamp") + serverTimestamp);

        // positive server timestamp
        if (serverTimestamp < CONSTANT_ZERO) {
            LOG.info(I18N.getMessage("ChallengeInformationExpirationTimeValidation.negativeServerTimestamp"));

            secureLoggerWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder().logEvent(AuthenticationLogEvents.SERVER_CHALLENGE_EXPIRED)
                    .objectId(credentialId).user(credentialId).electionEvent(electionEventId)
                    .additionalInfo(AuthenticationLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                    .additionalInfo(AuthenticationLogConstants.INFO_ERR_DESC, "Negative value of server timestamp")
                    .createLogInfo());

            return false;
        }

        // positive difference between timestamps
        long timestampDifference = currentTimestamp - serverTimestamp;
        if (timestampDifference < CONSTANT_ZERO) {
            LOG.info(I18N.getMessage("ChallengeInformationExpirationTimeValidation.negativeDifferenceTimestamp"));

            secureLoggerWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder().logEvent(AuthenticationLogEvents.CHALLENGE_RESPONSE_NOT_VALIDATED)
                    .objectId(credentialId).user(credentialId).electionEvent(electionEventId)
                    .additionalInfo(AuthenticationLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                    .additionalInfo(AuthenticationLogConstants.INFO_ERR_DESC, "Negative difference between timestamps")
                    .createLogInfo());

            return false;
        }

        // validation result
        AuthenticationContent authenticationContent =
            authenticationContentService.getAuthenticationContent(tenantId, electionEventId);
        long expirationTimeInMilliseconds = authenticationContent.getChallengeExpirationTime() * MILLIS;

        // valid = not expired = true
        boolean valid = timestampDifference <= expirationTimeInMilliseconds;
        LOG.info(
            I18N.getMessage("ChallengeInformationExpirationTimeValidation.expirationTimestampValidationOK") + valid);

        // expired
        if (!valid) {
            secureLoggerWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder().logEvent(AuthenticationLogEvents.SERVER_CHALLENGE_EXPIRED)
                    .objectId(credentialId).user(credentialId).electionEvent(electionEventId)
                    .additionalInfo(AuthenticationLogConstants.INFO_TRACK_ID, trackId.getTrackId())
                    .additionalInfo(AuthenticationLogConstants.INFO_ERR_DESC, "Challenge expired").createLogInfo());
        }

        return valid;
    }

    @Override
    public int getOrder() {
        return order;
    }

}
