/**
 * Copyright (c) 2016 YCSB contributors. All rights reserved.
 * <p/>
 * Licensed 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
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * 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. See accompanying
 * LICENSE file.
 */

package com.yahoo.ycsb.db;

import com.yahoo.ycsb.ByteIterator;
import com.yahoo.ycsb.DB;
import com.yahoo.ycsb.Status;
import com.yahoo.ycsb.StringByteIterator;
import org.apache.solr.client.solrj.embedded.JettyConfig;
import org.apache.solr.cloud.MiniSolrCloudCluster;
import org.apache.solr.common.util.NamedList;
import org.junit.*;

import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public abstract class SolrClientBaseTest {

  protected static MiniSolrCloudCluster miniSolrCloudCluster;
  private DB instance;
  private final static HashMap<String, ByteIterator> MOCK_DATA;
  protected final static String MOCK_TABLE = "ycsb";
  private final static String MOCK_KEY0 = "0";
  private final static String MOCK_KEY1 = "1";
  private final static int NUM_RECORDS = 10;

  static {
    MOCK_DATA = new HashMap<>(NUM_RECORDS);
    for (int i = 0; i < NUM_RECORDS; i++) {
      MOCK_DATA.put("field" + i, new StringByteIterator("value" + i));
    }
  }

  @BeforeClass
  public static void onlyOnce() throws Exception {
    Path miniSolrCloudClusterTempDirectory = Files.createTempDirectory("miniSolrCloudCluster");
    miniSolrCloudClusterTempDirectory.toFile().deleteOnExit();
    miniSolrCloudCluster = new MiniSolrCloudCluster(1, miniSolrCloudClusterTempDirectory, JettyConfig.builder().build());

    // Upload Solr configuration
    URL configDir = SolrClientBaseTest.class.getClassLoader().getResource("solr_config");
    assertNotNull(configDir);
    miniSolrCloudCluster.uploadConfigDir(new File(configDir.toURI()), MOCK_TABLE);
  }

  @AfterClass
  public static void destroy() throws Exception {
    if(miniSolrCloudCluster != null) {
      miniSolrCloudCluster.shutdown();
    }
  }

  @Before
  public void setup() throws Exception {
    NamedList<Object> namedList = miniSolrCloudCluster.createCollection(MOCK_TABLE, 1, 1, MOCK_TABLE, null);
    assertEquals(namedList.indexOf("success", 0), 1);
    Thread.sleep(1000);

    instance = getDB();
  }

  @After
  public void tearDown() throws Exception {
    if(miniSolrCloudCluster != null) {
      NamedList<Object> namedList = miniSolrCloudCluster.deleteCollection(MOCK_TABLE);
      assertEquals(namedList.indexOf("success", 0), 1);
      Thread.sleep(1000);
    }
  }

  @Test
  public void testInsert() throws Exception {
    Status result = instance.insert(MOCK_TABLE, MOCK_KEY0, MOCK_DATA);
    assertEquals(Status.OK, result);
  }

  @Test
  public void testDelete() throws Exception {
    Status result = instance.delete(MOCK_TABLE, MOCK_KEY1);
    assertEquals(Status.OK, result);
  }

  @Test
  public void testRead() throws Exception {
    Set<String> fields = MOCK_DATA.keySet();
    HashMap<String, ByteIterator> resultParam = new HashMap<>(NUM_RECORDS);
    Status result = instance.read(MOCK_TABLE, MOCK_KEY1, fields, resultParam);
    assertEquals(Status.OK, result);
  }

  @Test
  public void testUpdate() throws Exception {
    HashMap<String, ByteIterator> newValues = new HashMap<>(NUM_RECORDS);

    for (int i = 0; i < NUM_RECORDS; i++) {
      newValues.put("field" + i, new StringByteIterator("newvalue" + i));
    }

    Status result = instance.update(MOCK_TABLE, MOCK_KEY1, newValues);
    assertEquals(Status.OK, result);

    //validate that the values changed
    HashMap<String, ByteIterator> resultParam = new HashMap<>(NUM_RECORDS);
    instance.read(MOCK_TABLE, MOCK_KEY1, MOCK_DATA.keySet(), resultParam);

    for (int i = 0; i < NUM_RECORDS; i++) {
      assertEquals("newvalue" + i, resultParam.get("field" + i).toString());
    }
  }

  @Test
  public void testScan() throws Exception {
    Set<String> fields = MOCK_DATA.keySet();
    Vector<HashMap<String, ByteIterator>> resultParam = new Vector<>(NUM_RECORDS);
    Status result = instance.scan(MOCK_TABLE, MOCK_KEY1, NUM_RECORDS, fields, resultParam);
    assertEquals(Status.OK, result);
  }

  /**
   * Gets the test DB.
   *
   * @return The test DB.
   */
  protected DB getDB() {
    return getDB(new Properties());
  }

  /**
   * Gets the test DB.
   *
   * @param props
   *    Properties to pass to the client.
   * @return The test DB.
   */
  protected abstract DB getDB(Properties props);
}
