package org.wildfly.swarm.repository;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.xml.transform.TransformerException;

import static org.wildfly.swarm.repository.PomUtils.extract;

public class FullProjectGenerator {

    private static final String POM_TEMPLATE = "pom.xml-template";
    private static final String DEPENDENCIES_PLACEHOLDER = "FRACTIONS_FROM_BOM";
    private static final String PROPERTIES = "PROPERTIES";
    private static final String SWARM_VERSION = "SWARM_VERSION";
    private static final String SWARM_FRACTION_PLUGIN_VERSION = "SWARM_FRACTION_PLUGIN_VERSION";
    private static final String BOM_ARTIFACT = "BOM_ARTIFACT";
    private static final String WORK_DIR = "target/";

    static final String FULL_PROJECT_DIR = WORK_DIR + "/full-project";
    private static final String BOM_DIR = WORK_DIR + "/bom-dir";


    private final Path bomDir;
    private final Path projectDir;

    private FullProjectGenerator() {
        bomDir = Paths.get(BOM_DIR);
        projectDir = Paths.get(FULL_PROJECT_DIR);
    }


    private void prepareProject() {
        try {
            projectDir.toFile().mkdir();
            preparePom();
            prepareWebXml();
        } catch (Exception e) {
            e.printStackTrace();
            fail("Failed to prepare project");
        }
    }

    private void preparePom() throws Exception {
        File bomFile = getFile(bomDir, e -> e.startsWith("bom") && e.endsWith(".pom"));
        String dependencies = extract(bomFile, "//dependencyManagement/dependencies/*")
                .skipping("arquillian", "shrinkwrap", "eap-runtime-artifacts", "commons-logging")
                .asString();
        String properties = extract(bomFile, "//properties/*").asString();
        String swarmVersion = extract(bomFile, "/project/version/text()").asString();
        String bomArtifact = extract(bomFile, "/project/artifactId/text()").asString();
        String swarmFractionPluginVersion = determineFractionPluginVersion();
        String pomContent = readTemplate()
                .replace(DEPENDENCIES_PLACEHOLDER, dependencies)
                .replace(PROPERTIES, properties)
                .replace(SWARM_VERSION, swarmVersion)
                .replace(SWARM_FRACTION_PLUGIN_VERSION, swarmFractionPluginVersion)
                .replace(BOM_ARTIFACT, bomArtifact);
        File pom = new File(projectDir.toFile(), "pom.xml");
        pom.createNewFile();
        try (FileWriter writer = new FileWriter(pom)) {
            writer.append(pomContent);
        }
    }

    String determineFractionPluginVersion() throws TransformerException {
        File buildParent = getFile(bomDir, e->e.startsWith("build-parent") && e.endsWith(".pom"));
        return extract(buildParent, "//properties/version.wildfly.swarm.fraction.plugin/text()").asString();
    }

    private void prepareWebXml() throws IOException {
        File webInf = new File(projectDir.toFile(), "src/main/webapp/WEB-INF");
        webInf.mkdirs();
        File webXml = new File(webInf, "web.xml");
        webXml.createNewFile();
    }

    private String readTemplate() {
        StringBuilder result = new StringBuilder();
        InputStream stream = FullProjectGenerator.class.getClassLoader().getResourceAsStream(POM_TEMPLATE);
        try (InputStreamReader rawReader = new InputStreamReader(stream);
             BufferedReader reader = new BufferedReader(rawReader)) {
            String line = null;
            while ((line = reader.readLine()) != null) {
                result.append(line).append("\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
            fail("error parsing the pom readTemplate");
        }
        return result.toString();
    }

    private File getFile(Path bomsPath, Function<String, Boolean> fileMatcher) {
        File[] contents = bomsPath.toFile().listFiles();
        List<File> bomFiles =
                Stream.of(contents)
                        .filter(e -> fileMatcher.apply(e.getName()))
                        .collect(Collectors.toList());
        if (bomFiles.size() != 1) {
            fail("Expected exactly one bom file in the directory, found: " + bomFiles.size());
        }
        return bomFiles.get(0);
    }

    public static void main(String[] args) {
        System.out.println("Preparing project... ");
        new FullProjectGenerator().prepareProject();
    }

    private static void fail(String message) {
        System.err.println(message);
        System.exit(1);
    }
}