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

import com.scytl.products.ov.commons.beans.domain.model.receipt.Receipt;
import java.io.IOException;

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

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.ApplicationException;
import com.scytl.products.ov.commons.beans.exceptions.DuplicateEntryException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.exceptions.SemanticErrorException;
import com.scytl.products.ov.commons.beans.exceptions.SyntaxErrorException;
import com.scytl.products.ov.commons.dto.CastCodeAndComputeResults;
import com.scytl.products.ov.commons.ui.ws.rs.ObjectMappers;
import com.scytl.products.ov.commons.util.JsonUtils;
import com.scytl.products.ov.commons.util.ValidationUtils;
import com.scytl.products.ov.commons.util.ZipUtils;
import com.scytl.products.ov.ei.services.domain.model.ballotbox.BallotBox;
import com.scytl.products.ov.ei.services.domain.model.ballotbox.BallotBoxRepository;
import com.scytl.products.ov.ei.services.domain.model.castcode.VoteCastCode;
import com.scytl.products.ov.ei.services.domain.model.castcode.VoteCastCodeRepository;
import com.scytl.products.ov.ei.services.domain.service.ballotbox.CleansedBallotBoxService;

@Stateless(name = "ei-VoteCastCodeService")
public class VoteCastCodeServiceImpl implements VoteCastCodeService {

    private static final String VOTE = "vote";

    @Inject
    private CleansedBallotBoxService cleansedBallotBoxService;

    @Inject
    private BallotBoxRepository ballotBoxRepository;

    @Inject
    private VoteCastCodeRepository voteCastCodeRepository;

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

    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void save(String tenantId, String electionEventId, String votingCardId, CastCodeAndComputeResults voteCastCode)
            throws ApplicationException, IOException {

        try {
            ValidationUtils.validate(voteCastCode);
        } catch (SyntaxErrorException | SemanticErrorException e) {
            LOG.error("Vote cast code validation failed", e);
            throw new ApplicationException("Vote cast code validation failed");
        }

        try {
            VoteCastCode voteCastCodeEntity = new VoteCastCode();
            voteCastCodeEntity.setVoteCastCode(voteCastCode.getVoteCastCode());
            voteCastCodeEntity.setSignature(voteCastCode.getSignature());
            voteCastCodeEntity.setComputationResults(ZipUtils.zipText(voteCastCode.getComputationResults()));
            
            voteCastCodeRepository.save(tenantId, electionEventId, votingCardId, voteCastCodeEntity);
        } catch (DuplicateEntryException e) {
            LOG.error("Error saving vote cast code due to duplicate entry", e);
            throw new ApplicationException("Duplicate vote cast code");
        }

        try {
            BallotBox ballotBox =
                ballotBoxRepository.findByTenantIdElectionEventIdVotingCardId(tenantId, electionEventId, votingCardId);
            String voteString = ballotBox.getVote();
            JsonObject voteJsonObject = JsonUtils.getJsonObject(voteString);
            Vote vote = ObjectMappers.fromJson(voteJsonObject.get(VOTE).toString(), Vote.class);
            Receipt receipt = getReceiptFromJson(voteString);
            cleansedBallotBoxService.storeCleansedVote(vote);
            cleansedBallotBoxService.storeSuccessfulVote(tenantId, electionEventId, ballotBox.getBallotBoxId(), votingCardId, receipt.getReceipt());
        } catch (ResourceNotFoundException e) {
            LOG.error("Ballot box not found", e);
            throw new ApplicationException("Ballot box not found");
        } catch (DuplicateEntryException e) {
            LOG.error("Duplicate cleansed ballot box", e);
            throw new ApplicationException("Duplicate cleansed ballot box");
        }
    }

    private Receipt getReceiptFromJson(String voteJsonString) throws ApplicationException {
        JsonObject voteJson = JsonUtils.getJsonObject(voteJsonString);
        JsonObject receiptJson = voteJson.getJsonObject("receipt");
        if (receiptJson == null || receiptJson.isEmpty()) {
            throw new ApplicationException("Inconsistency on server db : vote without receipt");
        }
        try {
            return ObjectMappers.fromJson(receiptJson.toString(), Receipt.class);
        } catch (IOException e) {
            throw new ApplicationException("Error parsing the receipt", e);
        }
    }
}
