package org.openautonomousconnection.webserver.runtime;

import javax.tools.*;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;

/**
 * Compiles and loads Java pages at runtime.
 *
 * <p>Supports packages by deriving the fully qualified class name from source.
 * <p>NOTE: Requires a JDK (ToolProvider.getSystemJavaCompiler != null).
 */
public final class JavaPageCompiler {

    private JavaPageCompiler() {}

    public static Class<?> compile(File javaFile) throws Exception {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null) throw new IllegalStateException("JDK required (JavaCompiler not available)");

        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> units = fileManager.getJavaFileObjects(javaFile);

        List<String> options = List.of("-classpath", System.getProperty("java.class.path"));

        boolean success = compiler.getTask(null, fileManager, null, options, null, units).call();
        fileManager.close();

        if (!success) throw new RuntimeException("Compilation failed: " + javaFile.getName());

        String fqcn = deriveFqcn(javaFile);

        // Root must be the folder containing the package root (javaFile parent is fine if classes output there).
        URLClassLoader cl = new URLClassLoader(new URL[]{ javaFile.getParentFile().toURI().toURL() });

        return cl.loadClass(fqcn);
    }

    private static String deriveFqcn(File javaFile) throws Exception {
        String src = Files.readString(javaFile.toPath(), StandardCharsets.UTF_8);

        String pkg = null;
        for (String line : src.split("\\R")) {
            String t = line.trim();
            if (t.startsWith("package ") && t.endsWith(";")) {
                pkg = t.substring("package ".length(), t.length() - 1).trim();
                break;
            }
            // Stop scanning early if class appears before package (invalid anyway)
            if (t.startsWith("public class") || t.startsWith("class ") || t.startsWith("public final class")) {
                break;
            }
        }

        String simple = javaFile.getName().replace(".java", "");
        if (pkg == null || pkg.isBlank()) return simple;
        return pkg + "." + simple;
    }
}
