package click.kamil.springstatemachineexporter.ast.common;

import org.eclipse.jdt.core.dom.*;
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;

class BindingResolverTest {

    @Test
    void testBindingResolverMethods(@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 interface Base {\n" +
            "    void run();\n" +
            "}"
        );
        Files.writeString(comFoo.resolve("Child.java"),
            "package com.foo;\n" +
            "public class Child implements Base {\n" +
            "    public void run() {}\n" +
            "    public void execute() {\n" +
            "        Child c = new Child();\n" +
            "        c.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();

        ITypeBinding binding = BindingResolver.resolveType(childTd);
        assertThat(binding).isNotNull();
        assertThat(BindingResolver.getFullyQualifiedName(binding)).isEqualTo("com.foo.Child");
        assertThat(BindingResolver.isSubtypeOf(binding, "com.foo.Base")).isTrue();
        assertThat(BindingResolver.isSubtypeOf(binding, "java.lang.Object")).isTrue();
        assertThat(BindingResolver.isSubtypeOf(binding, "com.foo.NonExistent")).isFalse();

        // Find MethodDeclaration execute
        MethodDeclaration executeMethod = null;
        for (MethodDeclaration md : childTd.getMethods()) {
            if (md.getName().getIdentifier().equals("execute")) {
                executeMethod = md;
            }
        }
        assertThat(executeMethod).isNotNull();

        // Traverse the body to find ClassInstanceCreation and MethodInvocation
        class Visitor extends ASTVisitor {
            ClassInstanceCreation cic = null;
            MethodInvocation mi = null;

            @Override
            public boolean visit(ClassInstanceCreation node) {
                cic = node;
                return super.visit(node);
            }

            @Override
            public boolean visit(MethodInvocation node) {
                mi = node;
                return super.visit(node);
            }
        }

        Visitor visitor = new Visitor();
        executeMethod.accept(visitor);

        assertThat(visitor.cic).isNotNull();
        IMethodBinding constrBinding = BindingResolver.resolveConstructor(visitor.cic);
        assertThat(constrBinding).isNotNull();
        assertThat(constrBinding.isConstructor()).isTrue();

        assertThat(visitor.mi).isNotNull();
        IMethodBinding methodBinding = BindingResolver.resolveMethod(visitor.mi);
        assertThat(methodBinding).isNotNull();
        assertThat(methodBinding.getName()).isEqualTo("run");
    }
}
