/*
 * 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.ea.services.infrastructure.persistence;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;

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

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
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.exceptions.ApplicationException;
import com.scytl.products.ov.commons.beans.exceptions.DuplicateEntryException;
import com.scytl.products.ov.commons.beans.exceptions.EntryPersistenceException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.tracking.TrackIdInstance;
import com.scytl.products.ov.ea.domain.model.EncryptedSVK;
import com.scytl.products.ov.ea.domain.model.extendedauthentication.AllowedAttemptsExceededException;
import com.scytl.products.ov.ea.domain.model.extendedauthentication.ExtendedAuthentication;
import com.scytl.products.ov.ea.services.infrastructure.logging.ExtendedAuthenticationLogConstants;
import com.scytl.products.ov.ea.services.infrastructure.logging.ExtendedAuthenticationLogEvents;

/**
 * Decorator for Extended Authentication Service containing the Secure Logger requirements.
 * {@link com.scytl.products.ov.ea.services.infrastructure.persistence.ExtendedAuthenticationService}
 */
@Decorator
public class ExtendedAuthenticationServiceDecorator implements ExtendedAuthenticationService {

    @Inject
    @Delegate
    private ExtendedAuthenticationService extendedAuthenticationService;

    @Inject
    private TrackIdInstance trackIdInstance;

    @Inject
    private SecureLoggingWriter secureLoggingWriter;

