package click.kamil.springstatemachineexporter;

import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

public class ResolutionRobustnessTest {

    @Test
    void testStackOverflowPreventionWithCyclicInheritance(@TempDir Path tempDir) throws IOException {
        Path fooDir = tempDir.resolve("com/foo");
        Files.createDirectories(fooDir);
        // Cyclic inheritance: Config extends Config
        Files.writeString(fooDir.resolve("Config.java"), 
            "package com.foo;\n" +
            "public class Config extends Config {\n" +
            "    public void configure() {}\n" +
            "}");

        CodebaseContext context = new CodebaseContext();
        context.scan(tempDir);

        TypeDeclaration td = context.getTypeDeclaration("com.foo.Config");
        assertThat(td).isNotNull();

        // This should not throw StackOverflowError
        boolean result = context.extendsStateMachineConfigurerAdapter(td);
        assertThat(result).isFalse();
    }

    @Test
    void testShadowingResolution(@TempDir Path tempDir) throws IOException {
        // Package com.foo has a class Config
        Path fooDir = tempDir.resolve("com/foo");
        Files.createDirectories(fooDir);
        Files.writeString(fooDir.resolve("Config.java"), "package com.foo; public class Config {}");

        // Package com.bar also has a class Config
        Path barDir = tempDir.resolve("com/bar");
        Files.createDirectories(barDir);
        Files.writeString(barDir.resolve("Config.java"), "package com.bar; public class Config {}");

        // App class imports com.bar.Config
        Path appDir = tempDir.resolve("com/app");
        Files.createDirectories(appDir);
        String appSource = "package com.app;\n" +
                           "import com.bar.Config;\n" +
                           "public class App extends Config {}";
        Path appFile = appDir.resolve("App.java");
        Files.writeString(appFile, appSource);

        CodebaseContext context = new CodebaseContext();
        context.scan(tempDir);

        TypeDeclaration appTd = context.getTypeDeclaration("com.app.App");
        assertThat(appTd).isNotNull();

        // Resolution in App.java context should pick com.bar.Config due to import
        TypeDeclaration resolvedTd = context.getTypeDeclaration("Config", (CompilationUnit) appTd.getRoot());
        assertThat(context.getFqn(resolvedTd)).isEqualTo("com.bar.Config");
    }

    @Test
    void testAmbiguityHandling(@TempDir Path tempDir) throws IOException {
        // Two classes with same name in different packages, no imports in context
        Files.createDirectories(tempDir.resolve("p1"));
        Files.writeString(tempDir.resolve("p1/X.java"), "package p1; public class X {}");

        Files.createDirectories(tempDir.resolve("p2"));
        Files.writeString(tempDir.resolve("p2/X.java"), "package p2; public class X {}");

        CodebaseContext context = new CodebaseContext();
        context.scan(tempDir);

        // Ambiguous simple name should return null to prevent incorrect resolution
        TypeDeclaration td = context.getTypeDeclaration("X");
        assertThat(td).isNull();

        // Exact FQN should still work
        assertThat(context.getTypeDeclaration("p1.X")).isNotNull();
        assertThat(context.getTypeDeclaration("p2.X")).isNotNull();
    }

    @Test
    void testJdtBindingsResolution(@TempDir Path tempDir) throws IOException {
        Path comFoo = tempDir.resolve("com/foo");
        Files.createDirectories(comFoo);
        Files.writeString(comFoo.resolve("Base.java"),
            "package com.foo;\n" +
            "public class Base {\n" +
            "    public void run() {}\n" +
            "}"
        );
        Files.writeString(comFoo.resolve("Child.java"),
            "package com.foo;\n" +
            "public class Child extends Base {\n" +
            "    public void execute() {\n" +
            "        run();\n" +
            "    }\n" +
            "}"
        );

        CodebaseContext context = new CodebaseContext();
        context.setResolveBindings(true);
        context.setSourcepath(List.of(tempDir.toAbsolutePath().toString()));
        context.scan(tempDir);

        TypeDeclaration childTd = context.getTypeDeclaration("com.foo.Child");
        assertThat(childTd).isNotNull();

        // Resolve the binding for Child class
        org.eclipse.jdt.core.dom.ITypeBinding binding = childTd.resolveBinding();
        assertThat(binding).isNotNull();
        assertThat(binding.getQualifiedName()).isEqualTo("com.foo.Child");
        
        // Verify we can get the superclass binding
        org.eclipse.jdt.core.dom.ITypeBinding superBinding = binding.getSuperclass();
        assertThat(superBinding).isNotNull();
        assertThat(superBinding.getQualifiedName()).isEqualTo("com.foo.Base");
    }
}
