package click.kamil.springstatemachineexporter.analysis.service;

import click.kamil.springstatemachineexporter.analysis.model.CallChain;
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.junit.jupiter.api.Test;

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 RunnableLambdaDispatchTest {

    @Test
    void shouldResolveEventThroughRunnableExecuteLambda() throws IOException {
        String controllerSource = """
                package com.example.web;

                import com.example.service.AbstractOrderService;
                import com.example.executor.TaskExecutor;

                public class OrderController {
                    private final TaskExecutor executor;
                    private final AbstractOrderService orderService;

                    public OrderController(TaskExecutor executor, AbstractOrderService orderService) {
                        this.executor = executor;
                        this.orderService = orderService;
                    }

                    public void process() {
                        executor.execute(() -> orderService.fireEvent());
                    }
                }
                """;

        String executorSource = """
                package com.example.executor;

                public class TaskExecutor {
                    public void execute(Runnable task) {
                        task.run();
                    }
                }
                """;

        String serviceSource = """
                package com.example.service;

                import com.example.domain.OrderCommand;

                public abstract class AbstractOrderService {
                    protected StateMachine machine;

                    public void fireEvent() {
                        sendEvent(OrderCommand.PAY);
                    }

                    protected void sendEvent(OrderCommand event) {
                        machine.fire(event);
                    }
                }

                class StateMachine {
                    public void fire(OrderCommand event) {}
                }
                """;

        String enumSource = """
                package com.example.domain;

                public enum OrderCommand { PAY, SHIP, META }
                """;

        Path tempDir = Files.createTempDirectory("callgraph_runnable_lambda");
        Files.writeString(tempDir.resolve("OrderController.java"), controllerSource);
        Files.writeString(tempDir.resolve("TaskExecutor.java"), executorSource);
        Files.writeString(tempDir.resolve("AbstractOrderService.java"), serviceSource);
        Files.writeString(tempDir.resolve("OrderCommand.java"), enumSource);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.web.OrderController")
                .methodName("process")
                .build();

        TriggerPoint trigger = TriggerPoint.builder()
                .className("com.example.service.AbstractOrderService")
                .methodName("sendEvent")
                .event("event")
                .build();

        List<CallChain> chains = engine.findChains(List.of(entryPoint), List.of(trigger));
        assertThat(chains).hasSize(1);
        assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents())
                .containsExactly("OrderCommand.PAY");
    }
}
