/*
 * 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.driver.core.schemabuilder;

import static com.datastax.driver.core.schemabuilder.SchemaBuilder.Caching;
import static com.datastax.driver.core.schemabuilder.SchemaBuilder.KeyCaching;
import static com.datastax.driver.core.schemabuilder.SchemaBuilder.alterTable;
import static com.datastax.driver.core.schemabuilder.SchemaBuilder.always;
import static com.datastax.driver.core.schemabuilder.SchemaBuilder.frozen;
import static com.datastax.driver.core.schemabuilder.SchemaBuilder.leveledStrategy;
import static com.datastax.driver.core.schemabuilder.SchemaBuilder.lz4;
import static com.datastax.driver.core.schemabuilder.SchemaBuilder.rows;
import static org.assertj.core.api.Assertions.assertThat;

import com.datastax.driver.core.DataType;
import org.testng.annotations.Test;

public class AlterTest {

  @Test(groups = "unit")
  public void should_alter_column_type() throws Exception {
    // When
    SchemaStatement statement = alterTable("test").alterColumn("name").type(DataType.ascii());

    // Then
    assertThat(statement.getQueryString()).isEqualTo("\n\tALTER TABLE test ALTER name TYPE ascii");
  }

  @Test(groups = "unit")
  public void should_alter_column_type_with_keyspace() throws Exception {
    // When
    SchemaStatement statement = alterTable("ks", "test").alterColumn("name").type(DataType.ascii());

    // Then
    assertThat(statement.getQueryString())
        .isEqualTo("\n\tALTER TABLE ks.test ALTER name TYPE ascii");
  }

  @Test(groups = "unit")
  public void should_alter_column_type_to_UDT() throws Exception {
    // When
    SchemaStatement statement =
        alterTable("ks", "test").alterColumn("address").udtType(frozen("address"));

    // Then
    assertThat(statement.getQueryString())
        .isEqualTo("\n\tALTER TABLE ks.test ALTER address TYPE frozen<address>");
  }

  @Test(groups = "unit")
  public void should_add_column() throws Exception {
    // When
    SchemaStatement statement = alterTable("test").addColumn("location").type(DataType.ascii());

    // Then
    assertThat(statement.getQueryString()).isEqualTo("\n\tALTER TABLE test ADD location ascii");
  }

  @Test(groups = "unit")
  public void should_add_column_with_UDT_type() throws Exception {
    // When
    SchemaStatement statement = alterTable("test").addColumn("location").udtType(frozen("address"));

    // Then
    assertThat(statement.getQueryString())
        .isEqualTo("\n\tALTER TABLE test ADD location frozen<address>");
  }

  @Test(groups = "unit")
  public void should_rename_column() throws Exception {
    // When
    SchemaStatement statement = alterTable("test").renameColumn("name").to("description");

    // Then
    assertThat(statement.getQueryString())
        .isEqualTo("\n\tALTER TABLE test RENAME name TO description");
  }

  @Test(groups = "unit")
  public void should_drop_column() throws Exception {
    // When
    SchemaStatement statement = alterTable("test").dropColumn("name");

    // Then
    assertThat(statement.getQueryString()).isEqualTo("\n\tALTER TABLE test DROP name");
  }

  @Test(groups = "unit")
  public void should_alter_table_options() throws Exception {
    // When
    // Note that this does not necessarily represent a valid configuration, the goal is just to test
    // all options
    // (some of which might be specific to C* 2.0 or 2.1)
    SchemaStatement statement =
        alterTable("test")
            .withOptions()
            .bloomFilterFPChance(0.01)
            .caching(Caching.ROWS_ONLY)
            .comment("This is a comment")
            .compactionOptions(leveledStrategy().ssTableSizeInMB(160))
            .compressionOptions(lz4())
            .dcLocalReadRepairChance(0.21)
            .defaultTimeToLive(100)
            .gcGraceSeconds(9999)
            .indexInterval(256)
            .minIndexInterval(64)
            .maxIndexInterval(512)
            .memtableFlushPeriodInMillis(12)
            .populateIOCacheOnFlush(true)
            .replicateOnWrite(true)
            .readRepairChance(0.42)
            .speculativeRetry(always())
            .cdc(true);

    SchemaStatement statementWith21Caching =
        alterTable("test").withOptions().caching(KeyCaching.NONE, rows(100));

    // Then
    assertThat(statement.getQueryString())
        .isEqualTo(
            "\n\tALTER TABLE test\n\t"
                + "WITH caching = 'rows_only' "
                + "AND bloom_filter_fp_chance = 0.01 "
                + "AND comment = 'This is a comment' "
                + "AND compression = {'sstable_compression' : 'LZ4Compressor'} "
                + "AND compaction = {'class' : 'LeveledCompactionStrategy', 'sstable_size_in_mb' : 160} "
                + "AND dclocal_read_repair_chance = 0.21 "
                + "AND default_time_to_live = 100 "
                + "AND gc_grace_seconds = 9999 "
                + "AND index_interval = 256 "
                + "AND min_index_interval = 64 "
                + "AND max_index_interval = 512 "
                + "AND memtable_flush_period_in_ms = 12 "
                + "AND populate_io_cache_on_flush = true "
                + "AND read_repair_chance = 0.42 "
                + "AND replicate_on_write = true "
                + "AND speculative_retry = 'ALWAYS' "
                + "AND cdc = true");

    assertThat(statementWith21Caching.getQueryString())
        .isEqualTo(
            "\n\tALTER TABLE test\n\t"
                + "WITH caching = {'keys' : 'none', 'rows_per_partition' : 100}");
  }

  @Test(
      groups = "unit",
      expectedExceptions = IllegalArgumentException.class,
      expectedExceptionsMessageRegExp =
          "The keyspace name 'add' is not allowed because it is a reserved keyword")
  public void should_fail_if_keyspace_name_is_a_reserved_keyword() throws Exception {
    alterTable("add", "test").addColumn("test").type(DataType.ascii());
  }

  @Test(
      groups = "unit",
      expectedExceptions = IllegalArgumentException.class,
      expectedExceptionsMessageRegExp =
          "The table name 'add' is not allowed because it is a reserved keyword")
  public void should_fail_if_table_name_is_a_reserved_keyword() throws Exception {
    alterTable("add").addColumn("test").type(DataType.ascii());
  }

  @Test(
      groups = "unit",
      expectedExceptions = IllegalArgumentException.class,
      expectedExceptionsMessageRegExp =
          "The new column name 'add' is not allowed because it is a reserved keyword")
  public void should_fail_if_added_column_is_a_reserved_keyword() throws Exception {
    alterTable("test").addColumn("add").type(DataType.ascii()).getQueryString();
  }

  @Test(
      groups = "unit",
      expectedExceptions = IllegalArgumentException.class,
      expectedExceptionsMessageRegExp =
          "The altered column name 'add' is not allowed because it is a reserved keyword")
  public void should_fail_if_altered_column_is_a_reserved_keyword() throws Exception {
    alterTable("test").alterColumn("add").type(DataType.ascii()).getQueryString();
  }

  @Test(
      groups = "unit",
      expectedExceptions = IllegalArgumentException.class,
      expectedExceptionsMessageRegExp =
          "The renamed column name 'add' is not allowed because it is a reserved keyword")
  public void should_fail_if_renamed_column_is_a_reserved_keyword() throws Exception {
    alterTable("test").renameColumn("add");
  }

  @Test(
      groups = "unit",
      expectedExceptions = IllegalArgumentException.class,
      expectedExceptionsMessageRegExp =
          "The new column name 'add' is not allowed because it is a reserved keyword")
  public void should_fail_if_new_renamed_column_is_a_reserved_keyword() throws Exception {
    alterTable("test").renameColumn("col").to("add");
  }

  @Test(
      groups = "unit",
      expectedExceptions = IllegalArgumentException.class,
      expectedExceptionsMessageRegExp =
          "The dropped column name 'add' is not allowed because it is a reserved keyword")
  public void should_fail_if_drop_column_is_a_reserved_keyword() throws Exception {
    alterTable("test").dropColumn("add").getQueryString();
  }

  @Test(groups = "unit")
  public void should_add_static_column() throws Exception {
    // When
    SchemaStatement statement = alterTable("test").addStaticColumn("stat").type(DataType.text());

    // Then
    assertThat(statement.getQueryString()).isEqualTo("\n\tALTER TABLE test ADD stat text static");
  }
}
