/*
 * 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.channel.cc.orchestrator.choicecodes.domain.services;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import javax.inject.Inject;

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

import com.scytl.products.ov.channel.cc.orchestrator.commons.infrastructure.persistence.Jdbc;
import com.scytl.products.ov.channel.cc.orchestrator.commons.infrastructure.persistence.PartialResultsRepository;
import com.scytl.products.ov.channel.cc.orchestrator.commons.polling.ResultsHandler;
import com.scytl.products.ov.commons.beans.domain.model.messaging.SafeStreamDeserializationException;
import com.scytl.products.ov.commons.beans.exceptions.EntryPersistenceException;
import com.scytl.products.ov.commons.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.dto.ChoiceCodeGenerationDTO;
import com.scytl.products.ov.commons.dto.ChoiceCodeGenerationResPayload;
import com.scytl.products.ov.commons.messaging.MessageListener;
import com.scytl.products.ov.commons.safestream.StreamSerializableObjectReader;
import com.scytl.products.ov.commons.safestream.StreamSerializableObjectReaderImpl;
import com.scytl.products.ov.commons.safestream.StreamSerializableObjectWriter;
import com.scytl.products.ov.commons.safestream.StreamSerializableObjectWriterImpl;

@ChoiceCodesGeneration
public class ChoiceCodesGenerationContributionsConsumer
        implements MessageListener {
    private final StreamSerializableObjectWriter writer = new StreamSerializableObjectWriterImpl();

    private final StreamSerializableObjectReader<ChoiceCodeGenerationDTO<ChoiceCodeGenerationResPayload>> reader =
        new StreamSerializableObjectReaderImpl<>();

    @Inject
    @Jdbc
    private PartialResultsRepository<byte[]> resultsRepository;

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

    @Inject
    private ResultsHandler<List<byte[]>> resultsHandler;

    @Inject
    private ComputedValuesService computedValuesService;

    @Override
    public void onMessage(Object message) {
        @SuppressWarnings("unchecked")
        ChoiceCodeGenerationDTO<ChoiceCodeGenerationResPayload> result = 
            (ChoiceCodeGenerationDTO<ChoiceCodeGenerationResPayload>) message;
        LOG.info("OR - Message accepted in " + this.getClass().getSimpleName());
        persistPartialResult(result);
    }

    private void persistPartialResult(ChoiceCodeGenerationDTO<ChoiceCodeGenerationResPayload> choiceCodeGenerationDTO) {
        LOG.info("OR - Persisting partial result in " + this.getClass().getSimpleName());

        UUID key = choiceCodeGenerationDTO.getCorrelationId();

        try {
            resultsRepository.save(key, writer.write(choiceCodeGenerationDTO));
        } catch (IOException e) {
            throw new IllegalStateException("Failed to serialize choice codes generation DTO.", e);
        }

        Optional<List<byte[]>> results = resultsHandler.handleResultsIfReady(key);
        if (results.isPresent()) {

            ChoiceCodeGenerationResPayload payload = choiceCodeGenerationDTO.getPayload();

            String tenantId = payload.getTenantId();
            String electionEventId = payload.getElectionEventId();
            String verificationCardSetId = payload.getVerificationCardSetId();
            int chunkId = payload.getChunkId();
            
            List<ChoiceCodeGenerationDTO<ChoiceCodeGenerationResPayload>> resultMessages = new ArrayList<>();
            for (byte[] bytes : results.get()) {
                try {
                    resultMessages.add(reader.read(bytes));
                } catch (SafeStreamDeserializationException e) {
                    throw new IllegalStateException("Failed to deserialize choice codes generation DTO.", e);
                }
            }
            
            try {
                computedValuesService.update(tenantId, electionEventId, verificationCardSetId, chunkId, resultMessages);
            } catch (ResourceNotFoundException e) {
                LOG.error(
                    "OR - Missing entry to store results for tenantId {}, electionEventId {}, verificationCardSetId {} and chunkId {}",
                    tenantId, electionEventId, verificationCardSetId, chunkId, e);
            } catch (EntryPersistenceException e) {
                LOG.error(
                    "OR - Could not update entry to for tenantId {}, electionEventId {}, verificationCardSetId {} and chunkId {}",
                    tenantId, electionEventId, verificationCardSetId, chunkId, e);
            } 
        }
    }
}
