package com.ctrip.apollo.internals;

import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.io.Files;

import com.ctrip.apollo.core.ConfigConsts;
import com.ctrip.apollo.util.ConfigUtil;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.unidal.lookup.ComponentTestCase;

import java.io.File;
import java.util.Properties;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * Created by Jason on 4/9/16.
 */
public class LocalFileConfigRepositoryTest extends ComponentTestCase {
  private File someBaseDir;
  private String someNamespace;
  private ConfigRepository fallbackRepo;
  private Properties someProperties;
  private static String someAppId = "someApp";
  private static String someCluster = "someCluster";
  private String defaultKey;
  private String defaultValue;

  @Before
  public void setUp() throws Exception {
    super.setUp();
    someBaseDir = new File("src/test/resources/config-cache");
    someBaseDir.mkdir();

    someNamespace = "someName";
    someProperties = new Properties();
    defaultKey = "defaultKey";
    defaultValue = "defaultValue";
    someProperties.setProperty(defaultKey, defaultValue);
    fallbackRepo = mock(ConfigRepository.class);
    when(fallbackRepo.getConfig()).thenReturn(someProperties);

    defineComponent(ConfigUtil.class, MockConfigUtil.class);
  }

  @After
  public void tearDown() throws Exception {
    super.tearDown();
    recursiveDelete(someBaseDir);
  }

  //helper method to clean created files
  private void recursiveDelete(File file) {
    if (!file.exists()) {
      return;
    }
    if (file.isDirectory()) {
      for (File f : file.listFiles()) {
        recursiveDelete(f);
      }
    }
    file.delete();
  }

  private String assembleLocalCacheFileName() {
    return String.format("%s.properties", Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR)
        .join(someAppId, someCluster, someNamespace));
  }


  @Test
  public void testLoadConfigWithLocalFile() throws Exception {
    File file = new File(someBaseDir, assembleLocalCacheFileName());

    String someKey = "someKey";
    String someValue = "someValue";

    Files.write(someKey + "=" + someValue, file, Charsets.UTF_8);

    LocalFileConfigRepository localRepo = new LocalFileConfigRepository(someBaseDir, someNamespace);
    Properties properties = localRepo.getConfig();

    assertEquals(someValue, properties.getProperty(someKey));

  }

  @Test
  public void testLoadConfigWithLocalFileAndFallbackRepo() throws Exception {
    File file = new File(someBaseDir, assembleLocalCacheFileName());

    String someValue = "someValue";

    Files.write(defaultKey + "=" + someValue, file, Charsets.UTF_8);

    LocalFileConfigRepository localRepo = new LocalFileConfigRepository(someBaseDir, someNamespace);

    //when fallback is set, it will try to sync from it
    localRepo.setFallback(fallbackRepo);

    Properties properties = localRepo.getConfig();

    assertEquals(defaultValue, properties.getProperty(defaultKey));
  }

  @Test
  public void testLoadConfigWithNoLocalFile() throws Exception {
    LocalFileConfigRepository
        localFileConfigRepository =
        new LocalFileConfigRepository(someBaseDir, someNamespace);

    localFileConfigRepository.setFallback(fallbackRepo);

    Properties result = localFileConfigRepository.getConfig();

    assertThat(
        "LocalFileConfigRepository's properties should be the same as fallback repo's when there is no local cache",
        result.entrySet(), equalTo(someProperties.entrySet()));
  }

  @Test
  public void testLoadConfigWithNoLocalFileMultipleTimes() throws Exception {
    LocalFileConfigRepository localRepo =
        new LocalFileConfigRepository(someBaseDir, someNamespace);

    localRepo.setFallback(fallbackRepo);

    Properties someProperties = localRepo.getConfig();

    LocalFileConfigRepository
        anotherLocalRepoWithNoFallback =
        new LocalFileConfigRepository(someBaseDir, someNamespace);

    Properties anotherProperties = anotherLocalRepoWithNoFallback.getConfig();

    assertThat(
        "LocalFileConfigRepository should persist local cache files and return that afterwards",
        someProperties.entrySet(), equalTo(anotherProperties.entrySet()));

  }

  @Test
  public void testOnRepositoryChange() throws Exception {
    RepositoryChangeListener someListener = mock(RepositoryChangeListener.class);

    LocalFileConfigRepository localFileConfigRepository =
        new LocalFileConfigRepository(someBaseDir, someNamespace);
    localFileConfigRepository.setFallback(fallbackRepo);
    localFileConfigRepository.addChangeListener(someListener);

    localFileConfigRepository.getConfig();

    Properties anotherProperties = new Properties();
    anotherProperties.put("anotherKey", "anotherValue");

    localFileConfigRepository.onRepositoryChange(someNamespace, anotherProperties);

    final ArgumentCaptor<Properties> captor = ArgumentCaptor.forClass(Properties.class);

    verify(someListener, times(1)).onRepositoryChange(eq(someNamespace), captor.capture());

    assertEquals(anotherProperties, captor.getValue());

  }

  public static class MockConfigUtil extends ConfigUtil {
    @Override
    public String getAppId() {
      return someAppId;
    }

    @Override
    public String getCluster() {
      return someCluster;
    }
  }

}

@@ -402,16 +402,77 @@
 o.File;%0A
+import java.io.FileOutputStream;%0Aimport java.io.IOException;%0A
 import j
@@ -2434,210 +2434,237 @@
 n %7B%0A
-    File file = new File(someBaseDir, assembleLocalCacheFileName());%0A%0A    String someKey = %22someKey%22;%0A    String someValue = %22someValue%22;%0A%0A    Files.write(someKey + %22=%22 + someValue, file, Charsets.UTF_8
+%0A    String someKey = %22someKey%22;%0A    String someValue = %22someValue%5Cnxxx%5Cnyyy%22;%0A%0A    Properties someProperties = new Properties();%0A    someProperties.setProperty(someKey, someValue);%0A    createLocalCachePropertyFile(someProperties
 );%0A%0A
@@ -5808,10 +5808,417 @@
  %7D%0A  %7D%0A%0A
+  private File createLocalCachePropertyFile(Properties properties) throws IOException %7B%0A    File file = new File(someBaseDir, assembleLocalCacheFileName());%0A    FileOutputStream in = null;%0A    try %7B%0A      in = new FileOutputStream(file);%0A      properties.store(in, %22Persisted by LocalFileConfigRepositoryTest%22);%0A    %7D finally %7B%0A      if (in != null) %7B%0A        in.close();%0A      %7D%0A    %7D%0A    return file;%0A  %7D%0A
 %7D%0A

add ut for property file with new line - \n