package org.infinispan.query.remote.impl;

import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;

import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.cache.Index;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.protostream.DescriptorParserException;
import org.infinispan.protostream.FileDescriptorSource;
import org.infinispan.protostream.SerializationContext;
import org.infinispan.test.SingleCacheManagerTest;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.transaction.TransactionMode;
import org.testng.annotations.Test;

/**
 * Test that ProtobufIndexedFieldProvider correctly identifies indexed fields based on the protostream annotations
 * applied in the test model (bank.proto).
 *
 * @author anistor@redhat.com
 * @since 9.0
 */
@Test(groups = "functional", testName = "query.remote.impl.ProtobufFieldIndexingMetadataTest")
public class ProtobufFieldIndexingMetadataTest extends SingleCacheManagerTest {

   private static final String PROTO_DEFINITIONS =
         "/** @Indexed */ message User {\n" +
               "\n" +
               "   /** @IndexedField */ required string name = 1;\n" +
               "\n" +
               "   required string surname = 2;\n" +
               "\n" +
               "   /** @Indexed(true) */" +
               "   message Address {\n" +
               "      /** @IndexedField */ required string street = 10;\n" +
               "      required string postCode = 20;\n" +
               "   }\n" +
               "\n" +
               "   /** @IndexedField */ repeated Address indexedAddresses = 3;\n" +
               "\n" +
               "   /** @IndexedField(index=false, store=false) */ repeated Address unindexedAddresses = 4;\n" +
               "}";

   protected EmbeddedCacheManager createCacheManager() throws Exception {
      ConfigurationBuilder cfg = getDefaultStandaloneCacheConfig(true);
      cfg.transaction().transactionMode(TransactionMode.TRANSACTIONAL)
            .indexing().index(Index.ALL)
            .addProperty("default.directory_provider", "ram")
            .addProperty("lucene_version", "LUCENE_CURRENT");
      return TestCacheManagerFactory.createCacheManager(cfg);
   }

   public void testProtobufFieldIndexingMetadata() throws Exception {
      SerializationContext serCtx = ProtobufMetadataManagerImpl.getSerializationContextInternal(cacheManager);
      serCtx.registerProtoFiles(FileDescriptorSource.fromString("user_definition.proto", PROTO_DEFINITIONS));
      ProtobufFieldIndexingMetadata userIndexedFieldProvider = new ProtobufFieldIndexingMetadata(serCtx.getMessageDescriptor("User"));
      ProtobufFieldIndexingMetadata addressIndexedFieldProvider = new ProtobufFieldIndexingMetadata(serCtx.getMessageDescriptor("User.Address"));

      // try top level attributes
      assertTrue(userIndexedFieldProvider.isIndexed(new String[]{"name"}));
      assertFalse(userIndexedFieldProvider.isIndexed(new String[]{"surname"}));
      assertTrue(addressIndexedFieldProvider.isIndexed(new String[]{"street"}));
      assertFalse(addressIndexedFieldProvider.isIndexed(new String[]{"postCode"}));

      // try nested attributes
      assertTrue(userIndexedFieldProvider.isIndexed(new String[]{"indexedAddresses", "street"}));
      assertFalse(userIndexedFieldProvider.isIndexed(new String[]{"indexedAddresses", "postCode"}));
      assertFalse(userIndexedFieldProvider.isIndexed(new String[]{"unindexedAddresses", "street"}));
      assertFalse(userIndexedFieldProvider.isIndexed(new String[]{"unindexedAddresses", "postCode"}));
   }

   @Test(expectedExceptions = DescriptorParserException.class, expectedExceptionsMessageRegExp = "java.lang.IllegalStateException: Annotation 'IndexedField' cannot be used together with 'Field' on field test.User.age")
   public void testIndexedFieldAndField() throws Exception {
      String testProto = "package test;\n" +
            "/* @Indexed */ message User {\n" +
            "   /* @IndexedField(index=false, store=false) @Field(store=Store.NO, index=Index.NO) */ " +
            "   required int32 age = 1;\n" +
            "}";

      SerializationContext serCtx = ProtobufMetadataManagerImpl.getSerializationContextInternal(cacheManager);
      serCtx.registerProtoFiles(FileDescriptorSource.fromString("test.proto", testProto));
   }

   @Test(expectedExceptions = DescriptorParserException.class, expectedExceptionsMessageRegExp = "java.lang.IllegalStateException: Annotation 'IndexedField' cannot be used together with 'SortableField' on field test.User1.age")
   public void testIndexedFieldAndSortableField() throws Exception {
      String testProto = "package test;\n" +
            "/* @Indexed */ message User1 {\n" +
            "   /* @IndexedField(index=false, store=false) @SortableField */ " +
            "   required int32 age = 1;\n" +
            "}";

      SerializationContext serCtx = ProtobufMetadataManagerImpl.getSerializationContextInternal(cacheManager);
      serCtx.registerProtoFiles(FileDescriptorSource.fromString("test1.proto", testProto));
   }

   @Test(expectedExceptions = DescriptorParserException.class, expectedExceptionsMessageRegExp = "java.lang.IllegalStateException: Annotation 'IndexedField' cannot be used together with 'Analyzer' on field test.User2.name")
   public void testIndexedFieldAndAnalyzer() throws Exception {
      String testProto = "package test;\n" +
            "/* @Indexed */" +
            "message User2 {\n" +
            "   /* @IndexedField(index=false, store=false) @Analyzer(definition=\"standard\") */" +
            "   required string name = 1;\n" +
            "}";

      SerializationContext serCtx = ProtobufMetadataManagerImpl.getSerializationContextInternal(cacheManager);
      serCtx.registerProtoFiles(FileDescriptorSource.fromString("test1.proto", testProto));
   }

   @Test(expectedExceptions = DescriptorParserException.class, expectedExceptionsMessageRegExp = "java.lang.IllegalStateException: Cannot specify an analyzer for field test.User3.name unless the field is analyzed.")
   public void testAnalyzerForNotAnalyzedField() throws Exception {
      String testProto = "package test;\n" +
            "/* @Indexed */ message User3 {\n" +
            "   /* @Field(store=Store.NO, index=Index.YES, analyze=Analyze.NO, analyzer=@Analyzer(definition=\"standard\")) */" +
            "   required string name = 1;\n" +
            "}";

      SerializationContext serCtx = ProtobufMetadataManagerImpl.getSerializationContextInternal(cacheManager);
      serCtx.registerProtoFiles(FileDescriptorSource.fromString("test2.proto", testProto));
   }
}
