import java.io.*;
import java.nio.file.*;

/**
 * Generates mega-classes with thousands of fields to simulate real-world
 * scenarios where classes have 37,000+ fields.
 *
 * This stresses the AOT archiver's metadata handling much more than
 * many simple classes.
 */
public class MegaClassGenerator {

    // Configuration
    private static final int MEGA_CLASS_COUNT = 2_000;      // Number of mega-classes
    private static final int FIELDS_PER_CLASS = 5_000;      // Fields per mega-class (Java constant pool limit)
    private static final String OUTPUT_DIR = "generated-classes/src";
    private static final String PACKAGE_NAME = "com.stress.generated";

    public static void main(String[] args) throws IOException {
        System.out.println("=== Mega Class Generator ===");
        System.out.println("Configuration:");
        System.out.println("  Mega-classes: " + MEGA_CLASS_COUNT);
        System.out.println("  Fields per class: " + FIELDS_PER_CLASS);
        System.out.println("  Total fields: " + (MEGA_CLASS_COUNT * FIELDS_PER_CLASS));
        System.out.println();
        System.out.println("This simulates your real application's mega-classes!");
        System.out.println();

        // Create output directory
        Path outputPath = Paths.get(OUTPUT_DIR, PACKAGE_NAME.replace('.', '/'));
        Files.createDirectories(outputPath);

        long startTime = System.currentTimeMillis();

        // Generate mega-classes
        for (int i = 0; i < MEGA_CLASS_COUNT; i++) {
            generateMegaClass(outputPath, i);

            if ((i + 1) % 10 == 0) {
                System.out.printf("Generated %d / %d mega-classes%n", i + 1, MEGA_CLASS_COUNT);
            }
        }

        // Also generate all the FieldObject classes
        System.out.println("\nGenerating FieldObject classes...");
        generateFieldObjectClasses(outputPath);

        long elapsed = System.currentTimeMillis() - startTime;
        System.out.println();
        System.out.println("=== Generation Complete ===");
        System.out.println("Mega-classes: " + MEGA_CLASS_COUNT);
        System.out.println("Fields per mega-class: " + FIELDS_PER_CLASS);
        System.out.println("FieldObject classes: " + FIELDS_PER_CLASS);
        System.out.println("Total classes: " + (MEGA_CLASS_COUNT + FIELDS_PER_CLASS));
        System.out.println("Time taken: " + elapsed + "ms");
        System.out.println();
        System.out.println("Estimated metadata objects:");
        System.out.println("  Per mega-class: ~" + (FIELDS_PER_CLASS * 4) + " (fields + descriptors)");
        System.out.println("  Total: ~" + (MEGA_CLASS_COUNT * FIELDS_PER_CLASS * 4) + " objects");
        System.out.println();
        System.out.println("This should DEFINITELY trigger OOM with these massive classes!");
        System.out.println();
        System.out.println("Next: Compile with compile-mega-classes.sh");
    }

    private static void generateMegaClass(Path outputPath, int classNumber) throws IOException {
        String className = "MegaDataClass" + classNumber;
        Path classFile = outputPath.resolve(className + ".java");

        StringBuilder code = new StringBuilder();
        code.append("package ").append(PACKAGE_NAME).append(";\n\n");
        code.append("/**\n");
        code.append(" * Mega-class #").append(classNumber).append(" with ").append(FIELDS_PER_CLASS).append(" fields\n");
        code.append(" * This simulates real-world mega-classes that cause AOT OOM issues.\n");
        code.append(" */\n");
        code.append("public class ").append(className).append(" {\n\n");

        // Generate massive number of fields
        for (int i = 0; i < FIELDS_PER_CLASS; i++) {
            // Use object types to increase metadata (not primitives)
            code.append("    private FieldObject").append(i).append(" field").append(i).append(";\n");

            // Add line breaks every 100 fields for readability
            if ((i + 1) % 100 == 0 && i < FIELDS_PER_CLASS - 1) {
                code.append("\n");
            }
        }
        code.append("\n");

        // Constructor that initializes all fields
        code.append("    public ").append(className).append("() {\n");
        code.append("        // Initialize some fields (not all to keep compilation fast)\n");
        for (int i = 0; i < Math.min(100, FIELDS_PER_CLASS); i++) {
            code.append("        this.field").append(i).append(" = new FieldObject").append(i).append("();\n");
        }
        code.append("    }\n\n");

        // Generate getters (not all, to keep file size manageable)
        code.append("    // Getters for first 100 fields\n");
        for (int i = 0; i < Math.min(100, FIELDS_PER_CLASS); i++) {
            code.append("    public FieldObject").append(i).append(" getField").append(i).append("() {\n");
            code.append("        return field").append(i).append(";\n");
            code.append("    }\n\n");
        }

        code.append("    public int getFieldCount() {\n");
        code.append("        return ").append(FIELDS_PER_CLASS).append(";\n");
        code.append("    }\n");

        code.append("}\n");

        Files.writeString(classFile, code.toString());
    }

    private static void generateFieldObjectClasses(Path outputPath) throws IOException {
        // Generate individual FieldObject classes
        // These are the types used by the mega-class fields

        for (int i = 0; i < FIELDS_PER_CLASS; i++) {
            Path classFile = outputPath.resolve("FieldObject" + i + ".java");

            StringBuilder code = new StringBuilder();
            code.append("package ").append(PACKAGE_NAME).append(";\n\n");
            code.append("/**\n");
            code.append(" * Field object #").append(i).append("\n");
            code.append(" */\n");
            code.append("public class FieldObject").append(i).append(" {\n");
            code.append("    private int value = ").append(i).append(";\n");
            code.append("    private String name = \"field").append(i).append("\";\n\n");

            code.append("    public int getValue() { return value; }\n");
            code.append("    public String getName() { return name; }\n");
            code.append("}\n");

            Files.writeString(classFile, code.toString());

            if ((i + 1) % 5000 == 0) {
                System.out.printf("  Generated %d / %d FieldObject classes%n", i + 1, FIELDS_PER_CLASS);
            }
        }
    }
}
