/*
 * 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.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;
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.config.QueuesConfig;
import com.scytl.products.ov.channel.cc.orchestrator.commons.config.TopicsConfig;
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.commons.dto.ChoiceCodeVerificationDTO;
import com.scytl.products.ov.commons.dto.ChoiceCodesVerificationDecryptResPayload;
import com.scytl.products.ov.commons.messaging.MessageListener;
import com.scytl.products.ov.commons.messaging.MessagingException;
import com.scytl.products.ov.commons.messaging.MessagingService;

@ChoiceCodesDecryption
@Contributions
public class ChoiceCodesDecryptionContributionsConsumer
        implements MessageListener {
    private static final Logger LOG = LoggerFactory.getLogger("std");
    
    @Inject
    private MessagingService messagingService;

    @Inject
    @Jdbc
    private PartialResultsRepository<byte[]> repository;
    
    @Override
    public void onMessage(Object message) {
        @SuppressWarnings("unchecked")
        ChoiceCodeVerificationDTO<ChoiceCodesVerificationDecryptResPayload> result =
            (ChoiceCodeVerificationDTO<ChoiceCodesVerificationDecryptResPayload>) message;
        LOG.info("OR - Message accepted in " + this.getClass().getSimpleName());
        persistPartialResult(result);
        sendPartialResultsReadyIfNecessary(result);
    }

    private void sendPartialResultsReadyIfNecessary(
            ChoiceCodeVerificationDTO<ChoiceCodesVerificationDecryptResPayload> result) {
        UUID correlationId = result.getCorrelationId();
        if (repository.hasAll(correlationId,
            QueuesConfig.VERIFICATION_DECRYPTION_CONTRIBUTIONS_RES_QUEUES.length)) {
            LOG.info("OR - All partial results are ready in "
                + getClass().getSimpleName());
            byte[] message = encodeCorrelationId(correlationId);
            try {
                messagingService.send(TopicsConfig.HA_TOPIC, message);
            } catch (MessagingException e) {
                throw new IllegalStateException(
                    "Failed to send notification.", e);
            }
            LOG.info("OR - Correlation identifier {} is sent to {}.",
                correlationId, TopicsConfig.HA_TOPIC);
        }
    }
    
    private byte[] encodeCorrelationId(UUID correlationId) {
        return ByteBuffer.allocate(Long.BYTES * 2)
            .putLong(correlationId.getMostSignificantBits())
            .putLong(correlationId.getLeastSignificantBits()).array();
    }

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

        UUID key = result.getCorrelationId();

        byte[] bytes =
                serializePayload(result.getPayload());

        repository.save(key, bytes);
    }

    private byte[] serializePayload(
            ChoiceCodesVerificationDecryptResPayload payload) {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        try (ObjectOutputStream stream = new ObjectOutputStream(bytes)) {
            stream.writeObject(payload);
        } catch (IOException e) {
            throw new IllegalStateException(
                "Failed to serialize the payload.", e);
        }
        return bytes.toByteArray();
    }
}
