package ru.micord.ervu.kafka.service.impl;

import java.lang.invoke.MethodHandles;
import java.util.Optional;
import java.util.concurrent.ExecutionException;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.header.internals.RecordHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.kafka.requestreply.ReplyingKafkaTemplate;
import org.springframework.kafka.requestreply.RequestReplyFuture;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.stereotype.Service;
import ru.micord.ervu.kafka.exception.KafkaMessageException;
import ru.micord.ervu.kafka.exception.KafkaMessageReplyTimeoutException;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;

/**
 * @author Eduard Tihomirov
 */
@Service
public class BaseReplyingKafkaServiceImpl implements ReplyingKafkaService {
  private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
  private final ReplyingKafkaTemplate<String, String, String> replyingKafkaTemplate;

  public BaseReplyingKafkaServiceImpl(
      ReplyingKafkaTemplate<String, String, String> replyingKafkaTemplate) {
    this.replyingKafkaTemplate = replyingKafkaTemplate;
  }

  public String sendMessageAndGetReply(String requestTopic,
                                       String replyTopic,
                                       String requestMessage) {
    long startTime = System.currentTimeMillis();
    ProducerRecord<String, String> record = new ProducerRecord<>(requestTopic, requestMessage);
    record.headers().add(new RecordHeader(KafkaHeaders.REPLY_TOPIC, replyTopic.getBytes()));
    RequestReplyFuture<String, String, String> replyFuture = replyingKafkaTemplate.sendAndReceive(record);

    try {
      String result = Optional.ofNullable(replyFuture.get())
          .map(ConsumerRecord::value)
          .orElseThrow(() -> new KafkaMessageException("Kafka return result is null."));
      LOGGER.info("Thread {} - KafkaSendMessageAndGetReply: {} ms",
          Thread.currentThread().getId(), System.currentTimeMillis() - startTime);
      return result;
    }
    catch (InterruptedException | ExecutionException e) {
      LOGGER.error("Thread {} - KafkaSendMessageAndGetReply: {} ms",
          Thread.currentThread().getId(), System.currentTimeMillis() - startTime);
      throw new KafkaMessageReplyTimeoutException(e);
    }
  }
}
