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 org.junit.jupiter.api.io.TempDir;

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

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

/**
 * Per-branch dispatcher splitting: if-else machineType arms must resolve to branch-specific
 * valueOf triggers and constraints, not a collapsed ternary across machines.
 */
class EnterpriseDispatcherBranchSplitTest {

    @Test
    void shouldResolveBranchSpecificValueOfForEachMachineTypeBinding(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class MachineController {
                StateMachineDispatcher dispatcher;
                public void transition(String machineType, String event) {
                    dispatcher.dispatch(machineType, event);
                }
            }
            class StateMachineDispatcher {
                void dispatch(String machineType, String eventString) {
                    if ("ORDER".equalsIgnoreCase(machineType)) {
                        fireOrder(eventString);
                    } else if ("DOCUMENT".equalsIgnoreCase(machineType)) {
                        fireDocument(eventString);
                    } else if ("USER".equalsIgnoreCase(machineType)) {
                        fireUser(eventString);
                    }
                }
                void fireOrder(String eventString) {
                    OrderEvent event = OrderEvent.valueOf(eventString.toUpperCase());
                    send(event);
                }
                void fireDocument(String eventString) {
                    DocumentEvent event = DocumentEvent.valueOf(eventString.toUpperCase());
                    send(event);
                }
                void fireUser(String eventString) {
                    UserEvent event = UserEvent.valueOf(eventString.toUpperCase());
                    send(event);
                }
                void send(OrderEvent event) {}
                void send(DocumentEvent event) {}
                void send(UserEvent event) {}
            }
            enum OrderEvent { PAY }
            enum DocumentEvent { SUBMIT }
            enum UserEvent { VERIFY }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.MachineController")
                .methodName("transition")
                .name("POST /api/machine/{machineType}/transition/{event}")
                .parameters(List.of(
                        EntryPoint.Parameter.builder()
                                .name("machineType")
                                .type("String")
                                .annotations(List.of("PathVariable"))
                                .build(),
                        EntryPoint.Parameter.builder()
                                .name("event")
                                .type("String")
                                .annotations(List.of("PathVariable"))
                                .build()))
                .build();

        TriggerPoint orderTrigger = TriggerPoint.builder()
                .className("com.example.StateMachineDispatcher")
                .methodName("send")
                .event("event")
                .build();

        List<CallChain> chains = engine.findChains(List.of(entryPoint), List.of(orderTrigger));

        CallChain orderChain = chains.stream()
                .filter(c -> c.getEntryPoint().getName().contains("/ORDER/"))
                .findFirst()
                .orElseThrow();
        List<CallChain> documentChains = chains.stream()
                .filter(c -> c.getEntryPoint().getName().contains("/DOCUMENT/"))
                .toList();
        assertThat(documentChains).isNotEmpty();
        CallChain documentChain = documentChains.stream()
                .filter(c -> c.getMethodChain().contains("com.example.StateMachineDispatcher.fireDocument"))
                .findFirst()
                .orElseThrow();

        assertThat(orderChain.getTriggerPoint().getEvent())
                .contains("OrderEvent.valueOf")
                .doesNotContain("DocumentEvent")
                .doesNotContain("UserEvent")
                .doesNotContain("true ?");
        assertThat(orderChain.getTriggerPoint().getConstraint())
                .contains("ORDER")
                .doesNotContain("DOCUMENT");

        assertThat(documentChain.getMethodChain())
                .contains("com.example.StateMachineDispatcher.fireDocument")
                .doesNotContain("com.example.StateMachineDispatcher.fireOrder");
        assertThat(documentChain.getTriggerPoint().getEvent())
                .satisfiesAnyOf(
                        e -> assertThat(e).contains("DocumentEvent.valueOf"),
                        e -> assertThat(e).isEqualTo("event"));
        assertThat(documentChain.getTriggerPoint().getConstraint())
                .contains("DOCUMENT")
                .doesNotContain("USER");
    }

    @Test
    void bindingExpanderShouldProduceMachineTypeVariants(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class MachineController {
                StateMachineDispatcher dispatcher;
                public void transition(String machineType, String event) {
                    dispatcher.dispatch(machineType, event);
                }
            }
            class StateMachineDispatcher {
                void dispatch(String machineType, String eventString) {
                    if ("ORDER".equalsIgnoreCase(machineType)) {
                        order(eventString);
                    } else if ("DOCUMENT".equalsIgnoreCase(machineType)) {
                        document(eventString);
                    }
                }
                void order(String eventString) {}
                void document(String eventString) {}
            }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.MachineController")
                .methodName("transition")
                .parameters(List.of(
                        EntryPoint.Parameter.builder()
                                .name("machineType")
                                .type("String")
                                .annotations(List.of("PathVariable"))
                                .build()))
                .build();

        List<Map<String, String>> variants = EntryPointBindingExpander.expandPathVariableBindings(
                entryPoint, context, engine.buildCallGraph());

        assertThat(variants).extracting(map -> map.get("machineType"))
                .containsExactlyInAnyOrder("ORDER", "DOCUMENT");
    }
}
