/*
 * 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.ejb.Stateless;
import javax.inject.Inject;
import java.util.Base64;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.products.ov.au.services.domain.model.information.VoterInformation;
import com.scytl.products.ov.au.services.domain.model.information.VoterInformationRepository;
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.remote.ValidationRepositoryImpl;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.validation.CryptographicOperationException;
import com.scytl.products.ov.commons.crypto.SignatureForObjectService;
import com.scytl.products.ov.commons.random.impl.SecureRandomBytes;
import com.scytl.products.ov.commons.util.DateUtils;


/**
 * Factory class for building authentication token.
 */
@Stateless
public class AuthenticationTokenFactoryImpl implements AuthenticationTokenFactory {

    private static final String KEYSTORE_ALIAS = "privatekey";

    // the length of the token id
    private static final int LENGTH_TOKEN_ID = 16;

    private static final String EMPTY_ID = "";

    @Inject
    private VoterInformationRepository voterInformationRepository;

    @Inject
    private SignatureForObjectService signatureService;

    @Inject
    private ValidationRepositoryImpl validationRepository;

    /**
     * @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(final String tenantId, final String electionEventId,
                                                               final String credentialId)
        throws AuthenticationTokenGenerationException, AuthenticationTokenSigningException {
        try {
            // get information for auth token generation
            VoterInformation voterInformation = voterInformationRepository
                .findByTenantIdElectionEventIdCredentialId(tenantId, electionEventId, credentialId);

            // validates that election is in dates
            AuthenticationTokenMessage authenticationTokenMessage = new AuthenticationTokenMessage();
            com.scytl.products.ov.commons.validation.ValidationResult validateIfElectionIsOpen = validationRepository
                .validateElectionInDates(tenantId, electionEventId, voterInformation.getBallotBoxId());

            // always include the auth-message and validation-error in the auth-token
            authenticationTokenMessage.setValidationError(validateIfElectionIsOpen.getValidationError());

            String base64AuthenticationTokenId =
                Base64.getEncoder().encodeToString(SecureRandomBytes.getInstance().getRandomValue(LENGTH_TOKEN_ID));
            String currentTimestamp = DateUtils.getTimestamp();
            byte[] tokenSignature = signAuthenticationToken(tenantId, electionEventId, voterInformation,
                base64AuthenticationTokenId, currentTimestamp);

            // create token
            AuthenticationToken authenticationToken = new AuthenticationToken(voterInformation,
                base64AuthenticationTokenId, currentTimestamp, Base64.getEncoder().encodeToString(tokenSignature));
            authenticationTokenMessage.setAuthenticationToken(authenticationToken);
            return authenticationTokenMessage;
        } catch (ResourceNotFoundException | GeneralCryptoLibException e) {
            throw new AuthenticationTokenGenerationException("Error generating the authentication token", e);
        }
    }

    private byte[] signAuthenticationToken(final String tenantId, final String electionEventId,
                                           final VoterInformation voterInformation, final String base64AuthenticationTokenId,
                                           final String currentTimestamp) throws AuthenticationTokenSigningException {
        // sign the auth token
        try {
            return signatureService.sign(tenantId, electionEventId, EMPTY_ID, KEYSTORE_ALIAS,
                base64AuthenticationTokenId, currentTimestamp, voterInformation.getTenantId(),
                voterInformation.getElectionEventId(), voterInformation.getVotingCardId(),
                voterInformation.getBallotId(), voterInformation.getCredentialId(),
                voterInformation.getVerificationCardId(), voterInformation.getBallotBoxId(),
                voterInformation.getVerificationCardSetId(), voterInformation.getVotingCardSetId());
        } catch (CryptographicOperationException | ResourceNotFoundException e) {
            throw new AuthenticationTokenSigningException("Error signing authentication token", e);
        }
    }

}
