package click.kamil.springstatemachineexporter.analysis.enricher.routing;

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

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

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

class HeuristicBeanResolutionEngineRoutingTest {

    private final HeuristicBeanResolutionEngine engine = new HeuristicBeanResolutionEngine();

    @Test
    void shouldRejectChainWhenEventTypesDifferEvenIfStateTypesMatch(@TempDir Path tempDir) throws Exception {
        writeTwoMachineSample(tempDir);
        CodebaseContext context = new CodebaseContext();
        context.scan(tempDir);

        CallChain chain = CallChain.builder()
                .triggerPoint(TriggerPoint.builder()
                        .event("InvoiceEvent.PAY")
                        .eventTypeFqn("com.example.invoice.InvoiceEvent")
                        .stateTypeFqn("com.example.shared.SharedState")
                        .build())
                .build();

        assertThat(engine.isRoutedToCorrectMachine(
                chain,
                "com.example.config.OrderStateMachineConfiguration",
                context)).isFalse();
    }

    @Test
    void sharedServiceWithProvenOrderTypesShouldOnlyRouteToOrderMachine(@TempDir Path tempDir) throws Exception {
        Files.writeString(tempDir.resolve("SharedDispatcher.java"), """
                package com.example;
                public class SharedDispatcher {
                    void payOrder() {
                        org.springframework.statemachine.StateMachine<OrderState, OrderEvent> machine = null;
                        machine.sendEvent(OrderEvent.PAY);
                    }
                    void submitDocument() {
                        org.springframework.statemachine.StateMachine<DocumentState, DocumentEvent> machine = null;
                        machine.sendEvent(DocumentEvent.SUBMIT);
                    }
                }
                enum OrderEvent { PAY }
                enum OrderState { NEW, PAID }
                enum DocumentEvent { SUBMIT }
                enum DocumentState { DRAFT }
                class OrderStateMachineConfiguration
                        extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<OrderState, OrderEvent> {}
                class DocumentStateMachineConfiguration
                        extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<DocumentState, DocumentEvent> {}
                """);

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

        CallChain orderChain = CallChain.builder()
                .triggerPoint(TriggerPoint.builder()
                        .event("com.example.OrderEvent.PAY")
                        .eventTypeFqn("com.example.OrderEvent")
                        .stateTypeFqn("com.example.OrderState")
                        .className("com.example.SharedDispatcher")
                        .methodName("payOrder")
                        .build())
                .methodChain(List.of("com.example.SharedDispatcher.payOrder()"))
                .build();
        CallChain documentChain = CallChain.builder()
                .triggerPoint(TriggerPoint.builder()
                        .event("com.example.DocumentEvent.SUBMIT")
                        .eventTypeFqn("com.example.DocumentEvent")
                        .stateTypeFqn("com.example.DocumentState")
                        .className("com.example.SharedDispatcher")
                        .methodName("submitDocument")
                        .build())
                .methodChain(List.of("com.example.SharedDispatcher.submitDocument()"))
                .build();

        assertThat(engine.isRoutedToCorrectMachine(
                orderChain, "com.example.OrderStateMachineConfiguration", context)).isTrue();
        assertThat(engine.isRoutedToCorrectMachine(
                orderChain, "com.example.DocumentStateMachineConfiguration", context)).isFalse();
        assertThat(engine.isRoutedToCorrectMachine(
                documentChain, "com.example.DocumentStateMachineConfiguration", context)).isTrue();
        assertThat(engine.isRoutedToCorrectMachine(
                documentChain, "com.example.OrderStateMachineConfiguration", context)).isFalse();
    }

    private static void writeTwoMachineSample(Path tempDir) throws Exception {
        Path orderPkg = tempDir.resolve("com/example/order");
        Path invoicePkg = tempDir.resolve("com/example/invoice");
        Path sharedPkg = tempDir.resolve("com/example/shared");
        Path configPkg = tempDir.resolve("com/example/config");
        Files.createDirectories(orderPkg);
        Files.createDirectories(invoicePkg);
        Files.createDirectories(sharedPkg);
        Files.createDirectories(configPkg);

        Files.writeString(sharedPkg.resolve("SharedState.java"),
                "package com.example.shared; public enum SharedState { NEW }");
        Files.writeString(orderPkg.resolve("OrderState.java"),
                "package com.example.order; public enum OrderState { NEW }");
        Files.writeString(orderPkg.resolve("OrderEvent.java"),
                "package com.example.order; public enum OrderEvent { PAY }");
        Files.writeString(invoicePkg.resolve("InvoiceEvent.java"),
                "package com.example.invoice; public enum InvoiceEvent { PAY }");
        Files.writeString(configPkg.resolve("OrderStateMachineConfiguration.java"),
                """
                package com.example.config;
                import com.example.order.OrderEvent;
                import com.example.order.OrderState;
                import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
                public class OrderStateMachineConfiguration
                        extends EnumStateMachineConfigurerAdapter<OrderState, OrderEvent> {
                }
                """);
    }
}
