/*
 * 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.sdm.application.service;

import com.fasterxml.jackson.core.JsonProcessingException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.scytl.products.ov.commons.beans.domain.model.messaging.PayloadVerificationException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.dto.ChoiceCodeGenerationReqPayload;
import com.scytl.products.ov.commons.verify.PayloadVerifier;
import com.scytl.products.ov.sdm.application.log.SdmSecureLogEvent;
import com.scytl.products.ov.sdm.domain.model.status.InvalidStatusTransitionException;
import com.scytl.products.ov.sdm.domain.model.status.Status;
import com.scytl.products.ov.sdm.infrastructure.cc.ChoiceCodeGenerationRequestPayloadRepository;
import com.scytl.products.ov.sdm.infrastructure.cc.PayloadStorageException;
import com.scytl.products.ov.sdm.infrastructure.service.ConfigurationEntityStatusService;
import java.security.cert.X509Certificate;

/**
 * This is an application service that deals with the computation of voting card
 * data.
 */
@Service
public class VotingCardSetComputationService extends BaseVotingCardSetService {

    private static final Logger LOGGER = LoggerFactory.getLogger(VotingCardSetComputationService.class);

    @Autowired
    private ConfigurationEntityStatusService configurationEntityStatusService;

    @Autowired
    private VotingCardSetChoiceCodesService votingCardSetChoiceCodesService;

    @Autowired
    private ChoiceCodeGenerationRequestPayloadRepository choiceCodeGenerationRequestPayloadRepository;

    @Autowired
    private PayloadVerifier payloadVerifier;

    @Autowired
    private PlatformRootCAService platformRootCAService;

    @Value("${OR_URL}")
    private String orchestratorUrl;

    @Value("${tenantID}")
    private String tenantId;
    
    @Autowired
    IdleStatusService idleStatusService;

    /**
     * Compute a voting card set.
     *
     * @param votingCardSetId
     *            the identifier of the voting card set
     * @param electionEventId
     *            the identifier of the election event
     * @throws com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException
     * @throws InvalidStatusTransitionException
     *             if the original status does not allow computing
     * @throws JsonProcessingException
     * @throws PayloadStorageException
     *             if the payload could not be store
     * @throws PayloadVerificationException
     *             if the payload signature could not be verified
     */
    public void compute(String votingCardSetId, String electionEventId)
            throws ResourceNotFoundException, InvalidStatusTransitionException, JsonProcessingException,
            PayloadStorageException, PayloadVerificationException {
        
        if (!idleStatusService.isIdReady(votingCardSetId)) {
            return;
        }
        
        try {
            
            LOGGER.info("Starting computation of voting card set {}...", votingCardSetId);

            Status fromStatus = Status.PRECOMPUTED;
            Status toStatus = Status.COMPUTING;

            validateIds(votingCardSetId, electionEventId);

            checkVotingCardSetStatusTransition(electionEventId, votingCardSetId, fromStatus, toStatus);

            secureLog(SdmSecureLogEvent.SENDING_VCS_TO_COMPUTE, electionEventId, votingCardSetId);

            String verificationCardSetId = votingCardSetRepository.getVerificationCardSetId(votingCardSetId);

            X509Certificate platformRootCACertificate;
            try {
                platformRootCACertificate = platformRootCAService.load();
            } catch (CertificateManagementException e) {
                // The payload cannot be verified because the certificate could
                // not be loaded.
                throw new PayloadVerificationException(e);
            }

            int chunkCount =
                choiceCodeGenerationRequestPayloadRepository.getCount(electionEventId, verificationCardSetId);
            for (int i = 0; i < chunkCount; i++) {
                // Retrieve the payload.
                ChoiceCodeGenerationReqPayload payload =
                    choiceCodeGenerationRequestPayloadRepository.retrieve(electionEventId, verificationCardSetId, i);

                // Validate the signature.
                if (payloadVerifier.isValid(payload, platformRootCACertificate)) {
                    // The signature is valid, send for processing.
                    votingCardSetChoiceCodesService.sendToCompute(payload);
                    LOGGER.info("Chunk {} of {} from voting card set {} was sent", i, chunkCount, votingCardSetId);
                } else {
                    // The signature is not valid: do not send.
                    LOGGER.error("Chunk {} of {} from voting card set {} was NOT sent: signature is not valid", i,
                        chunkCount, votingCardSetId);
                }
            }

            configurationEntityStatusService.update(toStatus.name(), votingCardSetId, votingCardSetRepository);

            LOGGER.info("Computation of voting card set {} started", votingCardSetId);

            secureLog(SdmSecureLogEvent.VCS_SENT_TO_COMPUTATION_SUCCESSFULLY, electionEventId, votingCardSetId);
        } finally {
            idleStatusService.freeIdleStatus(votingCardSetId);
        }
    }
}
