/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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 com.datastax.oss.driver.core.cql;

import static org.assertj.core.api.Assertions.assertThat;

import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.cql.BatchStatement;
import com.datastax.oss.driver.api.core.cql.BatchType;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.testinfra.ccm.CcmRule;
import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement;
import com.datastax.oss.driver.api.testinfra.requirement.BackendType;
import com.datastax.oss.driver.api.testinfra.session.SessionRule;
import com.datastax.oss.driver.categories.ParallelizableTests;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
import java.util.function.Function;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;

@Category(ParallelizableTests.class)
@BackendRequirement(type = BackendType.CASSANDRA, minInclusive = "4.0")
// Use next version -- not sure if it will be in by then, but as a reminder to check
@BackendRequirement(
    type = BackendType.DSE,
    minInclusive = "7.0",
    description = "Feature not available in DSE yet")
public class NowInSecondsIT {

  private static final CcmRule CCM_RULE = CcmRule.getInstance();

  private static final SessionRule<CqlSession> SESSION_RULE = SessionRule.builder(CCM_RULE).build();

  @ClassRule
  public static final TestRule CHAIN = RuleChain.outerRule(CCM_RULE).around(SESSION_RULE);

  @Before
  public void setup() {
    for (String statement :
        ImmutableList.of(
            "DROP TABLE IF EXISTS test", "CREATE TABLE test(k int PRIMARY KEY, v int)")) {
      SESSION_RULE
          .session()
          .execute(
              SimpleStatement.newInstance(statement)
                  .setExecutionProfile(SESSION_RULE.slowProfile()));
    }
  }

  @Test
  public void should_use_now_in_seconds_with_simple_statement() {
    should_use_now_in_seconds(SimpleStatement::newInstance);
  }

  @Test
  public void should_use_now_in_seconds_with_bound_statement() {
    should_use_now_in_seconds(
        queryString -> {
          PreparedStatement preparedStatement = SESSION_RULE.session().prepare(queryString);
          return preparedStatement.bind();
        });
  }

  @Test
  public void should_use_now_in_seconds_with_batch_statement() {
    should_use_now_in_seconds(
        queryString ->
            BatchStatement.newInstance(BatchType.LOGGED, SimpleStatement.newInstance(queryString)));
  }

  private <StatementT extends Statement<StatementT>> void should_use_now_in_seconds(
      Function<String, StatementT> buildWriteStatement) {
    CqlSession session = SESSION_RULE.session();

    // Given
    StatementT writeStatement =
        buildWriteStatement.apply("INSERT INTO test (k,v) VALUES (1,1) USING TTL 20");
    SimpleStatement readStatement =
        SimpleStatement.newInstance("SELECT TTL(v) FROM test WHERE k = 1");

    // When
    // insert at t = 0 with TTL 20
    session.execute(writeStatement.setNowInSeconds(0));
    // read TTL at t = 10
    ResultSet rs = session.execute(readStatement.setNowInSeconds(10));
    int remainingTtl = rs.one().getInt(0);

    // Then
    assertThat(remainingTtl).isEqualTo(10);
  }
}
