/*
 * 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.model.rule;

import java.math.BigInteger;

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.proofs.ProofsServiceAPI;
import com.scytl.cryptolib.mathematical.groups.impl.ZpGroupElement;
import com.scytl.cryptolib.mathematical.groups.impl.ZpSubgroup;
import com.scytl.cryptolib.proofs.proof.Proof;
import com.scytl.products.ov.commons.beans.domain.model.vote.Vote;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.validation.ValidationErrorType;
import com.scytl.products.ov.commons.crypto.Utils;
import com.scytl.products.ov.commons.domain.model.rule.AbstractRule;
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;

/**
 * This is an implementation of a validator of the Schnorr Proof.
 */
public class SchnorrProofRule implements AbstractRule<Vote> {

    // The name of the json parameter p (group).
    private static final String JSON_PARAMTER_P = "p";

    // The name of the json parameter q (order).
    private static final String JSON_PARAMETER_Q = "q";

    // The name of the json parameter generator.
    private static final String JSON_PARAMETER_GENERATOR = "g";

    // The name of the json parameter encryptionParameters.
    private static final String JSON_PARAMETER_ENCRYPTION_PARAMETERS = "encryptionParameters";

    @Inject
    private BallotBoxInformationRepository ballotBoxContentRepository;

    @Inject
    private ProofsServiceAPI proofsService;

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

    /**
     * This method validates in the given vote the Schnorr proof. The steps are: generating mathematical group based on
     * encryption parameters, extracting the first part from the encrypted options, and in the end using them to call
     * the verifier from the cryptolib.
     * 
     * @see com.scytl.products.ov.commons.domain.model.rule.AbstractRule#execute(com.scytl.products.ov.ei.services.domain.model.vote.Vote)
     */
    @Override
    public ValidationError execute(Vote vote) {
        ValidationError result = new ValidationError();

        // the encryption parameters (p, q, g) are in the ballot box information
        BallotBoxInformation ballotBoxInformation = null;
        try {
            ballotBoxInformation = ballotBoxContentRepository.findByTenantIdElectionEventIdBallotBoxId(
                vote.getTenantId(), vote.getElectionEventId(), vote.getBallotBoxId());
        } catch (ResourceNotFoundException e) {
            LOG.error("", e);
            return result;
        }

        // there is no other way, so we parse the json manually to read the
        // encryption parameters
        JsonObject ballotBoxInformationJson = JsonUtils.getJsonObject(ballotBoxInformation.getJson());
        JsonObject encryptionParametersJson =
            ballotBoxInformationJson.getJsonObject(JSON_PARAMETER_ENCRYPTION_PARAMETERS);
        String generatorEncryptParam = encryptionParametersJson.getString(JSON_PARAMETER_GENERATOR);
        String pEncryptParam = encryptionParametersJson.getString(JSON_PARAMTER_P);
        String qEncryptParam = encryptionParametersJson.getString(JSON_PARAMETER_Q);

        try {
            ZpSubgroup mathematicalGroup = new ZpSubgroup(new BigInteger(generatorEncryptParam),
                new BigInteger(pEncryptParam), new BigInteger(qEncryptParam));
            BigInteger encryptedParamC0 = new BigInteger(Utils.getC0FromEncryptedOptions(vote.getEncryptedOptions()));
            ZpGroupElement groupElement = new ZpGroupElement(encryptedParamC0, mathematicalGroup);

            if (proofsService.createProofVerifierAPI(mathematicalGroup).verifySchnorrProof(groupElement,
                vote.getVotingCardId(), vote.getElectionEventId(), Proof.fromJson(vote.getSchnorrProof()))) {
                result.setValidationErrorType(ValidationErrorType.SUCCESS);
            }
            // for now any exception that could occur will be just caught and
            // logged
            // making the result of the validation being a false
            // the number format exception is in case there is a problem with
            // the instantiation of a BigInteger for the
            // encryption parameters
        } catch (GeneralCryptoLibException | NumberFormatException e) {
            LOG.error("", e);
        }

        return result;
    }

    /**
     * @see com.scytl.products.ov.commons.domain.model.rule.AbstractRule#getName()
     */
    @Override
    public String getName() {
        return RuleNames.VOTE_SCHNORR_PROFF.getText();
    }
}
