/*
 * 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
 */
/**
 * Created by riglesias on 17/03/15.
 */
package com.scytl.products.ov.ei.services.domain.model.vote;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.json.JsonObject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.primitives.PrimitivesServiceAPI;
import com.scytl.products.ov.commons.beans.domain.model.receipt.Receipt;
import com.scytl.products.ov.commons.beans.domain.model.vote.Vote;
import com.scytl.products.ov.commons.beans.exceptions.ApplicationException;
import com.scytl.products.ov.commons.beans.exceptions.ApplicationExceptionMessages;
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.InputDataFormatterService;
import com.scytl.products.ov.commons.crypto.SignatureForObjectService;
import com.scytl.products.ov.commons.logging.I18nLoggerMessages;
import com.scytl.products.ov.commons.util.JsonUtils;

/**
 * Service which will generate the receipt of a vote.
 */
@Stateless
public class ReceiptFactoryImpl implements ReceiptFactory {

    // The keystore alias
    private static final String KEYSTORE_ALIAS = "privatekey";

    private static final Logger LOG = LoggerFactory.getLogger(ReceiptFactoryImpl.class);

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

    @Inject
    private PrimitivesServiceAPI primitivesService;

    @Inject
    private InputDataFormatterService inputDataFormatterService;

    // The signature service
    @Inject
    private SignatureForObjectService signatureService;

    /**
     * @see com.scytl.products.ov.ei.services.domain.model.vote.ReceiptFactory#generate(com.scytl.products.ov.ei.services.domain.model.vote.Vote,
     *      java.lang.String)
     */
    @Override
    public Receipt generate(Vote vote, String authToken) throws ApplicationException, CryptographicOperationException {
        validateVote(vote);
        validateAuthenticationToken(authToken);

        LOG.info(I18N.getMessage("ReceiptGeneratorServiceImpl.generate.generatingReceipt"), vote.getTenantId(),
            vote.getBallotId());

        try {

            JsonObject object = JsonUtils.getJsonObject(authToken);
            String tokenSignature = object.getString("signature");

            Receipt receipt = new Receipt();
            byte[] hashBytes =
                primitivesService.getHash(inputDataFormatterService.concatenate(vote.getSignature(), tokenSignature,
                    vote.getVerificationCardPKSignature(), vote.getElectionEventId(), vote.getVotingCardId()));

            receipt.setReceipt(new String(Base64.getEncoder().encode(hashBytes), StandardCharsets.UTF_8));

            LOG.info(I18N.getMessage("ReceiptGeneratorServiceImpl.generate.receiptGenerated"), receipt.getReceipt(),
                vote.getTenantId(), vote.getBallotId());

            return receipt;
        } catch (GeneralCryptoLibException e) {
            throw new CryptographicOperationException("Could not calculate the receipt", e);
        }
    }

    // validate fields of a vote.
    private void validateVote(Vote vote) throws ApplicationException {
        if (vote == null) {
            throw new ApplicationException(ApplicationExceptionMessages.EXCEPTION_MESSAGE_VOTE_IS_NULL);
        }
        validateInput(vote.getEncryptedOptions());
        validateInput(vote.getElectionEventId());
        validateInput(vote.getVotingCardId());
        validateInput(vote.getVerificationCardPublicKey());
        validateInput(vote.getAuthenticationToken());
        validateInput(vote.getAuthenticationTokenSignature());
        validateInput(vote.getBallotBoxId());
        validateInput(vote.getBallotId());
        validateInput(vote.getCertificate());
        validateInput(vote.getCorrectnessIds());
        validateInput(vote.getCredentialId());

        // as receipt is generated both with and withour confirmation
        // and PCCs and proofs are not stored when not confirmation required
        // do not validate their presence

        validateInput(vote.getSchnorrProof());
        validateInput(vote.getSignature());
        validateInput(vote.getTenantId());
        validateInput(vote.getVerificationCardId());
        validateInput(vote.getVerificationCardPKSignature());
        validateInput(vote.getVerificationCardSetId());
    }

    // validate authentication token
    private void validateAuthenticationToken(String authToken) throws ApplicationException {
        validateInput(authToken);
    }

    // validate string parameter
    private void validateInput(String parameters) throws ApplicationException {
        if (parameters == null || parameters.isEmpty()) {
            throw new ApplicationException("Invalid parameter: it's null or empty");
        }
    }

    /**
     * @see com.scytl.products.ov.ei.services.domain.model.vote.ReceiptFactory#sign(Receipt,
     *      Vote)
     */
    @Override
    public String sign(Receipt receipt, Vote vote) throws CryptographicOperationException, ResourceNotFoundException {
        // signed receipt over receipt
        byte[] receiptSignature = signatureService.sign(vote.getTenantId(), vote.getElectionEventId(),
            vote.getBallotBoxId(), KEYSTORE_ALIAS, receipt.getReceipt());
        return new String(Base64.getEncoder().encode(receiptSignature), StandardCharsets.UTF_8);
    }
}
