/*
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.cloud.partners.pubsub.kafka;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.cloud.partners.pubsub.kafka.config.Project;
import com.google.cloud.partners.pubsub.kafka.config.PubSub;
import com.google.cloud.partners.pubsub.kafka.config.Server;
import com.google.cloud.partners.pubsub.kafka.config.Server.Kafka;
import com.google.cloud.partners.pubsub.kafka.config.Server.Security;
import com.google.cloud.partners.pubsub.kafka.config.Subscription;
import com.google.cloud.partners.pubsub.kafka.config.Topic;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
import com.google.pubsub.v1.PubsubMessage;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.header.Header;
import org.apache.kafka.common.header.internals.RecordHeaders;
import org.apache.kafka.common.record.TimestampType;

public class TestHelpers {

  public static final String PROJECT1_TOPIC1 = "projects/project-1/topics/topic-1";
  public static final String PROJECT1_TOPIC2 = "projects/project-1/topics/topic-2";
  public static final String PROJECT2_TOPIC1 = "projects/project-2/topics/topic-1";
  public static final String PROJECT2_TOPIC2 = "projects/project-2/topics/topic-2";
  public static final String PROJECT1_SUBSCRIPTION1 =
      "projects/project-1/subscriptions/subscription-1";
  public static final String PROJECT1 = "projects/project-1";
  public static final String PROJECT1_SUBSCRIPTION2 =
      "projects/project-1/subscriptions/subscription-2";
  public static final String PROJECT2_SUBSCRIPTION3 =
      "projects/project-2/subscriptions/subscription-3";
  public static final String CONFIG_FILE = "unit-test-config.json";
  public static final String PUBSUB_FILE = "unit-test-pubsub.json";

  public static final String SERVER_JSON =
      "{\n"
          + "  \"port\": 8080,\n"
          + "  \"security\": {\n"
          + "    \"certificateChainFile\": \"/path/to/server.crt\",\n"
          + "    \"privateKeyFile\": \"/path/to/server.key\"\n"
          + "  },\n"
          + "  \"kafka\": {\n"
          + "    \"bootstrapServers\": [\"server1:2192\", \"server2:2192\"],\n"
          + "    \"producerProperties\": {\n"
          + "      \"linger.ms\": \"5\",\n"
          + "      \"batch.size\": \"1000000\",\n"
          + "      \"buffer.memory\": \"32000000\"\n"
          + "    },\n"
          + "    \"producerExecutors\": 4,\n"
          + "    \"consumerProperties\": {\n"
          + "      \"max.poll.records\": \"1000\"\n"
          + "    },\n"
          + "    \"consumersPerSubscription\": 4\n"
          + "  }\n"
          + "}";

  public static final Server SERVER_CONFIG =
      Server.newBuilder()
          .setPort(8080)
          .setSecurity(
              Security.newBuilder()
                  .setCertificateChainFile("/path/to/server.crt")
                  .setPrivateKeyFile("/path/to/server.key")
                  .build())
          .setKafka(
              Kafka.newBuilder()
                  .addAllBootstrapServers(ImmutableList.of("server1:2192", "server2:2192"))
                  .putConsumerProperties("max.poll.records", "1000")
                  .putProducerProperties("linger.ms", "5")
                  .putProducerProperties("batch.size", "1000000")
                  .putProducerProperties("buffer.memory", "32000000")
                  .setProducerExecutors(4)
                  .setConsumersPerSubscription(4)
                  .build())
          .build();

  public static final String PUBSUB_JSON =
      "{\n"
          + "  \"projects\": [{\n"
          + "    \"name\": \"project-1\",\n"
          + "    \"topics\": [{\n"
          + "      \"name\": \"topic-1\",\n"
          + "      \"kafkaTopic\": \"kafka-topic-1\",\n"
          + "      \"subscriptions\": [{\n"
          + "        \"name\": \"subscription-1\",\n"
          + "        \"ackDeadlineSeconds\": 10\n"
          + "      }, {\n"
          + "        \"name\": \"subscription-2\",\n"
          + "        \"ackDeadlineSeconds\": 10\n"
          + "      }]\n"
          + "    }, {\n"
          + "      \"name\": \"topic-2\",\n"
          + "      \"kafkaTopic\": \"kafka-topic-2\",\n"
          + "      \"subscriptions\": [{\n"
          + "        \"name\": \"subscription-3\",\n"
          + "        \"ackDeadlineSeconds\": 30\n"
          + "      }, {\n"
          + "        \"name\": \"subscription-4\",\n"
          + "        \"ackDeadlineSeconds\": 45\n"
          + "      }]\n"
          + "    }]\n"
          + "  }, {\n"
          + "    \"name\": \"project-2\",\n"
          + "    \"topics\": [{\n"
          + "      \"name\": \"topic-1\",\n"
          + "      \"kafkaTopic\": \"kafka-topic-1\",\n"
          + "      \"subscriptions\": [{\n"
          + "        \"name\": \"subscription-1\",\n"
          + "        \"ackDeadlineSeconds\": 10\n"
          + "      }, {\n"
          + "        \"name\": \"subscription-2\",\n"
          + "        \"ackDeadlineSeconds\": 10\n"
          + "      }]\n"
          + "    }, {\n"
          + "      \"name\": \"topic-2\",\n"
          + "      \"kafkaTopic\": \"kafka-topic-2\",\n"
          + "      \"subscriptions\": [{\n"
          + "        \"name\": \"subscription-3\",\n"
          + "        \"ackDeadlineSeconds\": 30\n"
          + "      }]\n"
          + "    }]\n"
          + "  }]\n"
          + "}";

  public static final PubSub PUBSUB_CONFIG =
      PubSub.newBuilder()
          .addProjects(
              Project.newBuilder()
                  .setName("project-1")
                  .addTopics(
                      Topic.newBuilder()
                          .setName("topic-1")
                          .setKafkaTopic("kafka-topic-1")
                          .addSubscriptions(
                              Subscription.newBuilder()
                                  .setName("subscription-1")
                                  .setAckDeadlineSeconds(10)
                                  .build())
                          .addSubscriptions(
                              Subscription.newBuilder()
                                  .setName("subscription-2")
                                  .setAckDeadlineSeconds(10)
                                  .build())
                          .build())
                  .addTopics(
                      Topic.newBuilder()
                          .setName("topic-2")
                          .setKafkaTopic("kafka-topic-2")
                          .addSubscriptions(
                              Subscription.newBuilder()
                                  .setName("subscription-3")
                                  .setAckDeadlineSeconds(30)
                                  .build())
                          .addSubscriptions(
                              Subscription.newBuilder()
                                  .setName("subscription-4")
                                  .setAckDeadlineSeconds(45)
                                  .build())
                          .build())
                  .build())
          .addProjects(
              Project.newBuilder()
                  .setName("project-2")
                  .addTopics(
                      Topic.newBuilder()
                          .setName("topic-1")
                          .setKafkaTopic("kafka-topic-1")
                          .addSubscriptions(
                              Subscription.newBuilder()
                                  .setName("subscription-1")
                                  .setAckDeadlineSeconds(10)
                                  .build())
                          .addSubscriptions(
                              Subscription.newBuilder()
                                  .setName("subscription-2")
                                  .setAckDeadlineSeconds(10)
                                  .build())
                          .build())
                  .addTopics(
                      Topic.newBuilder()
                          .setName("topic-2")
                          .setKafkaTopic("kafka-topic-2")
                          .addSubscriptions(
                              Subscription.newBuilder()
                                  .setName("subscription-3")
                                  .setAckDeadlineSeconds(30)
                                  .build())
                          .build())
                  .build())
          .build();

  /** Generate a sequence of PubsubMessage objects. */
  static List<PubsubMessage> generatePubsubMessages(int howMany) {
    List<PubsubMessage> messages = new ArrayList<>();
    for (int i = 0; i < howMany; i++) {
      messages.add(
          PubsubMessage.newBuilder().setData(ByteString.copyFrom("message-" + i, UTF_8)).build());
    }
    return messages;
  }

  static List<PubsubMessage> generatePubsubMessagesWithHeader(int howMany) {
    List<PubsubMessage> messages = new ArrayList<>();
    for (int i = 0; i < howMany; i++) {
      messages.add(
          PubsubMessage.newBuilder()
              .setData(ByteString.copyFrom("message-" + i, UTF_8))
              .putAllAttributes(generateAttributes())
              .build());
    }
    return messages;
  }

  private static Map<String, String> generateAttributes() {
    Map<String, String> attributesMap = new HashMap<>();
    attributesMap.put("some-key", "some-value");
    return attributesMap;
  }

  /**
   * Generate a sequence of ConsumerRecord objects. The records will be evenly distributed amongst
   * the partitions in round-robin fashion.
   */
  static List<ConsumerRecord<String, ByteBuffer>> generateConsumerRecords(
      String topic, int partitions, int recordsPerPartition, List<Header> headers) {
    List<ConsumerRecord<String, ByteBuffer>> records = new ArrayList<>();
    int messageSeq = 0;
    long now = System.currentTimeMillis();
    for (int p = 0; p < partitions; p++) {
      for (int r = 0; r < recordsPerPartition; r++) {
        ByteBuffer value =
            ByteBuffer.wrap(String.format("message-%04d", messageSeq++).getBytes(UTF_8));
        records.add(
            new ConsumerRecord<>(
                topic,
                p,
                r,
                now,
                TimestampType.LOG_APPEND_TIME,
                (long) ConsumerRecord.NULL_CHECKSUM,
                ConsumerRecord.NULL_SIZE,
                value.capacity(),
                null,
                value,
                new RecordHeaders(headers)));
      }
    }
    return records;
  }
}