/*
 * 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.dse.driver.api.core.graph;

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

import com.datastax.dse.driver.api.core.graph.predicates.Search;
import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement;
import com.datastax.oss.driver.api.testinfra.requirement.BackendType;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(DataProviderRunner.class)
public abstract class GraphTextSearchIndexITBase {

  protected abstract boolean isGraphBinary();

  protected abstract GraphTraversalSource graphTraversalSource();

  @DataProvider
  public static Object[][] indexTypes() {
    return new Object[][] {{"search"}

      // FIXME for some reason, materialized and secondary indices have decided not to work
      // I get an exception saying "there is no index for this query, here is the defined
      // indices: " and the list contains the indices that are needed. Mysterious.
      // There may be something to do with differences in the CCMBridge adapter of the new
      // driver, some changes make materialized views and secondary indices to be not
      // considered for graph:
      //
      //  , {"materialized"}
      //  , {"secondary"}
    };
  }

  /**
   * Validates that a graph traversal can be made by using a Search prefix predicate on an indexed
   * property of the given type.
   *
   * <p>Finds all 'user' vertices having a 'full_name' property beginning with 'Paul'.
   *
   * @test_category dse:graph
   */
  @UseDataProvider("indexTypes")
  @Test
  public void search_by_prefix_search(String indexType) {
    // Only one user with full_name starting with Paul.
    GraphTraversal<Vertex, String> traversal =
        graphTraversalSource()
            .V()
            .has("user", "full_name_" + indexType, Search.prefix("Paul"))
            .values("full_name_" + indexType);
    assertThat(traversal.toList()).containsOnly("Paul Thomas Joe");
  }

  /**
   * Validates that a graph traversal can be made by using a Search regex predicate on an indexed
   * property of the given type.
   *
   * <p>Finds all 'user' vertices having a 'full_name' property matching regex '.*Paul.*'.
   *
   * @test_category dse:graph
   */
  @UseDataProvider("indexTypes")
  @Test
  public void search_by_regex(String indexType) {
    // Only two people with names containing pattern for Paul.
    GraphTraversal<Vertex, String> traversal =
        graphTraversalSource()
            .V()
            .has("user", "full_name_" + indexType, Search.regex(".*Paul.*"))
            .values("full_name_" + indexType);
    assertThat(traversal.toList()).containsOnly("Paul Thomas Joe", "James Paul Joe");
  }

  /**
   * Validates that a graph traversal can be made by using a Search fuzzy predicate on an indexed
   * property of the given type.
   *
   * <p>Finds all 'user' vertices having a 'alias' property matching 'awrio' with a fuzzy distance
   * of 1.
   *
   * @test_category dse:graph
   */
  @UseDataProvider("indexTypes")
  @Test
  @BackendRequirement(type = BackendType.DSE, minInclusive = "5.1.0")
  public void search_by_fuzzy(String indexType) {
    // Alias matches 'awrio' fuzzy
    GraphTraversal<Vertex, String> traversal =
        graphTraversalSource()
            .V()
            .has("user", "alias_" + indexType, Search.fuzzy("awrio", 1))
            .values("full_name_" + indexType);
    // Should not match 'Paul Thomas Joe' since alias is 'mario', which is at distance 2 of 'awrio'
    // (a -> m, w -> a)
    // Should match 'George Bill Steve' since alias is 'wario' witch matches 'awrio' within a
    // distance of 1 (transpose w with a).
    assertThat(traversal.toList()).containsOnly("George Bill Steve");
  }

  /**
   * Validates that a graph traversal can be made by using a Search token predicate on an indexed
   * property of the given type.
   *
   * <p>Finds all 'user' vertices having a 'description' property containing the token 'cold'.
   *
   * @test_category dse:graph
   */
  @UseDataProvider("indexTypes")
  @Test
  public void search_by_token(String indexType) {
    // Description containing token 'cold'
    GraphTraversal<Vertex, String> traversal =
        graphTraversalSource()
            .V()
            .has("user", "description_" + indexType, Search.token("cold"))
            .values("full_name_" + indexType);
    assertThat(traversal.toList()).containsOnly("Jill Alice", "George Bill Steve");
  }

  /**
   * Validates that a graph traversal can be made by using a Search token prefix predicate on an
   * indexed property of the given type.
   *
   * <p>Finds all 'user' vertices having a 'description' containing the token prefix 'h'.
   */
  @UseDataProvider("indexTypes")
  @Test
  public void search_by_token_prefix(String indexType) {
    // Description containing a token starting with h
    GraphTraversal<Vertex, String> traversal =
        graphTraversalSource()
            .V()
            .has("user", "description_" + indexType, Search.tokenPrefix("h"))
            .values("full_name_" + indexType);
    assertThat(traversal.toList()).containsOnly("Paul Thomas Joe", "James Paul Joe");
  }

  /**
   * Validates that a graph traversal can be made by using a Search token regex predicate on an
   * indexed property of the given type.
   *
   * <p>Finds all 'user' vertices having a 'description' containing the token regex
   * '(nice|hospital)'.
   */
  @UseDataProvider("indexTypes")
  @Test
  public void search_by_token_regex(String indexType) {
    // Description containing nice or hospital
    GraphTraversal<Vertex, String> traversal =
        graphTraversalSource()
            .V()
            .has("user", "description_" + indexType, Search.tokenRegex("(nice|hospital)"))
            .values("full_name_" + indexType);
    assertThat(traversal.toList()).containsOnly("Paul Thomas Joe", "Jill Alice");
  }

  /**
   * Validates that a graph traversal can be made by using a Search fuzzy predicate on an indexed
   * property of the given type.
   *
   * <p>Finds all 'user' vertices having a 'description' property matching 'lieks' with a fuzzy
   * distance of 1.
   *
   * @test_category dse:graph
   */
  @UseDataProvider("indexTypes")
  @Test
  @BackendRequirement(type = BackendType.DSE, minInclusive = "5.1.0")
  public void search_by_token_fuzzy(String indexType) {
    // Description containing 'lives' fuzzy
    GraphTraversal<Vertex, String> traversal =
        graphTraversalSource()
            .V()
            .has("user", "description_" + indexType, Search.tokenFuzzy("lieks", 1))
            .values("full_name_" + indexType);
    // Should not match 'Paul Thomas Joe' since description contains 'Lives' which is at distance of
    // 2 (e -> v, k -> e)
    // Should match 'James Paul Joe' since description contains 'Likes' (transpose e for k)
    assertThat(traversal.toList()).containsOnly("James Paul Joe");
  }

  /**
   * Validates that a graph traversal can be made by using a Search phrase predicate on an indexed
   * property of the given type.
   *
   * <p>Finds all 'user' vertices having a 'description' property matching 'a cold' with a distance
   * of 2.
   *
   * @test_category dse:graph
   */
  @UseDataProvider("indexTypes")
  @Test
  @BackendRequirement(type = BackendType.DSE, minInclusive = "5.1.0")
  public void search_by_phrase(String indexType) {
    // Full name contains phrase "Paul Joe"
    GraphTraversal<Vertex, String> traversal =
        graphTraversalSource()
            .V()
            .has("user", "description_" + indexType, Search.phrase("a cold", 2))
            .values("full_name_" + indexType);
    // Should match 'George Bill Steve' since 'A cold dude' is at distance of 0 for 'a cold'.
    // Should match 'Jill Alice' since 'Enjoys a very nice cold coca cola' is at distance of 2 for
    // 'a cold'.
    assertThat(traversal.toList()).containsOnly("George Bill Steve", "Jill Alice");
  }
}
