/*
 * 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.ei.services.domain.service.confirmation;

import java.math.BigInteger;
import java.util.Base64;

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

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;
import com.scytl.products.ov.commons.beans.domain.model.authentication.AuthenticationToken;
import com.scytl.products.ov.commons.beans.exceptions.ConfirmationMessageMathematicalGroupValidationException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.validation.ValidationErrorType;
import com.scytl.products.ov.commons.util.JsonUtils;
import com.scytl.products.ov.commons.validation.ValidationError;
import com.scytl.products.ov.ei.services.domain.model.ballotbox.BallotBoxInformation;
import com.scytl.products.ov.ei.services.domain.model.ballotbox.BallotBoxInformationRepository;
import com.scytl.products.ov.ei.services.domain.model.confirmation.ConfirmationInformation;
import com.scytl.products.ov.ei.services.domain.model.confirmation.ConfirmationMessageValidation;

/**
 * This class implements the confirmation message is member of a mathematical group.
 */
public class ConfirmationMessageMathematicalGroupValidation implements ConfirmationMessageValidation {

    private static final String ENCRYPTION_PARAMETERS = "encryptionParameters";

    private static final String ENCRYPTION_PARAMETER_P = "p";

    private static final String ENCRYPTION_PARAMETER_Q = "q";

    private static final String ENCRYPTION_PARAMETER_G = "g";

    @Inject
    private BallotBoxInformationRepository ballotBoxInformationRepository;

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

    /**
     * This method implements the validation of confirmation message is member of a mathematical group.
     *
     * @param tenantId
     *            - the tenant identifier.
     * @param electionEventId
     *            - the election event identifier.
     * @param votingCardId
     *            - the voting card identifier.
     * @param confirmationInformation
     *            - the confirmation information to be validated.
     * @param authenticationToken
     *            - the authentication token.
     * @return A ValidationError describing if the rule is satisfied or not.
     */
    @Override
    public ValidationError execute(String tenantId, String electionEventId, String votingCardId,
            ConfirmationInformation confirmationInformation, AuthenticationToken authenticationToken) {
        ValidationError result = new ValidationError();

        BigInteger p, q, g;
        try {
            // encryption parameters
            String ballotBoxId = authenticationToken.getVoterInformation().getBallotBoxId();
            BallotBoxInformation ballotBoxInformation = ballotBoxInformationRepository
                .findByTenantIdElectionEventIdBallotBoxId(tenantId, electionEventId, ballotBoxId);
            JsonObject json = JsonUtils.getJsonObject(ballotBoxInformation.getJson());
            JsonObject encryptionParameters = json.getJsonObject(ENCRYPTION_PARAMETERS);
            p = new BigInteger(encryptionParameters.getString(ENCRYPTION_PARAMETER_P));
            q = new BigInteger(encryptionParameters.getString(ENCRYPTION_PARAMETER_Q));
            g = new BigInteger(encryptionParameters.getString(ENCRYPTION_PARAMETER_G));
        } catch (ResourceNotFoundException | NumberFormatException e) {
            throw new ConfirmationMessageMathematicalGroupValidationException("Error trying to validate confirmation message.", e);
        }

        try {
            // confirmation key
            BigInteger value = new BigInteger(new String(
                Base64.getDecoder().decode(confirmationInformation.getConfirmationMessage().getConfirmationKey())));

            // element
            ZpGroupElement element = new ZpGroupElement(value, p, q);

            // group
            ZpSubgroup group = new ZpSubgroup(g, p, q);

            // is group member?
            if (group.isGroupMember(element)) {
                result.setValidationErrorType(ValidationErrorType.SUCCESS);
            }
        } catch (GeneralCryptoLibException | NumberFormatException e) {
            LOG.error("Error trying to validate confirmation message.", e);
            result.setErrorArgs(new String[] {ExceptionUtils.getRootCauseMessage(e) });
        }
        return result;
    }
}
