package click.kamil.springstatemachineexporter.analysis.service;

import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.eclipse.jdt.core.dom.CompilationUnit;
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 GenericEventDetectorTest {

    @Test
    void testMessageBuilderVariableAssignment(@TempDir Path tempDir) throws IOException {
        Path dir = tempDir.resolve("com/example");
        Files.createDirectories(dir);
        Files.writeString(dir.resolve("MyService.java"),
            "package com.example;\n" +
            "import org.springframework.messaging.support.MessageBuilder;\n" +
            "import org.springframework.messaging.Message;\n" +
            "import org.springframework.statemachine.StateMachine;\n" +
            "public class MyService {\n" +
            "    private StateMachine<String, String> stateMachine;\n" +
            "    private void a(MyInterface myEvent) {\n" +
            "        Message<MyInterface> msg = MessageBuilder.withPayload(myEvent).setHeader(\"k\", \"v\").build();\n" +
            "        stateMachine.sendEvent(Mono.just(msg));\n" +
            "    }\n" +
            "}\n");

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

        GenericEventDetector detector = new GenericEventDetector(context, new ConstantResolver(), List.of());
        CompilationUnit cu = context.getCompilationUnits().iterator().next();
        List<TriggerPoint> triggers = detector.detect(cu);

        assertThat(triggers).hasSize(1);
        assertThat(triggers.get(0).getEvent()).isEqualTo("myEvent");
        assertThat(triggers.get(0).getMethodName()).isEqualTo("a");
    }

    @Test
    void testMessageBuilderIntermediateVariable(@TempDir Path tempDir) throws IOException {
        Path dir = tempDir.resolve("com/example");
        Files.createDirectories(dir);
        Files.writeString(dir.resolve("MyService.java"),
            "package com.example;\n" +
            "import org.springframework.messaging.support.MessageBuilder;\n" +
            "import org.springframework.messaging.Message;\n" +
            "import org.springframework.statemachine.StateMachine;\n" +
            "public class MyService {\n" +
            "    private StateMachine<String, String> stateMachine;\n" +
            "    private void a(MyInterface myEvent) {\n" +
            "        MessageBuilder<MyInterface> messageBuilder = MessageBuilder.withPayload(myEvent).setHeader(\"k\", \"v\");\n" +
            "        stateMachine.sendEvent(messageBuilder.build());\n" +
            "    }\n" +
            "}\n");

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

        GenericEventDetector detector = new GenericEventDetector(context, new ConstantResolver(), List.of());
        CompilationUnit cu = context.getCompilationUnits().iterator().next();
        List<TriggerPoint> triggers = detector.detect(cu);

        assertThat(triggers).hasSize(1);
        assertThat(triggers.get(0).getEvent()).isEqualTo("myEvent");
        assertThat(triggers.get(0).getMethodName()).isEqualTo("a");
    }

    @Test
    void testEnumGetterUnionExtraction(@TempDir Path tempDir) throws IOException {
        Path dir = tempDir.resolve("com/example");
        Files.createDirectories(dir);
        Files.writeString(dir.resolve("MyEnum.java"),
            "package com.example;\n" +
            "public enum MyEnum {\n" +
            "    STATE_A, STATE_B;\n" +
            "}\n");
            
        Files.writeString(dir.resolve("MyService.java"),
            "package com.example;\n" +
            "import org.springframework.statemachine.StateMachine;\n" +
            "public class MyService {\n" +
            "    private StateMachine<String, String> stateMachine;\n" +
            "    public MyEnum getEvent() { return someExternalCall(); }\n" +
            "    public MyEnum someExternalCall() { return null; }\n" +
            "    public void trigger() {\n" +
            "        stateMachine.sendEvent(this.getEvent());\n" +
            "    }\n" +
            "}\n");

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

        GenericEventDetector detector = new GenericEventDetector(context, new ConstantResolver(), List.of());
        
        CompilationUnit serviceCu = context.getCompilationUnits().stream()
                .filter(cu -> cu.getJavaElement() == null || cu.getJavaElement().getElementName().contains("MyService"))
                .findFirst().orElseThrow();
                
        List<TriggerPoint> triggers = detector.detect(serviceCu);

        System.out.println("TRIGGERS: " + triggers);
        assertThat(triggers).hasSize(2);
        assertThat(triggers).anyMatch(t -> t.getEvent().equals("com.example.MyEnum.STATE_A"));
        assertThat(triggers).anyMatch(t -> t.getEvent().equals("com.example.MyEnum.STATE_B"));
    }
}
