/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * 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
 *
 *      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.alibaba.nacos.client.naming.cache;

import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;

class DiskCacheTest {
    
    private static final String CACHE_DIR = DiskCacheTest.class.getClassLoader().getResource("disk_cache_test").getPath() + "cache/";
    
    private ServiceInfo serviceInfo;
    
    private Instance instance;
    
    @BeforeEach
    void setUp() {
        System.out.println(CACHE_DIR);
        serviceInfo = new ServiceInfo("G@@testName", "testClusters");
        instance = new Instance();
        instance.setClusterName("testClusters");
        instance.setIp("1.1.1.1");
        instance.setPort(1234);
        instance.setServiceName("testName");
        instance.addMetadata("chinese", "中文");
        serviceInfo.setHosts(Collections.singletonList(instance));
    }
    
    @AfterEach
    void tearDown() {
        File file = new File(CACHE_DIR);
        if (file.exists() && file.list().length > 0) {
            for (File each : file.listFiles()) {
                each.delete();
            }
            file.deleteOnExit();
        }
    }
    
    @Test
    void testCache() {
        DiskCache.write(serviceInfo, CACHE_DIR);
        Map<String, ServiceInfo> actual = DiskCache.read(CACHE_DIR);
        assertEquals(1, actual.size());
        assertTrue(actual.containsKey(serviceInfo.getKey()));
        assertServiceInfo(actual.get(serviceInfo.getKey()), serviceInfo);
    }
    
    @Test
    void testWriteCacheWithErrorPath() {
        File file = new File(CACHE_DIR, serviceInfo.getKeyEncoded());
        try {
            file.mkdirs();
            DiskCache.write(serviceInfo, CACHE_DIR);
            assertTrue(file.isDirectory());
        } finally {
            file.delete();
        }
    }
    
    @Test
    void testReadCacheForAllSituation() {
        String dir = DiskCacheTest.class.getClassLoader().getResource("disk_cache_test").getPath();
        Map<String, ServiceInfo> actual = DiskCache.read(dir);
        assertEquals(2, actual.size());
        assertTrue(actual.containsKey("legal@@no_name@@file"));
        assertEquals("1.1.1.1", actual.get("legal@@no_name@@file").getHosts().get(0).getIp());
        assertTrue(actual.containsKey("legal@@with_name@@file"));
        assertEquals("1.1.1.1", actual.get("legal@@with_name@@file").getHosts().get(0).getIp());
    }
    
    @Test
    void testReadCacheForNullFile() {
        Map<String, ServiceInfo> actual = DiskCache.read(null);
        assertTrue(actual.isEmpty());
    }
    
    @Test
    void testParseServiceInfoFromNonExistFile() throws UnsupportedEncodingException {
        File file = new File("non%40%40exist%40%40file");
        Map<String, ServiceInfo> actual = DiskCache.parseServiceInfoFromCache(file);
        assertTrue(actual.isEmpty());
    }
    
    @Test
    void testCreateFileIfAbsentForDir() throws Throwable {
        assertThrows(IllegalStateException.class, () -> {
            File file = mock(File.class);
            DiskCache.createFileIfAbsent(file, true);
        });
    }
    
    @Test
    void testCreateFileIfAbsentForFile() throws Throwable {
        assertThrows(IllegalStateException.class, () -> {
            File file = mock(File.class);
            DiskCache.createFileIfAbsent(file, false);
        });
    }
    
    private void assertServiceInfo(ServiceInfo actual, ServiceInfo expected) {
        assertEquals(actual.getName(), expected.getName());
        assertEquals(actual.getGroupName(), expected.getGroupName());
        assertEquals(actual.getClusters(), expected.getClusters());
        assertEquals(actual.getCacheMillis(), expected.getCacheMillis());
        assertEquals(actual.getLastRefTime(), expected.getLastRefTime());
        assertEquals(actual.getKey(), expected.getKey());
        assertHosts(actual.getHosts(), expected.getHosts());
    }
    
    private void assertHosts(List<Instance> actual, List<Instance> expected) {
        assertEquals(actual.size(), expected.size());
        for (int i = 0; i < expected.size(); i++) {
            assertInstance(actual.get(i), expected.get(i));
        }
    }
    
    private void assertInstance(Instance actual, Instance expected) {
        assertEquals(actual.getServiceName(), expected.getServiceName());
        assertEquals(actual.getClusterName(), expected.getClusterName());
        assertEquals(actual.getIp(), expected.getIp());
        assertEquals(actual.getPort(), expected.getPort());
        assertEquals(actual.getMetadata(), expected.getMetadata());
    }
    
    @Test
    void testGetLineSeparator() {
        String lineSeparator = DiskCache.getLineSeparator();
        assertTrue(lineSeparator.length() > 0);
    }
}
