/*
 * Copyright © 2014-2017 EntIT Software LLC, a Micro Focus company (L.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
 *
 *     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 io.cloudslang.dependency.impl.services;

import io.cloudslang.dependency.api.services.DependencyService;
import io.cloudslang.dependency.api.services.MavenConfig;
import io.cloudslang.dependency.impl.services.utils.UnzipUtil;
import io.cloudslang.score.events.EventBus;
import io.cloudslang.score.events.EventConstants;
import io.cloudslang.score.events.ScoreEvent;
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.io.File;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.test.util.AssertionErrors.assertEquals;

/**
 * @author A. Eskin
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DependencyServiceTest.TestConfig.class)
public class DependencyServiceTest {
    private static boolean shouldRunMaven;
    static  {
        ClassLoader classLoader = DependencyServiceTest.class.getClassLoader();
        @SuppressWarnings("ConstantConditions") String settingsXmlPath = classLoader.getResource("settings.xml").getPath();
        File rootHome = new File(settingsXmlPath).getParentFile();
        File mavenHome = new File(rootHome, "maven");
        UnzipUtil.unzipToFolder(mavenHome.getAbsolutePath(), classLoader.getResourceAsStream("maven.zip"));

        System.setProperty(MavenConfigImpl.MAVEN_HOME,  mavenHome.getAbsolutePath());

        shouldRunMaven = System.getProperties().containsKey(MavenConfigImpl.MAVEN_REMOTE_URL) &&
                System.getProperties().containsKey(MavenConfigImpl.MAVEN_PLUGINS_URL);
        System.setProperty(MavenConfigImpl.MAVEN_REPO_LOCAL, new TestConfig().mavenConfig().getLocalMavenRepoPath());

        //noinspection ConstantConditions
        System.setProperty("maven.home", classLoader.getResource("maven").getPath());

        String localRepository = System.getProperty(MavenConfigImpl.MAVEN_REPO_LOCAL);
        if (localRepository != null && !localRepository.isEmpty()) {
            System.setProperty("maven.repo.local", localRepository);
        }

        System.setProperty(MavenConfigImpl.MAVEN_SETTINGS_PATH, settingsXmlPath);
        //noinspection ConstantConditions
        System.setProperty(MavenConfigImpl.MAVEN_M2_CONF_PATH, classLoader.getResource("m2.conf").getPath());
    }


    @Autowired
    private DependencyService dependencyService;

    @Autowired
    private EventBus eventBus;

    @After
    public void cleanup() {
        String basePath = new TestConfig().mavenConfig().getLocalMavenRepoPath();
        new File(basePath + "/junit/junit/4.12/junit-4.12.path").delete();
        new File(basePath + "/groupId1/mvn_artifact1/1.0/mvn_artifact1-1.0.path").delete();
        reset(eventBus);
    }

    @Test
    public void testMultipleDependencyResolution() {
        Set<String> ret = dependencyService.getDependencies(new HashSet<>(Arrays.asList("groupId1:test-artifact:1.0",
                "groupId1:test-artifact1:1.1")));
        List<String> referenceList = Arrays.asList("C:/aaa/bbb/ccc.jar", "C:/bbb/ccc/ddd.zip", "C:/ccc/ddd/eee/fff.jar",
                "C:/aaaa/bbbb/cccc.jar", "C:/bbbb/cccc/dddd.zip");
        Assert.assertTrue("Unexpected returned set", ret.containsAll(referenceList) && ret.size() == referenceList.size());
    }

    @Test
    public void testSingleDependencyResolution() {
        Set<String> ret = dependencyService.getDependencies(new HashSet<>(Collections.singletonList("groupId1:test-artifact1:1.1")));
        List<String> referenceList = Arrays.asList("C:/aaaa/bbbb/cccc.jar", "C:/bbbb/cccc/dddd.zip");
        Assert.assertTrue("Unexpected returned set", ret.containsAll(referenceList) && ret.size() == referenceList.size());
    }

    @Test
    public void testEmptyResourceSet() {
        Set<String> ret1 = dependencyService.getDependencies(new HashSet<String>());
        Assert.assertTrue("Unexpected returned set", ret1.isEmpty());
    }

    @Test
    public void testMalformedGav() {
        try {
            dependencyService.getDependencies(new HashSet<>(Collections.singletonList("groupId1:test-artifact1")));
            Assert.fail("Expected IllegalArgumentException, but succeeded");
        } catch (IllegalArgumentException ignore) {

        }
    }

    @Test
    public void testBuildClassPath1() throws InterruptedException {
        Assume.assumeTrue(shouldRunMaven);
        Set <String> ret = dependencyService.getDependencies(new HashSet<>(Collections.singletonList("groupId1:mvn_artifact1:1.0")));
        final List<File> retFiles = new ArrayList<>();
        for (String s : ret) {
            retFiles.add(new File(s));
        }
        String basePath = new TestConfig().mavenConfig().getLocalMavenRepoPath();
        List<File> referenceList = Arrays.asList(
                new File(basePath + "/junit/junit/4.12/junit-4.12.jar"),
                new File(basePath + "/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar"),
                new File(basePath + "/org/springframework/spring-core/4.2.5.RELEASE/spring-core-4.2.5.RELEASE.jar"),
                new File(basePath + "/commons-logging/commons-logging/1.2/commons-logging-1.2.jar"),
                new File(basePath + "/groupId1/mvn_artifact1/1.0/mvn_artifact1-1.0.jar"));

        final ArgumentCaptor<ScoreEvent> argumentCaptor =
                ArgumentCaptor.forClass(ScoreEvent.class);

        verify(eventBus, times(2)).dispatch(argumentCaptor.capture());

        List<ScoreEvent> scoreEvents = argumentCaptor.getAllValues();

        assertEquals( "Argument does not match", "MAVEN_DEPENDENCY_BUILD", scoreEvents.get(0).getEventType());
        Map<String, Serializable> dataBuildEvent = (Map<String, Serializable>) scoreEvents.get(0).getData();
        assertEquals("Argument does not match", "Downloading artifact with gav: groupId1:mvn_artifact1:1.0 ",
                dataBuildEvent.get(EventConstants.MAVEN_DEPENDENCY_BUILD));

        assertEquals( "Argument does not match", "MAVEN_DEPENDENCY_BUILD_FINISHED", scoreEvents.get(1).getEventType());
        Map<String, Serializable> dataBuildFinishedEvent = (Map<String, Serializable>) scoreEvents.get(1).getData();
        assertEquals("Argument does not match", "Download complete for artifact with gav: groupId1:mvn_artifact1:1.0 ",
                dataBuildFinishedEvent.get(EventConstants.MAVEN_DEPENDENCY_BUILD_FINISHED));

        Assert.assertTrue("Unexpected returned set", retFiles.containsAll(referenceList) && ret.size() == referenceList.size());
    }

    @Test
    public void testBuildClassPath1_1() {
        Assume.assumeTrue(shouldRunMaven);
        Set <String> ret = dependencyService.getDependencies(new HashSet<>(Collections.singletonList("groupId1:mvn_artifact1:1.1")));
        final List<File> retFiles = new ArrayList<>();
        for (String s : ret) {
            retFiles.add(new File(s));
        }
        String basePath = new TestConfig().mavenConfig().getLocalMavenRepoPath();
        List<File> referenceList = Collections.singletonList(
                new File(basePath + "/groupId1/mvn_artifact1/1.1/mvn_artifact1-1.1.jar"));
        Assert.assertTrue("Unexpected returned set", retFiles.containsAll(referenceList) && ret.size() == referenceList.size());
    }

    @Test
    public void testBuildClassPath1_2() {
        Assume.assumeTrue(shouldRunMaven);
        Set <String> ret = dependencyService.getDependencies(new HashSet<>(Collections.singletonList("groupId1:mvn_artifact1:1.2")));
        final List<File> retFiles = new ArrayList<>();
        for (String s : ret) {
            retFiles.add(new File(s));
        }
        String basePath = new TestConfig().mavenConfig().getLocalMavenRepoPath();
        List<File> referenceList = Collections.singletonList(
                new File(basePath + "/groupId1/mvn_artifact1/1.2/mvn_artifact1-1.2.jar"));
        Assert.assertTrue("Unexpected returned set", retFiles.containsAll(referenceList) && ret.size() == referenceList.size());
    }

    @Test
    public void testBuildClassPath2() {
        Assume.assumeTrue(shouldRunMaven);
        String basePath = new TestConfig().mavenConfig().getLocalMavenRepoPath();
        File junitArtifactDir = new File(basePath + "/junit");
        if(junitArtifactDir.exists()) {
            boolean isDeleted1 = new File(basePath + "/junit/junit/4.12/junit-4.12.jar").delete();
            boolean isDeleted2 = new File(basePath + "/junit/junit/4.12/junit-4.12.pom").delete();
            Assert.assertTrue(isDeleted1 && isDeleted2);
        }
        Set <String> ret = dependencyService.getDependencies(new HashSet<>(Collections.singletonList("junit:junit:4.12")));
        final List<File> retFiles = new ArrayList<>();
        for (String s : ret) {
            retFiles.add(new File(s));
        }
        List<File> referenceList = Arrays.asList(
                new File(basePath + "/junit/junit/4.12/junit-4.12.jar"),
                new File(basePath + "/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar"));
        Assert.assertTrue("Unexpected returned set", retFiles.containsAll(referenceList) && ret.size() == referenceList.size());
    }


    @Configuration
    static class TestConfig {
        @Bean
        public DependencyService dependencyService() {
            return new DependencyServiceImpl();
        }

        @Bean
        public EventBus eventBus() {
            return mock(EventBus.class);
        }

        @Bean
        public MavenConfig mavenConfig() {
            return new MavenConfig() {
                @Override
                public String getLocalMavenRepoPath() {
                    String testMvnRepo = "test-mvn-repo";
                    URL url = getClass().getClassLoader().getResource(testMvnRepo);
                    if(url != null) {
                        return url.getPath();
                    }
                    return null;
                }

                @Override
                public String getRemoteMavenRepoUrl() {
                    return null;
                }
            };
        }

    }

}
