/*
 * Copyright 2019 Red Hat, Inc.
 *
 * Red Hat licenses this file to you 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 io.vertx.cassandra;

import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder;
import com.datastax.oss.driver.api.core.cql.Statement;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import static java.util.concurrent.TimeUnit.NANOSECONDS;

@RunWith(VertxUnitRunner.class)
public class StreamingTest extends CassandraClientTestBase {

  @Test
  public void testReadStream(TestContext testContext) throws Exception {
    initializeRandomStringKeyspace();
    insertRandomStrings(50);
    String query = "select random_string from random_strings.random_string_by_first_letter where first_letter = 'A'";
    Statement statement = SimpleStatement.newInstance(query)
      .setPageSize(5); // make sure data is not loaded at once from Cassandra
    Async async = testContext.async();
    client.queryStream(query, testContext.asyncAssertSuccess(stream -> {
      List<Row> items = Collections.synchronizedList(new ArrayList<>());
      AtomicInteger idx = new AtomicInteger();
      long pause = 500;
      long start = System.nanoTime();
      stream.endHandler(end -> {
        long duration = NANOSECONDS.toMillis(System.nanoTime() - start);
        testContext.assertTrue(duration >= 5 * pause);
        async.countDown();
      }).exceptionHandler(testContext::fail).handler(item -> {
        items.add(item);
        int j = idx.getAndIncrement();
        if (j == 3 || j == 16 || j == 21 || j == 38 || j == 47) {
          stream.pause();
          int emitted = items.size();
          vertx.setTimer(pause, tid -> {
            testContext.assertTrue(emitted == items.size());
            stream.resume();
          });
        }
      });
    }));
  }

  @Test
  public void streamFetchesDoesNotOverflowDefault512KbJVMStack(TestContext testContext) throws Exception {
    int fetchSize = 100_000;
    initializeRandomStringKeyspace();
    insertRandomStrings(5_000);
    final SimpleStatement query = new SimpleStatementBuilder(
      String.format(
        "select random_string from random_strings.random_string_by_first_letter limit %d",
        fetchSize
      )
    ).setPageSize(fetchSize).build();
    Async async = testContext.async();
    client.queryStream(query, testContext.asyncAssertSuccess(stream -> {
      stream.endHandler(end -> async.countDown())
        .exceptionHandler(testContext::fail)
        .handler(item -> {});
    }));
  }

  @Test
  public void emptyStream(TestContext testContext) throws Exception {
    initializeRandomStringKeyspace();
    insertRandomStrings(1);
    String query = "select random_string from random_strings.random_string_by_first_letter where first_letter = '$'";
    Async async = testContext.async();
    client.queryStream(query, testContext.asyncAssertSuccess(stream -> {
      stream.endHandler(end -> async.countDown())
        .exceptionHandler(testContext::fail)
        .handler(item -> testContext.fail());
    }));
  }
}