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

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

                import com.example.service.StateMachineService;
                import com.example.domain.OrderEvent;
                import org.springframework.stereotype.Component;

                @Component
                public class JmsOrderListener {
                    private final StateMachineService stateMachineService;

                    public JmsOrderListener(StateMachineService stateMachineService) {
                        this.stateMachineService = stateMachineService;
                    }

                    public void receiveCancelCommand(String message) {
                        stateMachineService.sendMessageWithProvider(() -> OrderEvent.CANCEL);
                    }
                }
                """;

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

                import com.example.domain.OrderEvent;
                import org.springframework.statemachine.StateMachine;
                import org.springframework.messaging.support.MessageBuilder;
                import reactor.core.publisher.Mono;
                import java.util.function.Supplier;

                public class StateMachineServiceImpl implements StateMachineService {
                    private StateMachine<OrderEvent, OrderEvent> stateMachine;

                    @Override
                    public <T extends OrderEvent> void sendMessageWithProvider(Supplier<T> eventProvider) {
                        T event = eventProvider.get();
                        if (event != null) {
                            stateMachine.sendEvent(Mono.just(MessageBuilder.withPayload(event).build())).subscribe();
                        }
                    }
                }
                """;

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

                import com.example.domain.OrderEvent;
                import java.util.function.Supplier;

                public interface StateMachineService {
                    <T extends OrderEvent> void sendMessageWithProvider(Supplier<T> eventProvider);
                }
                """;

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

                public enum OrderEvent { CANCEL, PROCESS }
                """;

        Path tempDir = Files.createTempDirectory("callgraph_cross_class_lambda");
        Files.writeString(tempDir.resolve("JmsOrderListener.java"), webSource);
        Files.writeString(tempDir.resolve("StateMachineServiceImpl.java"), serviceSource);
        Files.writeString(tempDir.resolve("StateMachineService.java"), ifaceSource);
        Files.writeString(tempDir.resolve("OrderEvent.java"), domainSource);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.web.JmsOrderListener")
                .methodName("receiveCancelCommand")
                .build();

        TriggerPoint trigger = TriggerPoint.builder()
                .className("com.example.service.StateMachineServiceImpl")
                .methodName("sendMessageWithProvider")
                .event("eventProvider")
                .build();

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