package io.quarkus.test.junit.nativeimage;

import java.io.File;
import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;

import org.junit.jupiter.api.Assertions;

/**
 * This is a general utility to assert via
 * unit testing which classes have been included in a native-image.
 */
public final class ClassInclusionReport {

    private final Set<String> includedClasses;

    private ClassInclusionReport(Set<String> strings, Path usedClassesReport) {
        includedClasses = strings;
    }

    /**
     * This will load the class inclusions report assuming the native-image
     * was built in the current module and following Maven conventions:
     * by walking into the '/target' directory from the current directory.
     *
     * @return An assertable report of all classes included in the current app
     */
    public static ClassInclusionReport load() {
        final Path usedClassesReport = getUsedClassesReport();
        TreeSet<String> set = new TreeSet<>();
        try (Scanner scanner = new Scanner(usedClassesReport.toFile())) {
            while (scanner.hasNextLine()) {
                set.add(scanner.nextLine());
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException("Could not load used classes report", e);
        }
        return new ClassInclusionReport(set, usedClassesReport);
    }

    public void assertContains(final Class<?> type) {
        assertContains(type.getName());
    }

    public void assertContainsNot(final Class<?> type) {
        assertContainsNot(type.getName());
    }

    public void assertContains(final String typeName) {
        final boolean contains = includedClasses.contains(typeName);
        if (!contains) {
            Assertions.fail(
                    "Type '" + typeName + "' was not found in the report in " + getUsedClassesReport()
                            .toString());
        }
    }

    public void assertContainsNot(final String typeName) {
        final boolean contains = includedClasses.contains(typeName);
        if (contains) {
            Assertions.fail("Type '" + typeName + "' was found in the report in " + getUsedClassesReport()
                    .toString());
        }
    }

    private static Path getUsedClassesReport() {
        final Path reportsPath = nativeImageReportsPath();
        final File[] usedClassesReports = reportsPath.toFile().listFiles((dir, name) -> name.toLowerCase(Locale.ROOT)
                .startsWith("used_classes_"));
        Assertions.assertEquals(1, usedClassesReports.length, "Could not identify the native image build directory");
        final Path usedClassesReport = usedClassesReports[0].toPath();
        return usedClassesReport;
    }

    private static Path nativeImageReportsPath() {
        final Path nativeBuildPath = locateNativeImageBuildDirectory();
        final Path reportsPath = nativeBuildPath.resolve("reports");
        Assertions.assertTrue(reportsPath.toFile().exists(),
                "The reports directory doesn't exist?! Make sure this build was invoked with 'quarkus.native.enable-reports=true'");
        return reportsPath;
    }

    private static Path locateNativeImageBuildDirectory() {
        Path buildPath = Paths.get("target");
        final File[] files = buildPath.toFile().listFiles((dir, name) -> name.toLowerCase(Locale.ROOT)
                .endsWith("-native-image-source-jar"));
        Assertions.assertEquals(1, files.length, "Could not identify the native image build directory");
        return files[0].toPath();
    }
}

@@ -501,16 +501,58 @@
 Classes;
+%0A    private final Path usedClassesReport;
 %0A%0A    pr
@@ -590,22 +590,30 @@
 String%3E 
-string
+includedClasse
 s, Path 
@@ -641,16 +641,21 @@
         
+this.
 included
@@ -668,15 +668,75 @@
 s = 
-strings
+includedClasses;%0A        this.usedClassesReport = usedClassesReport
 ;%0A  
@@ -2066,36 +2066,33 @@
 e report in %22 + 
-getU
+u
 sedClassesReport
@@ -2095,50 +2095,8 @@
 port
-()%0A                            .toString()
 );%0A 
@@ -2347,20 +2347,17 @@
  in %22 + 
-getU
+u
 sedClass
@@ -2368,42 +2368,8 @@
 port
-()%0A                    .toString()
 );%0A 
@@ -2653,24 +2653,133 @@
 lasses_%22));%0A
+        Assertions.assertNotNull(usedClassesReports, %22Could not identify the native image build directory%22);%0A
         Asse
@@ -2888,79 +2888,8 @@
 %22);%0A
-        final Path usedClassesReport = usedClassesReports%5B0%5D.toPath();%0A
     
@@ -2908,32 +2908,45 @@
 sedClassesReport
+s%5B0%5D.toPath()
 ;%0A    %7D%0A%0A    pri
@@ -3620,24 +3620,120 @@
 rce-jar%22));%0A
+        Assertions.assertNotNull(files, %22Could not identify the native image build directory%22);%0A
         Asse

Apply minor polish to ClassInclusionReport