    /**
     * {@inheritDoc}
     * 
     * @throws GeneralCryptoLibException
     */
    @Override
    public EncryptedSVK authenticate(String tenantId, String authId, String extraParam, String electionEventId)
            throws ResourceNotFoundException, AllowedAttemptsExceededException, AuthenticationException,
            ApplicationException, GeneralCryptoLibException {

        try {
            final EncryptedSVK encryptedSVK =
                extendedAuthenticationService.authenticate(tenantId, authId, extraParam, electionEventId);
            secureLoggingWriter.log(Level.INFO,
                new LogContent.LogContentBuilder().logEvent(ExtendedAuthenticationLogEvents.EAAUTH_SUCCESS)
                    .objectId(authId).user(authId).electionEvent(electionEventId)
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .createLogInfo());

            return encryptedSVK;
        } catch (ResourceNotFoundException e) {
            secureLoggingWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder()
                    .logEvent(ExtendedAuthenticationLogEvents.EAAUTH_ERROR_AUTHID_NOT_FOUND).objectId(authId)
                    .user(authId).electionEvent(electionEventId)
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.ERR_DESC, e.getMessage()).createLogInfo());
            throw e;
        } catch (AllowedAttemptsExceededException e) {
            secureLoggingWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder()
                    .logEvent(ExtendedAuthenticationLogEvents.EAAUTH_ERROR_MAX_ATTEMPTS_REACHED).objectId(authId)
                    .user(authId).electionEvent(electionEventId)
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.ERR_DESC, e.getMessage()).createLogInfo());
            throw e;
        } catch (AuthenticationException | ApplicationException e) {
            secureLoggingWriter.log(Level.ERROR, new LogContent.LogContentBuilder()
                .logEvent(ExtendedAuthenticationLogEvents.EAAUTH_ERROR_INVALID_ATTEMPT).objectId(authId).user(authId)
                .electionEvent(electionEventId)
                .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                .additionalInfo(ExtendedAuthenticationLogConstants.ERR_DESC, "Attempt is not valid").createLogInfo());
            throw e;
        }

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ExtendedAuthentication renewExistingExtendedAuthentication(String tenantId, String oldAuthId,
            String newAuthId, String newSVK, String electionEventId)
            throws ApplicationException, ResourceNotFoundException {

        ExtendedAuthentication extendedAuthentication;

        try {
            extendedAuthentication = extendedAuthenticationService.renewExistingExtendedAuthentication(tenantId,
                oldAuthId, newAuthId, newSVK, electionEventId);

            secureLoggingWriter.log(Level.INFO,
                new LogContent.LogContentBuilder().logEvent(ExtendedAuthenticationLogEvents.EARENEW_SAVE_SUCCESS)
                    .objectId(oldAuthId).user(newAuthId).electionEvent(extendedAuthentication.getElectionEvent())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_CREDENTIAL_ID,
                        extendedAuthentication.getCredentialId())
                    .createLogInfo());

        } catch (ApplicationException | ResourceNotFoundException e) {
            secureLoggingWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder().logEvent(ExtendedAuthenticationLogEvents.EARENEW_ERROR_SAVING_DATA)
                    .objectId(oldAuthId).user(newAuthId).electionEvent(electionEventId)
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.ERR_DESC, e.getMessage()).createLogInfo());

            throw e;
        }

        return extendedAuthentication;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ExtendedAuthentication retrieveExistingExtendedAuthenticationForUpdate(String tenantId, String authId,
            String electionEventId) throws ResourceNotFoundException, ApplicationException {
        return extendedAuthenticationService.retrieveExistingExtendedAuthenticationForUpdate(tenantId, authId,
            electionEventId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ExtendedAuthentication retrieveExistingExtendedAuthenticationForRead(String tenantId, String authId,
            String electionEventId) throws ResourceNotFoundException, ApplicationException {
        return extendedAuthenticationService.retrieveExistingExtendedAuthenticationForRead(tenantId, authId,
            electionEventId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean saveExtendedAuthenticationFromFile(Path extendedAuthenticationFilePath, String tenantId,
            String electionEventId, String adminBoardId) throws IOException {

        try {
            boolean saveExtendedAuthenticationFromFile =
                extendedAuthenticationService.saveExtendedAuthenticationFromFile(extendedAuthenticationFilePath,
                    tenantId, electionEventId, adminBoardId);
            secureLoggingWriter.log(Level.INFO,
                new LogContent.LogContentBuilder().logEvent(ExtendedAuthenticationLogEvents.EAMULSTO_SUCCESS)
                    .objectId("").user(tenantId).electionEvent(electionEventId)
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_ADMIN_BOARD_ID, adminBoardId)
                    .createLogInfo());
            return saveExtendedAuthenticationFromFile;
        } catch (IOException e) {
            secureLoggingWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder().logEvent(ExtendedAuthenticationLogEvents.EAMULSTO_ERROR).objectId("")
                    .user(tenantId).electionEvent(electionEventId)
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_ADMIN_BOARD_ID, adminBoardId)
                    .createLogInfo());
            throw e;
        }

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updateExistingExtendedAuthentication(ExtendedAuthentication extendedAuthentication)
            throws EntryPersistenceException {

        String authId = extendedAuthentication.getAuthId();

        try {
            extendedAuthenticationService.updateExistingExtendedAuthentication(extendedAuthentication);

            secureLoggingWriter.log(Level.INFO,
                new LogContent.LogContentBuilder().logEvent(ExtendedAuthenticationLogEvents.EAUPDATE_SUCCESS)
                    .objectId(authId).user(authId).electionEvent(extendedAuthentication.getElectionEvent())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_CREDENTIAL_ID,
                        extendedAuthentication.getCredentialId())
                    .createLogInfo());

        } catch (EntryPersistenceException e) {
            secureLoggingWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder().logEvent(ExtendedAuthenticationLogEvents.EAUPDATE_ERROR_UPDATING)
                    .objectId(authId).user(authId).electionEvent(extendedAuthentication.getElectionEvent())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_CREDENTIAL_ID,
                        extendedAuthentication.getCredentialId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.ERR_DESC, e.getMessage()).createLogInfo());

            throw e;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void saveNewExtendedAuthentication(ExtendedAuthentication extendedAuthentication)
            throws DuplicateEntryException {

        String authId = extendedAuthentication.getAuthId();

        try {
            extendedAuthenticationService.saveNewExtendedAuthentication(extendedAuthentication);

            secureLoggingWriter.log(Level.INFO,
                new LogContent.LogContentBuilder().logEvent(ExtendedAuthenticationLogEvents.EASAVE_SUCCESS)
                    .objectId(authId).user(authId).electionEvent(extendedAuthentication.getElectionEvent())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_CREDENTIAL_ID,
                        extendedAuthentication.getCredentialId())
                    .createLogInfo());

        } catch (DuplicateEntryException e) {

            secureLoggingWriter.log(Level.ERROR,
                new LogContent.LogContentBuilder().logEvent(ExtendedAuthenticationLogEvents.EASAVE_ERROR_SAVING)
                    .objectId(authId).user(authId).electionEvent(extendedAuthentication.getElectionEvent())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_TRACK_ID, trackIdInstance.getTrackId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.INFO_CREDENTIAL_ID,
                        extendedAuthentication.getCredentialId())
                    .additionalInfo(ExtendedAuthenticationLogConstants.ERR_DESC, e.getMessage()).createLogInfo());

            throw e;
        }
    }

    @Override
    public void findAndWriteVotingCardsWithFailedAuthentication(final String tenantId, final String electionEventId,
            final OutputStream stream) throws IOException {
        extendedAuthenticationService.findAndWriteVotingCardsWithFailedAuthentication(tenantId, electionEventId,
            stream);
    }
}
