package com.github.sormuras.bach.api;

import com.github.sormuras.bach.Bach;
import com.github.sormuras.bach.Command;
import com.github.sormuras.bach.Options;
import com.github.sormuras.bach.internal.Strings;
import com.github.sormuras.bach.project.ModuleDeclaration;
import com.github.sormuras.bach.tool.Jar;
import com.github.sormuras.bach.tool.Javac;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;

/** Methods related to building projects. */
public interface ProjectBuilderAPI {

  Bach bach();

  default void buildProject() throws Exception {
    var bach = bach();
    var project = bach.project();
    bach.print("Build %s %s", project.name(), project.version());
    if (bach.is(Options.Flag.VERBOSE)) bach.info();
    var start = Instant.now();
    if (bach.is(Options.Flag.STRICT)) bach.formatJavaSourceFiles(JavaFormatterAPI.Mode.VERIFY);
    bach.loadMissingExternalModules();
    bach.verifyExternalModules();
    buildProjectMainSpace();
    buildProjectTestSpace();
    bach.print("Build took %s", Strings.toString(Duration.between(start, Instant.now())));
  }

  default void buildProjectMainSpace() throws Exception {
    var main = bach().project().spaces().main();
    var modules = main.declarations();
    if (modules.isEmpty()) {
      bach().debug("Main module list is empty, nothing to build here.");
      return;
    }
    bach().print("Build %d main module%s", modules.size(), modules.size() == 1 ? "" : "s");

    var release = main.release();
    var feature = "" + (release == 0 ? Runtime.version().feature() : release);
    var classes = bach().folders().workspace("classes", main.name(), feature);

    bach().run(buildProjectMainJavac(release, classes)).requireSuccessful();

    Files.createDirectories(bach().folders().workspace("modules"));
    bach()
        .run(modules.map().values().stream().map(module -> buildProjectMainJar(module, classes)))
        .requireSuccessful();
  }

  /**
   * {@return the {@code javac} call to compile all modules of the main space}
   *
   * @param release the release
   */
  default Javac buildProjectMainJavac(int release, Path classes) {
    var project = bach().project();
    var main = project.spaces().main();
    return Command.javac()
        .ifTrue(release != 0, javac -> javac.add("--release", release))
        .add("--module", main.declarations().toNames(","))
        .add("--module-version", project.version())
        .forEach(
            main.declarations().toModuleSourcePaths(false),
            (javac, path) -> javac.add("--module-source-path", path))
        .ifPresent(main.modulePaths().pruned(), (javac, paths) -> javac.add("--module-path", paths))
        .ifTrue(bach().is(Options.Flag.STRICT), javac -> javac.add("-Xlint").add("-Werror"))
        .addAll(main.tweaks().arguments("javac"))
        .add("-d", classes);
  }

  default Jar buildProjectMainJar(ModuleDeclaration module, Path classes) {
    var project = bach().project();
    var main = project.spaces().main();
    var name = module.name();
    var file = bach().folders().workspace("modules", buildProjectJarFileName(name));
    var jar =
        Command.jar()
            .ifTrue(bach().is(Options.Flag.VERBOSE), command -> command.add("--verbose"))
            .add("--create")
            .add("--file", file)
            .addAll(main.tweaks().arguments("jar"))
            .addAll(main.tweaks().arguments("jar(" + name + ")"));
    var baseClasses = classes.resolve(name);
    if (Files.isDirectory(baseClasses)) jar = jar.add("-C", baseClasses, ".");
    return jar;
  }

  default String buildProjectJarFileName(String module) {
    return module + '@' + bach().project().versionNumberAndPreRelease() + ".jar";
  }

  default void buildProjectTestSpace() throws Exception {
    var test = bach().project().spaces().test();
    var modules = test.declarations();
    if (modules.isEmpty()) {
      bach().debug("Test module list is empty, nothing to build here.");
      return;
    }
    bach().print("Build %d test module%s", modules.size(), modules.size() == 1 ? "" : "s");

    throw new UnsupportedOperationException("buildProjectTestSpace() needs some love");
  }
}

@@ -347,16 +347,60 @@
 .Javac;%0A
+import com.github.sormuras.bach.util.Paths;%0A
 import j
@@ -1437,32 +1437,76 @@
   return;%0A    %7D%0A
+    var s = modules.size() == 1 ? %22%22 : %22s%22;%0A
     bach().print
@@ -1523,24 +1523,28 @@
  main module
+%25s: 
 %25s%22, modules
@@ -1543,32 +1543,35 @@
  modules.size(),
+ s,
  modules.size() 
@@ -1563,38 +1563,29 @@
 modules.
-size() == 1 ? %22%22 : %22s%22
+toNames(%22, %22)
 );%0A%0A    
@@ -1769,24 +1769,137 @@
  feature);%0A%0A
+    var workspaceModules = bach().folders().workspace(%22modules%22);%0A    Paths.deleteDirectories(workspaceModules);%0A
     bach().r
@@ -1992,33 +1992,16 @@
 ctories(
-bach().folders().
 workspac
@@ -2001,27 +2001,23 @@
 orkspace
-(%22m
+M
 odules
-%22)
 );%0A    b

Clean module directory on build