package com.github.sormuras.bach.core;

import com.github.sormuras.bach.Bach;
import com.github.sormuras.bach.ToolOperator;
import java.io.PrintWriter;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.util.Set;

public class List implements ToolOperator {
  @Override
  public int run(Bach bach, PrintWriter out, PrintWriter err, String... args) {
    var set = Set.of(args);
    var all = set.isEmpty();
    if (all || set.contains("system-modules")) {
      if (set.size() != 1) out.println("\n## bach list system-modules");
      var names = names(ModuleFinder.ofSystem());
      names.forEach(out::println);
      out.printf("  %d system module%s%n", names.size(), names.size() == 1 ? "" : "s");
    }
    if (all || set.contains("external-modules")) {
      if (set.size() != 1) out.println("\n## bach list external-modules");
      var path = bach.configuration().paths().externalModules();
      var names = names(ModuleFinder.of(path));
      names.forEach(out::println);
      var size = names.size();
      out.printf("  %d external module%s in %s%n", size, size == 1 ? "" : "s", path);
    }
    if (all || set.contains("project-modules")) {
      if (set.size() != 1) out.println("\n## bach list project-modules");
      for (var space : bach.project().spaces().list()) {
        var names = space.modules().names();
        if (names.isEmpty()) continue;
        names.forEach(out::println);
        var size = names.size();
        out.printf(
            "  %d module%s in project's %s space%n", size, size == 1 ? "" : "s", space.name());
      }
    }
    return 0;
  }

  private static java.util.List<String> names(ModuleFinder finder) {
    return finder.findAll().stream()
        .map(ModuleReference::descriptor)
        .map(ModuleDescriptor::toNameAndVersion)
        .sorted()
        .toList();
  }
}

@@ -113,24 +113,192 @@
 olOperator;%0A
+import com.github.sormuras.bach.internal.ModulesSupport;%0Aimport com.github.sormuras.bach.project.DeclaredModules;%0Aimport com.github.sormuras.bach.project.ProjectSpace;%0A
 import java.
@@ -430,24 +430,52 @@
 eReference;%0A
+import java.util.ArrayList;%0A
 import java.
@@ -1837,24 +1837,746 @@
     %7D%0A    %7D%0A
+    if (all %7C%7C set.contains(%22missing-modules%22)) %7B%0A      var finders = new ArrayList%3CModuleFinder%3E();%0A      finders.add(ModuleFinder.of(bach.configuration().paths().externalModules()));%0A      finders.addAll(%0A          bach.project().spaces().list().stream()%0A              .map(ProjectSpace::modules)%0A              .map(DeclaredModules::toModuleFinder)%0A              .toList());%0A      var modules = ModulesSupport.listMissingModules(finders, Set.of());%0A      if (!modules.isEmpty()) %7B%0A        if (set.size() != 1) out.println(%22%5Cn## bach list missing-modules%22);%0A        modules.forEach(out::println);%0A        var size = modules.size();%0A        out.printf(%22  %25d missing module%25s%25n%22, size, size == 1 ? %22%22 : %22s%22);%0A      %7D%0A    %7D%0A
     return 0

Support `missing-modules` argument in `list` tool