package org.arquillian.cube.docker.impl.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.commons.io.FileUtils;
import org.arquillian.cube.containerobject.CubeDockerFile;
import org.arquillian.cube.impl.util.Which;

public class DockerFileUtil {

    private DockerFileUtil() {
        super();
    }

    public static void copyDockerfileDirectory(Class<?> containerObject, CubeDockerFile cubeDockerFile, File output)
        throws IOException {
        if (cubeDockerFile == null) {
            throw new IllegalArgumentException("No CubeDockerFile annotation is provided");
        }

        String dockerfilePath = resolveDockerfileLocation(containerObject, cubeDockerFile);
        copyDockerfileDirectoryFromClasspath(containerObject, dockerfilePath, output);
    }

    private static File createTemporaryFolder(Class<?> containerObject) throws IOException {
        File dir = File.createTempFile(containerObject.getSimpleName(), "Dockerfile");
        dir.delete();
        if (!dir.mkdirs()) {
            throw new IllegalStateException(
                String.format("Directory %s for storing Dockerfile cannot be created.", dir));
        }

        return dir;
    }

    private static String resolveDockerfileLocation(Class<?> containerObject, CubeDockerFile cubeDockerFile) {
        String prefix = null;
        if (isSpecificDockerfileLocationSet(cubeDockerFile)) {
            prefix = cubeDockerFile.value();
        } else {
            prefix = containerObject.getName();
        }
        return prefix.replace('.', '/').replace('$', '/');
    }

    private static boolean isSpecificDockerfileLocationSet(CubeDockerFile cubeDockerFile) {
        return !cubeDockerFile.value().isEmpty();
    }

    private static void copyDockerfileDirectoryFromClasspath(Class<?> containerObject, String dockerfileLocation,
        File dir) throws IOException {
        File jar = null;
        try {
            jar = Which.jarFile(containerObject);
        } catch (IllegalArgumentException | IOException e) {
            throw new IllegalArgumentException(e);
        }

        if (jar != null && jar.isFile()) {
            // files are packaged into a jar/war. extract them
            dockerfileLocation += "/";
            copyDockerfileDirectoryFromPackaged(jar, dockerfileLocation, dir);
        } else {
            // Dockerfile is not packaged into a jar file, so copy locally
            copyDockerfileDirectoryFromLocal(dockerfileLocation, dir);
        }
    }

    private static void copyDockerfileDirectoryFromPackaged(File jar, String location, File outputDirectory)
        throws IOException {
        try (JarFile j = new JarFile(jar)) {
            Enumeration<JarEntry> e = j.entries();
            while (e.hasMoreElements()) {
                JarEntry je = e.nextElement();
                if (je.getName().startsWith(location)) {
                    File dst = new File(outputDirectory, je.getName().substring(location.length()));
                    if (je.isDirectory()) {
                        dst.mkdirs();
                    } else {
                        try (InputStream in = j.getInputStream(je)) {
                            Files.copy(in, Paths.get(dst.toURI()));
                        }
                    }
                }
            }
        }
    }

    private static void copyDockerfileDirectoryFromLocal(String location, File outputDirectory) throws IOException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        URL resourceDir = classLoader.getResource(location);

        if (resourceDir == null) {
            throw new IllegalArgumentException(
                String.format("No location found at %s", location)
            );
        }
        copyFile(outputDirectory, resourceDir);
    }

    private static void copyFile(File outputDirectory, URL resourceDir) throws IOException {
        File dockerFileDir;
        try {
            dockerFileDir = new File(resourceDir.toURI());
        } catch (URISyntaxException e) {
            dockerFileDir = new File(resourceDir.getPath());
        }
        FileUtils.copyDirectory(dockerFileDir, outputDirectory);
    }
}
