package org.infinispan.lucene;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import static org.infinispan.lucene.CacheTestSupport.assertTextIsFoundInIds;
import static org.infinispan.lucene.CacheTestSupport.removeByTerm;
import static org.infinispan.lucene.CacheTestSupport.writeTextToIndex;

import org.apache.lucene.store.Directory;
import org.infinispan.commons.util.Util;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.context.Flag;
import org.infinispan.persistence.jdbc.configuration.JdbcStringBasedStoreConfigurationBuilder;
import org.infinispan.lucene.directory.DirectoryBuilder;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.SingleCacheManagerTest;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.testng.AssertJUnit;
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;

/**
 * Test to verify that it's possible to use the index using a JdbcStringBasedStore
 *
 * @see org.infinispan.persistence.jdbc.stringbased.JdbcStringBasedStore
 *
 * @author Sanne Grinovero
 * @since 4.1
 */
@SuppressWarnings("unchecked")
@Test(groups = "unstable", testName = "lucene.DatabaseStoredIndexTest", description = "original group: functional")
public class DatabaseStoredIndexTest extends SingleCacheManagerTest {

   private static final String DB_URL = "jdbc:h2:mem:infinispan;DB_CLOSE_DELAY=0";

   /** The INDEX_NAME */
   private static final String INDEX_NAME = "testing index";

   private final HashMap cacheCopy = new HashMap();

   private Connection connection;

   public DatabaseStoredIndexTest() throws SQLException {
      cleanup = CleanupPhase.AFTER_METHOD;
      connection = DriverManager.getConnection(DB_URL, "sa", "");
   }

   @AfterClass(alwaysRun=true)
   public void closeDB() {
      try {
         connection.close();
      } catch (SQLException e) {
         AssertJUnit.fail("Could not close the keepalive DB connection");
      }
   }

   @Override
   protected EmbeddedCacheManager createCacheManager() throws Exception {
      ConfigurationBuilder cb = TestCacheManagerFactory.getDefaultCacheConfiguration(false);
      cb.persistence()
            .addStore(JdbcStringBasedStoreConfigurationBuilder.class)
            .preload(true)
            .key2StringMapper(LuceneKey2StringMapper.class)
            .table()
               .idColumnName("ID_COLUMN")
               .idColumnType("VARCHAR(255)")
               .tableNamePrefix("ISPN_JDBC")
               .dataColumnName("DATA_COLUMN")
               .dataColumnType("BLOB")
               .timestampColumnName("TIMESTAMP_COLUMN")
               .timestampColumnType("BIGINT")
            .simpleConnection()
               .driverClass(org.h2.Driver.class)
               .connectionUrl(DB_URL)
               .username("sa");
      return TestCacheManagerFactory.createCacheManager(cb);
   }

   @Test
   public void testIndexUsage() throws IOException {
      cache = cacheManager.getCache();
      Directory dir = DirectoryBuilder.newDirectoryInstance(cache, cache, cache, INDEX_NAME).create();
      writeTextToIndex(dir, 0, "hello database");
      assertTextIsFoundInIds(dir, "hello", 0);
      writeTextToIndex(dir, 1, "you have to store my index segments");
      writeTextToIndex(dir, 2, "so that I can shut down all nodes");
      writeTextToIndex(dir, 3, "and restart later keeping the index around");
      assertTextIsFoundInIds(dir, "index", 1, 3);
      removeByTerm(dir, "and");
      assertTextIsFoundInIds(dir, "index", 1);
      dir.close();
      for (Map.Entry me : cache.getAdvancedCache().withFlags(Flag.SKIP_CACHE_LOAD).entrySet()) {
         cacheCopy.put(me.getKey(), me.getValue());
      }
      cache.stop();
      cacheManager.stop();
   }

   @Test(dependsOnMethods="testIndexUsage")
   public void indexWasStored() throws IOException {
      cache = cacheManager.getCache();
      boolean failed = false;
      for (Object key : cacheCopy.keySet()) {
         if (key instanceof FileReadLockKey) {
            System.out.println("Key found in store, shouldn't have persisted this or should have cleaned up all readlocks on directory close:" + key);
            failed = true;
         }
         else {
            Object expected = cacheCopy.get(key);
            Object actual = cache.get(key);
            if (expected==null && actual==null)
               continue;
            if (expected instanceof byte[]) {
               expected = Util.printArray((byte[]) expected, false);
               actual = Util.printArray((byte[]) actual, false);
            }
            if (expected == null || ! expected.equals(actual)) {
               System.out.println("Failure on key["+key.toString()+"] expected value:\n\t"+expected+"\tactual value:\n\t"+actual);
               failed = true;
            }
         }
      }
      AssertJUnit.assertFalse(failed);
      AssertJUnit.assertEquals("have a different number of keys", cacheCopy.keySet().size(), cache.keySet().size());
      Directory dir = DirectoryBuilder.newDirectoryInstance(cache, cache, cache, INDEX_NAME).create();
      assertTextIsFoundInIds(dir, "index", 1);
      dir.close();
   }

}
