package de.renew.plugin.locate;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collection;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.Answers;
import org.mockito.Mockito;

import de.renew.plugin.PluginProperties;

import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * Test class that tests the methods of the abstract class PluginFileFinder
 *
 * @author Philipp Schult
 */
public class PluginFileFinderTest {
    private static String _currentDir;
    private static URL _currentDirURL;
    private static File _currentFile;

    @BeforeAll
    public static void setUpAll() throws IOException {
        String currentBaseDir = System.getProperty("user.dir");
        _currentDir = currentBaseDir + "/Dummyplugin";
        _currentFile = new File(_currentDir);
        //in case the directory already exists, delete it and its contents beforehand
        if (_currentFile.exists()) {
            deleteDirectory(_currentFile);
        }
        Files.createDirectory(Paths.get(_currentDir));
        _currentDirURL = _currentFile.toURI().toURL();
        //create some files and directories
        Files.createFile(Paths.get(_currentDir + "/localfile.txt"));
        Files.createDirectory(Paths.get(_currentDir + "/localdirectory/"));
    }

    @AfterAll
    public static void tearDownAll() {
        //delete the files after testing is done
        deleteDirectory(_currentFile);
    }

    @Test
    public void testFindPluginLocationsDirIsNull() {
        //given
        PluginFileFinder testee = createFileFinder(null);
        //when
        Collection<PluginProperties> result = testee.findPluginLocations();
        //then
        assertTrue(result.isEmpty());
    }

    @Test
    public void testFindPluginLocationsDirDoesNotExist() throws MalformedURLException {
        //given
        File file = new File(_currentDir + "/doesnotexist");
        URL url = file.toURI().toURL();
        PluginFileFinder testee = createFileFinder(url);
        //when
        Collection<PluginProperties> result = testee.findPluginLocations();
        //then
        assertTrue(result.isEmpty());
    }

    @Test
    public void testFindPluginLocationsIsNotADirectory() throws MalformedURLException {
        //given
        File file = new File(_currentDir + "/localfile.txt");
        URL url = file.toURI().toURL();
        PluginFileFinder testee = createFileFinder(url);
        //when
        Collection<PluginProperties> result = testee.findPluginLocations();
        //then
        assertTrue(result.isEmpty());
    }

    /*
    As the PluginFileFinder leaves the implementation of getPluginFiles and getPluginConfigurations up to
    the inheriting classes and both of those methods make up the main part of the findPluginLocation method,
    the test for this class will only cover the edge cases that are tested above.
     */

    @Test
    public void testCheckPluginLocationURLIsNull() {
        //given
        PluginFileFinder testee = createFileFinder(_currentDirURL);
        //when
        PluginProperties result = testee.checkPluginLocation(null);
        //then
        assertNull(result);
    }

    @Test
    public void testCheckPluginLocationURLProtocolIsNotFile() throws MalformedURLException {
        //given
        PluginFileFinder testee = createFileFinder(_currentDirURL);
        //create a non-file URL (e.g. a jar URL)
        URL url = new URL("jar:file:" + _currentDir + "/test.jar!/");
        //when
        PluginProperties result = testee.checkPluginLocation(url);
        //then
        assertNull(result);
    }

    /*
    Similar to above, the main part of this method relies on the inheriting class implementing the
    getPluginConfigurations method, so this test class will only cover these two edge cases.
     */

    //helper methods
    /**
     * deletes a directory recursively
     * @param dir directory to be deleted
     * @return boolean for the purposes of recursion
     */
    private static boolean deleteDirectory(File dir) {
        File[] dirContent = dir.listFiles();
        if (dirContent != null) {
            for (File file : dirContent) {
                deleteDirectory(file);
            }
        }
        return dir.delete();
    }

    /**
     * Creates a PluginFileFinder mock using the given url as a parameter. The mock uses the implementation
     * of methods as they are in the abstract class.
     * @param url the url to be used as a parameter
     * @return a PluginFileFinder mock
     */
    private static PluginFileFinder createFileFinder(URL url) {
        return Mockito.mock(
            PluginFileFinder.class,
            Mockito.withSettings().useConstructor(url).defaultAnswer(Answers.CALLS_REAL_METHODS));
    }
}
