/*
 * 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.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

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

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

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.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.commons.beans.validation.ValidationErrorType;
import com.scytl.products.ov.commons.correctness.CachedAttributeCorrectnessEvaluator;
import com.scytl.products.ov.commons.correctness.feedback.CorrectnessFeedback;
import com.scytl.products.ov.commons.correctness.feedback.ReportedError;
import com.scytl.products.ov.ei.services.domain.model.ballot.Ballot;
import com.scytl.products.ov.ei.services.domain.model.ballot.BallotRepository;

/**
 * Rule that validates the vote correctness
 */
public class VoteCorrectnessRule implements AbstractRule<Vote> {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(VoteCorrectnessRule.class);

	private static final CachedAttributeCorrectnessEvaluator CORRECTNESS_EVALUATOR = new CachedAttributeCorrectnessEvaluator(LOGGER);
	
    private static final String ENCRYPTED_CORRECTNESS_RULE = "encryptedCorrectnessRule";

    private static final String CONTESTS_FIELD = "contests";

    @Inject
    BallotRepository ballotRepository;

    /**
     * @see AbstractRule#execute(Object)
     */
    @Override
    public ValidationError execute(Vote vote) {
        ValidationError result = new ValidationError(ValidationErrorType.SUCCESS);
        try {
            final Ballot ballot =
                ballotRepository.findByTenantIdElectionEventIdBallotId(vote.getTenantId(), vote.getElectionEventId(),
                    vote.getBallotId());
            final JsonObject jsonObject = JsonUtils.getJsonObject(ballot.getJson());
            JsonArray jsonArray = jsonObject.getJsonArray(CONTESTS_FIELD);
            for (int i = 0; i < jsonArray.size(); i++) {
            	JsonObject contest = jsonArray.getJsonObject(i);
            	String contestRule = contest.getString(ENCRYPTED_CORRECTNESS_RULE);
                List<String> attributesIds = getAttributesIds(vote);
                List<List<String>> attributesToBeEvaluated = new ArrayList<>();
                
                // strategy selection depends on VP_FE, how it is sending the ballot
                for (String string : attributesIds) {
                	ArrayList<String> selectionList = new ArrayList<>();
                	selectionList.add(string);
					attributesToBeEvaluated.add(selectionList);
				}
				
                CorrectnessFeedback evaluateCorrectness = CORRECTNESS_EVALUATOR.evaluateCorrectness(CorrectnessFeedback.decorateRule(contestRule), attributesToBeEvaluated);
				if (!evaluateCorrectness.getResult()) {
					List<ReportedError> errors = evaluateCorrectness.getErrors();
					StringBuilder sb = new StringBuilder();
					for (ReportedError reportedError : errors) {
						sb.append("Failure for ").append(reportedError.getReference()).append(": ").append(reportedError.getErrorType().toString()).append(".");
					}
		            result.setErrorArgs(new String[] {"Correctness validation failed: " + sb.toString() });
		            result.setValidationErrorType(ValidationErrorType.INVALID_VOTE_CORRECTNESS);
				}
            }

        } catch (ResourceNotFoundException e) {
        	LOGGER.info("Invalid vote correctness.", e);
            result.setValidationErrorType(ValidationErrorType.INVALID_VOTE_CORRECTNESS);
            result.setErrorArgs(new String[] {"Error retrieving the ballot " });
        }

        return result;
    }
    
    private List<String> getAttributesIds(Vote vote) {
        final String correctnessAttributes = vote.getCorrectnessIds();
        return JsonUtils.getJsonArray(correctnessAttributes).stream().flatMap(x -> {
            JsonArray array = (JsonArray) x;
            return array.stream();
        }).map(value -> {
            JsonString string = (JsonString) value;
            return string.getString();
        }).collect(Collectors.toList());
    }
    /**
     * @see AbstractRule#getName()
     */
    @Override
    public String getName() {
		return RuleNames.VOTE_CORRECTNESS.getText();
	}

}
