package click.kamil.file_combine;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.nio.file.Files;
import java.nio.file.Path;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class JavaConcatenatorTest {

    @TempDir
    Path tempDir;

    @Test
    void testCombineSimpleFiles() throws Exception {
        Path pkg1 = tempDir.resolve("pkg1");
        Files.createDirectories(pkg1);
        Files.writeString(pkg1.resolve("A.java"),
                "package pkg1;\n" +
                        "public class A {\n" +
                        "    public void hello() { System.out.println(\"Hello from A\"); }\n" +
                        "}");

        Path pkg2 = tempDir.resolve("pkg2");
        Files.createDirectories(pkg2);
        Files.writeString(pkg2.resolve("B.java"),
                "package pkg2;\n" +
                        "import pkg1.A;\n" +
                        "public class B {\n" +
                        "    public void run() { new A().hello(); }\n" +
                        "}");

        Path outputFile = tempDir.resolve("Combined.java");

        CommandLineRunner runner = new CommandLineRunner();
        int exitCode = runner.run(tempDir.toString(), "-o", outputFile.toString(), "-p", "combined");

        assertEquals(0, exitCode);
        assertTrue(Files.exists(outputFile));

        String content = Files.readString(outputFile);
        assertTrue(content.contains("package combined;"));
        assertTrue(content.contains("class A"));
        assertTrue(content.contains("class B"));
        assertFalse(content.contains("import pkg1.A;"));

        // Verify compilation
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int compilationResult = compiler.run(null, null, null, outputFile.toString());
        assertEquals(0, compilationResult, "Combined file should compile successfully");
    }

    @Test
    void testCollisionSameClassName() throws Exception {
        Path pkg1 = tempDir.resolve("pkg1");
        Files.createDirectories(pkg1);
        Files.writeString(pkg1.resolve("Utils.java"),
                "package pkg1;\n" +
                        "public class Utils {\n" +
                        "    public static void log() { System.out.println(\"pkg1\"); }\n" +
                        "}");

        Path pkg2 = tempDir.resolve("pkg2");
        Files.createDirectories(pkg2);
        Files.writeString(pkg2.resolve("Utils.java"),
                "package pkg2;\n" +
                        "public class Utils {\n" +
                        "    public static void log() { System.out.println(\"pkg2\"); }\n" +
                        "}");

        Path outputFile = tempDir.resolve("CombinedCollision.java");

        CommandLineRunner runner = new CommandLineRunner();
        int exitCode = runner.run(tempDir.toString(), "-o", outputFile.toString(), "-p", "combined.collision");

        assertEquals(0, exitCode);

        // This should produce a file with two 'class Utils' which is invalid Java
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int compilationResult = compiler.run(null, null, null, outputFile.toString());
        assertNotEquals(0, compilationResult, "Combined file with name collision should NOT compile successfully");
    }

    @Test
    void testStaticImportCollision() throws Exception {
        Path pkg1 = tempDir.resolve("pkg1");
        Files.createDirectories(pkg1);
        Files.writeString(pkg1.resolve("Utils1.java"),
                "package pkg1;\n" +
                        "public class Utils1 {\n" +
                        "    public static void log(String s) { System.out.println(\"1: \" + s); }\n" +
                        "}");

        Path pkg2 = tempDir.resolve("pkg2");
        Files.createDirectories(pkg2);
        Files.writeString(pkg2.resolve("Utils2.java"),
                "package pkg2;\n" +
                        "public class Utils2 {\n" +
                        "    public static void log(String s) { System.out.println(\"2: \" + s); }\n" +
                        "}");

        Path pkg3 = tempDir.resolve("pkg3");
        Files.createDirectories(pkg3);
        Files.writeString(pkg3.resolve("App.java"),
                "package pkg3;\n" +
                        "import pkg1.Utils1;\n" +
                        "public class App {\n" +
                        "    public void run() { Utils1.log(\"hello\"); }\n" +
                        "}");

        Path outputFile = tempDir.resolve("CombinedStatic.java");

        CommandLineRunner runner = new CommandLineRunner();
        int exitCode = runner.run(tempDir.toString(), "-o", outputFile.toString(), "-p", "combined.static_test");

        assertEquals(0, exitCode);

        String content = Files.readString(outputFile);
        // Both static imports should be present if they are not from internal packages
        // Actually, Utils1 and Utils2 ARE from internal packages in this test setup
        // So the concatenator should remove 'import static pkg1.Utils1.log'
        assertFalse(content.contains("import static pkg1.Utils1.log;"));

        // Verify compilation
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int compilationResult = compiler.run(null, null, null, outputFile.toString());
        assertEquals(0, compilationResult, "Combined file with static imports should compile successfully if imports are resolved internally");
    }

    @Test
    void testNestedClasses() throws Exception {
        Path pkg1 = tempDir.resolve("pkg1");
        Files.createDirectories(pkg1);
        Files.writeString(pkg1.resolve("Outer.java"),
                "package pkg1;\n" +
                        "public class Outer {\n" +
                        "    public static class Inner {\n" +
                        "        public void doIt() {}\n" +
                        "    }\n" +
                        "}");

        Path pkg2 = tempDir.resolve("pkg2");
        Files.createDirectories(pkg2);
        Files.writeString(pkg2.resolve("Client.java"),
                "package pkg2;\n" +
                        "import pkg1.Outer;\n" +
                        "public class Client {\n" +
                        "    public void use() { new Outer.Inner().doIt(); }\n" +
                        "}");

        Path outputFile = tempDir.resolve("CombinedNested.java");

        CommandLineRunner runner = new CommandLineRunner();
        int exitCode = runner.run(tempDir.toString(), "-o", outputFile.toString(), "-p", "combined.nested");

        assertEquals(0, exitCode);

        // Verify compilation
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int compilationResult = compiler.run(null, null, null, outputFile.toString());
        assertEquals(0, compilationResult, "Combined file with nested classes should compile successfully");
    }

    @Test
    void testImportCollision() throws Exception {
        Path pkg1 = tempDir.resolve("pkg1");
        Files.createDirectories(pkg1);
        Files.writeString(pkg1.resolve("A.java"),
                "package pkg1;\n" +
                        "import java.util.List;\n" +
                        "public class A {\n" +
                        "    public List<String> list;\n" +
                        "}");

        Path pkg2 = tempDir.resolve("pkg2");
        Files.createDirectories(pkg2);
        Files.writeString(pkg2.resolve("B.java"),
                "package pkg2;\n" +
                        "import java.awt.List;\n" +
                        "public class B {\n" +
                        "    public List awtList;\n" +
                        "}");

        Path outputFile = tempDir.resolve("CombinedImportCollision.java");

        CommandLineRunner runner = new CommandLineRunner();
        int exitCode = runner.run(tempDir.toString(), "-o", outputFile.toString(), "-p", "combined.import_collision");

        assertEquals(0, exitCode);

        // This should fail compilation because 'List' is ambiguous
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int compilationResult = compiler.run(null, null, null, outputFile.toString());
        assertNotEquals(0, compilationResult, "Combined file with ambiguous imports should NOT compile successfully");
    }

    @Test
    void testRecordsAndEnums() throws Exception {
        Path pkg1 = tempDir.resolve("pkg1");
        Files.createDirectories(pkg1);
        Files.writeString(pkg1.resolve("Data.java"),
                "package pkg1;\n" +
                        "public record Data(String name, int value) {}\n");

        Files.writeString(pkg1.resolve("Type.java"),
                "package pkg1;\n" +
                        "public enum Type {\n" +
                        "    A, B, C\n" +
                        "}\n");

        Path outputFile = tempDir.resolve("CombinedModern.java");

        CommandLineRunner runner = new CommandLineRunner();
        int exitCode = runner.run(tempDir.toString(), "-o", outputFile.toString(), "-p", "combined.modern");

        assertEquals(0, exitCode);

        String content = Files.readString(outputFile);
        assertTrue(content.contains("record Data"));
        assertTrue(content.contains("enum Type"));
        assertFalse(content.contains("public record Data"));
        assertFalse(content.contains("public enum Type"));

        // Verify compilation
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int compilationResult = compiler.run(null, null, null, outputFile.toString());
        assertEquals(0, compilationResult, "Combined file with records and enums should compile successfully");
    }

    @Test
    void testMetadataPreservation() throws Exception {
        Path pkg1 = tempDir.resolve("pkg1");
        Files.createDirectories(pkg1);
        Files.writeString(pkg1.resolve("Annotated.java"),
                "package pkg1;\n" +
                        "/**\n" +
                        " * Javadoc comment\n" +
                        " */\n" +
                        "@Deprecated\n" +
                        "public class Annotated {\n" +
                        "    // Line comment\n" +
                        "    /* Block comment */\n" +
                        "    @SuppressWarnings(\"unchecked\")\n" +
                        "    public void method() {}\n" +
                        "}");

        Path outputFile = tempDir.resolve("CombinedMetadata.java");

        CommandLineRunner runner = new CommandLineRunner();
        int exitCode = runner.run(tempDir.toString(), "-o", outputFile.toString(), "-p", "combined.metadata");

        assertEquals(0, exitCode);

        String content = Files.readString(outputFile);
        assertTrue(content.contains("Javadoc comment"));
        assertTrue(content.contains("@Deprecated"));
        assertTrue(content.contains("// Line comment"));
        assertTrue(content.contains("/* Block comment */"));
        assertTrue(content.contains("@SuppressWarnings(\"unchecked\")"));

        // Verify compilation
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int compilationResult = compiler.run(null, null, null, outputFile.toString());
        assertEquals(0, compilationResult, "Combined file with comments and annotations should compile successfully");
    }

    private static class CommandLineRunner {
        int run(String... args) {
            return new picocli.CommandLine(new JavaConcatenator()).execute(args);
        }
    }
}
