/*
 * 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.teradata.tempto.internal.convention;

import com.google.common.collect.Maps;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

import static com.teradata.tempto.internal.convention.SqlTestsFileUtils.copyRecursive;
import static java.lang.ClassLoader.getSystemResources;
import static java.nio.file.FileSystems.newFileSystem;
import static java.nio.file.Files.createTempDirectory;
import static java.nio.file.Files.exists;

public final class ConventionTestsUtils
{
    private static final String CONVENTION_TESTS_ROOT_DIR = "sql-tests";
    private static final String FILE_SCHEME = "file";
    private static final String JAR_SCHEME = "jar";
    private static final String JAR_FILE_PREFIX = "!";

    private static Optional<Path> temporaryTestsRootPath = Optional.empty();

    public static Optional<Path> getConventionsTestsPath(String child)
    {
        try {
            Enumeration<URL> productTestUrls = getSystemResources(CONVENTION_TESTS_ROOT_DIR + "/" + child);
            List<URI> productTestUris = new ArrayList<>();
            while (productTestUrls.hasMoreElements()) {
                URL url = productTestUrls.nextElement();
                productTestUris.add(url.toURI());
            }

            if (!productTestUris.isEmpty()) {
                return Optional.of(copyTestsToTemporaryDirectory(productTestUris, child));
            }
            else {
                return Optional.empty();
            }
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }
        catch (URISyntaxException e) {
            throw new IllegalArgumentException(e);
        }
    }

    private static Path copyTestsToTemporaryDirectory(List<URI> productTestsUris, String child)
            throws IOException
    {
        ensureTemporaryTestsRootPathExists();

        Path temporaryTestsPath = temporaryTestsRootPath.get().resolve(child);
        if (!exists(temporaryTestsPath)) {
            for (URI uri : productTestsUris) {
                processPathFromUri(uri, (Path path) -> copyRecursive(path, temporaryTestsPath));
            }
        }

        return temporaryTestsPath;
    }

    private static void ensureTemporaryTestsRootPathExists()
            throws IOException
    {
        if (!temporaryTestsRootPath.isPresent()) {
            temporaryTestsRootPath = Optional.of(createTempDirectory("product_tests"));
            temporaryTestsRootPath.get().toFile().deleteOnExit();
        }
    }

    /**
     * lConverts given {@link URI} to {@link Path} and process it using
     * given {@link Consumer}. If the URI points to a JAR file it opens
     * a new {@link FileSystem} for the duration of processing.
     * <p>
     * Example URIs: jar:/foo/bar.jar!/bar/foo; file://foo/bar
     */
    private static void processPathFromUri(URI uri, Consumer<Path> action)
    {
        String scheme = uri.getScheme();
        if (scheme.equals(FILE_SCHEME)) {
            action.accept(Paths.get(uri));
            return;
        }

        if (!scheme.equals(JAR_SCHEME)) {
            throw new IllegalArgumentException("URI scheme not supported: " + uri);
        }

        String s = uri.toString();
        int separator = s.indexOf(JAR_FILE_PREFIX);
        String entryName = s.substring(separator + JAR_FILE_PREFIX.length());
        URI fileUri = URI.create(s.substring(0, separator));

        try (FileSystem fileSystem = newFileSystem(fileUri, Maps.newHashMap())) {
            action.accept(fileSystem.getPath(entryName));
        }
        catch (IOException e) {
            throw new RuntimeException("Could not process URI: " + uri, e);
        }
    }

    private ConventionTestsUtils()
    {
    }
}

@@ -644,16 +644,73 @@
 ct.Maps;
+%0Aimport org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;
 %0A%0Aimport
@@ -951,24 +951,50 @@
 numeration;%0A
+import java.util.HashSet;%0A
 import java.
@@ -1031,16 +1031,38 @@
 tional;%0A
+import java.util.Set;%0A
 import j
@@ -1737,24 +1737,119 @@
 l.empty();%0A%0A
+    private static final Logger LOGGER = LoggerFactory.getLogger(ConventionTestsUtils.class);%0A%0A
     public s
@@ -2047,19 +2047,18 @@
         
-Lis
+Se
 t%3CURI%3E p
@@ -2078,24 +2078,22 @@
 s = new 
-ArrayLis
+HashSe
 t%3C%3E();%0A 
@@ -2261,32 +2261,126 @@
 %0A            %7D%0A%0A
+            LOGGER.debug(%22discovered following convention tests uris: %25s%22, productTestUris);%0A%0A
             if (
@@ -2844,19 +2844,18 @@
 rectory(
-Lis
+Se
 t%3CURI%3E p
@@ -3087,24 +3087,24 @@
 stsPath)) %7B%0A
-
             
@@ -3134,24 +3134,121 @@
 estsUris) %7B%0A
+                LOGGER.debug(%22copying convention tests from %25s to %25s%22, uri, temporaryTestsPath);%0A
             

Use set for collecting covention based tests paths