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.analysis.resolver.BooleanConstraintEvaluator;
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;

class EntryPointBindingExpanderTest {

    @Test
    void shouldExpandPathVariableFromStringSwitchMapper(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class GenericCommandController {
                CommandGateway gateway;
                public void execute(String commandKey) {
                    gateway.executeViaMapper(commandKey);
                }
            }
            class CommandGateway {
                StringCommandMapper mapper;
                void executeViaMapper(String commandKey) {
                    mapper.fromString(commandKey);
                }
            }
            class StringCommandMapper {
                DomainCommand fromString(String commandKey) {
                    return switch (commandKey) {
                        case "order.pay" -> DomainCommand.ORDER_PAY;
                        case "order.ship" -> DomainCommand.ORDER_SHIP;
                        default -> throw new IllegalArgumentException(commandKey);
                    };
                }
            }
            enum DomainCommand { ORDER_PAY, ORDER_SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldProduceSinglePayChainForGenericCommandEndpoint(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class GenericCommandController {
                private final CommandGateway gateway = new CommandGateway();
                public void execute(String commandKey) {
                    gateway.executeViaMapper(commandKey);
                }
            }
            enum DomainCommand { ORDER_PAY, ORDER_SHIP }
            enum OrderEvent { PAY, SHIP }
            class CommandGateway {
                private final StringCommandMapper mapper = new StringCommandMapper();
                private final CentralDispatcher central = new CentralDispatcher();
                void executeViaMapper(String commandKey) {
                    DomainCommand command = mapper.fromString(commandKey);
                    central.dispatch(command);
                }
            }
            class StringCommandMapper {
                DomainCommand fromString(String commandKey) {
                    return switch (commandKey) {
                        case "order.pay" -> DomainCommand.ORDER_PAY;
                        case "order.ship" -> DomainCommand.ORDER_SHIP;
                        default -> throw new IllegalArgumentException(commandKey);
                    };
                }
            }
            class CentralDispatcher {
                void dispatch(DomainCommand command) {
                    switch (command) {
                        case ORDER_PAY -> orderPay();
                        case ORDER_SHIP -> orderShip();
                    }
                }
                void orderPay() { fire(OrderEvent.PAY); }
                void orderShip() { fire(OrderEvent.SHIP); }
                void fire(OrderEvent event) {}
            }
            """;
        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.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();
        TriggerPoint trigger = TriggerPoint.builder()
                .className("com.example.CentralDispatcher")
                .methodName("fire")
                .event("event")
                .build();

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

        assertThat(chains).hasSize(2);
        assertThat(chains.stream().map(chain -> chain.getEntryPoint().getName()))
                .containsExactlyInAnyOrder("POST /api/commands/order.pay", "POST /api/commands/order.ship");
        assertThat(chains.stream().filter(chain -> chain.getEntryPoint().getName().endsWith("order.pay"))).hasSize(1);
        assertThat(chains.stream().filter(chain -> chain.getEntryPoint().getName().endsWith("order.pay")).findFirst().orElseThrow()
                .getMethodChain()).contains("com.example.CentralDispatcher.orderPay");
    }

    @Test
    void shouldExpandPathVariableFromIfElseEqualsChain(@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);
                    }
                }
                void fireOrder(String eventString) {}
                void fireDocument(String eventString) {}
            }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandEventPathVariableFromPreservedEnumValueOfParam(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class OrderController {
                OrderGateway gateway;
                public void transition(String event) {
                    gateway.trigger(event);
                }
            }
            class OrderGateway {
                void trigger(String event) {
                    OrderEvent.valueOf(event.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.OrderController")
                .methodName("transition")
                .name("POST /api/order/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("event"))
                .containsExactlyInAnyOrder("PAY", "SHIP");
        assertThat(EntryPointBindingExpander.withResolvedPath(entryPoint, variants.get(0)).getName())
                .isEqualTo("POST /api/order/PAY");
    }

    @Test
    void shouldExpandRenamedEventThroughDispatcherWhenMachineTypeIsBound(@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);
                    }
                }
                void fireOrder(String eventString) {
                    OrderEvent.valueOf(eventString.toUpperCase());
                }
                void fireDocument(String eventString) {
                    DocumentEvent.valueOf(eventString.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

        assertThat(variants).anySatisfy(variant -> {
            assertThat(variant).containsEntry("machineType", "ORDER");
            assertThat(variant.get("event")).isIn("PAY", "SHIP");
        });
        assertThat(EntryPointBindingExpander.withResolvedPath(
                entryPoint, Map.of("machineType", "ORDER", "event", "PAY")).getName())
                .isEqualTo("POST /api/machine/ORDER/transition/PAY");
    }

    @Test
    void shouldExpandRenamedEventFromInlineValueOfWhenMachineTypeIsBound(@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)) {
                        OrderEvent event = OrderEvent.valueOf(eventString.toUpperCase());
                        send(event);
                    } else if ("DOCUMENT".equalsIgnoreCase(machineType)) {
                        DocumentEvent event = DocumentEvent.valueOf(eventString.toUpperCase());
                        send(event);
                    }
                }
                void send(OrderEvent event) {}
                void send(DocumentEvent event) {}
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

        assertThat(variants.stream().anyMatch(variant ->
                "ORDER".equals(variant.get("machineType"))
                        && List.of("PAY", "SHIP").contains(variant.get("event")))).isTrue();
    }

    @Test
    void shouldExpandEventThroughMultiHopParameterRenames(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class MachineController {
                RoutingGateway gateway;
                public void transition(String machineType, String event) {
                    gateway.route(machineType, event);
                }
            }
            class RoutingGateway {
                void route(String domain, String actionKey) {
                    dispatcher.dispatch(domain, actionKey);
                }
                StateMachineDispatcher dispatcher;
            }
            class StateMachineDispatcher {
                void dispatch(String machineType, String eventString) {
                    if ("ORDER".equalsIgnoreCase(machineType)) {
                        fireOrder(eventString);
                    } else if ("DOCUMENT".equalsIgnoreCase(machineType)) {
                        fireDocument(eventString);
                    }
                }
                void fireOrder(String eventString) {
                    OrderEvent.valueOf(eventString.toUpperCase());
                }
                void fireDocument(String eventString) {
                    DocumentEvent.valueOf(eventString.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

        assertThat(variants).anySatisfy(variant -> {
            assertThat(variant).containsEntry("machineType", "ORDER");
            assertThat(variant.get("event")).isIn("PAY", "SHIP");
        });
        assertThat(EntryPointBindingExpander.withResolvedPath(
                entryPoint, Map.of("machineType", "ORDER", "event", "PAY")).getName())
                .isEqualTo("POST /api/machine/ORDER/transition/PAY");
    }

    @Test
    void shouldExpandEventWhenParameterIsRenamedWithinMethodBody(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class OrderController {
                OrderGateway gateway;
                public void transition(String event) {
                    gateway.trigger(event);
                }
            }
            class OrderGateway {
                void trigger(String event) {
                    String eventCode = event;
                    OrderEvent.valueOf(eventCode.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.OrderController")
                .methodName("transition")
                .name("POST /api/order/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("event"))
                .containsExactlyInAnyOrder("PAY", "SHIP");
    }

    @Test
    void shouldExpandEventWhenRenamedAliasIsForwardedAcrossCallEdge(@TempDir Path tempDir) throws Exception {
        // Intra-method alias (code = event) must be recognized when enqueueing dispatch(code).
        String source = """
            package com.example;
            public class OrderController {
                OrderGateway gateway;
                public void transition(String event) {
                    String code = event;
                    gateway.trigger(code);
                }
            }
            class OrderGateway {
                void trigger(String payload) {
                    OrderEvent.valueOf(payload);
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.OrderController")
                .methodName("transition")
                .name("POST /api/order/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("event"))
                .containsExactlyInAnyOrder("PAY", "SHIP");
    }

    @Test
    void shouldExpandEventWhenEntryIsInterfaceAndEdgesLiveOnImplementation(@TempDir Path tempDir)
            throws Exception {
        String source = """
            package com.example;
            public interface MachineApi {
                void transition(String event);
            }
            public class MachineController implements MachineApi {
                DomainCollaborator collaborator;
                public void transition(String event) {
                    collaborator.dispatch(event);
                }
            }
            class DomainCollaborator {
                void dispatch(String event) {
                    DomainEvent.valueOf(event);
                }
            }
            enum DomainEvent { PAY, SHIP, CANCEL }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        // Entry keyed on the interface type (Spring MVC interface controllers / OpenAPI stubs).
        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.MachineApi")
                .methodName("transition")
                .name("POST /api/machine/transition/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants)
                .as("expander must follow interface entry → impl edges → nested valueOf")
                .extracting(map -> map.get("event"))
                .containsExactlyInAnyOrder("PAY", "SHIP", "CANCEL");
    }

    @Test
    void shouldNotMergeBindingEdgesFromMultipleInterfaceImplementors(@TempDir Path tempDir)
            throws Exception {
        String source = """
            package com.example;
            public interface MachineApi {
                void transition(String event);
            }
            public class OrderController implements MachineApi {
                OrderGateway orderGateway;
                public void transition(String event) {
                    orderGateway.trigger(event);
                }
            }
            public class DocumentController implements MachineApi {
                DocumentGateway documentGateway;
                public void transition(String event) {
                    documentGateway.trigger(event);
                }
            }
            class OrderGateway {
                void trigger(String event) {
                    OrderEvent.valueOf(event);
                }
            }
            class DocumentGateway {
                void trigger(String event) {
                    DocumentEvent.valueOf(event);
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, ARCHIVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.MachineApi")
                .methodName("transition")
                .name("POST /api/machine/transition/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants)
                .as("multi-impl interface edges must not merge unrelated valueOf sites")
                .isEmpty();
    }

    @Test
    void shouldExpandEventWhenParameterIsNormalizedWithinMethodBody(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class OrderController {
                OrderGateway gateway;
                public void transition(String event) {
                    gateway.trigger(event);
                }
            }
            class OrderGateway {
                void trigger(String event) {
                    String normalized = event.trim();
                    OrderEvent.valueOf(normalized.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.OrderController")
                .methodName("transition")
                .name("POST /api/order/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("event"))
                .containsExactlyInAnyOrder("PAY", "SHIP");
    }

    @Test
    void shouldExpandEventWhenParameterIsPassedThroughSingleArgHelper(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class OrderController {
                OrderGateway gateway;
                public void transition(String event) {
                    gateway.trigger(event);
                }
            }
            class OrderGateway {
                void trigger(String event) {
                    OrderEvent.valueOf(normalize(event));
                }
                String normalize(String raw) {
                    return raw.toUpperCase();
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.OrderController")
                .methodName("transition")
                .name("POST /api/order/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("event"))
                .containsExactlyInAnyOrder("PAY", "SHIP");
    }

    @Test
    void shouldExpandEventWhenParameterIsPassedThroughCrossClassStaticHelper(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class OrderController {
                OrderGateway gateway;
                public void transition(String event) {
                    gateway.trigger(event);
                }
            }
            class OrderGateway {
                void trigger(String event) {
                    OrderEvent.valueOf(EventNormalizer.normalize(event));
                }
            }
            class EventNormalizer {
                static String normalize(String raw) {
                    return raw.toUpperCase();
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.OrderController")
                .methodName("transition")
                .name("POST /api/order/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("event"))
                .containsExactlyInAnyOrder("PAY", "SHIP");
    }

    @Test
    void shouldExpandPathVariableFromSwitchOnCrossClassStaticHelper(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class GenericCommandController {
                CommandGateway gateway;
                public void execute(String commandKey) {
                    gateway.executeViaMapper(commandKey);
                }
            }
            class CommandGateway {
                StringCommandMapper mapper;
                void executeViaMapper(String commandKey) {
                    mapper.fromString(commandKey);
                }
            }
            class StringCommandMapper {
                DomainCommand fromString(String commandKey) {
                    return switch (CommandKeys.normalize(commandKey)) {
                        case "order.pay" -> DomainCommand.ORDER_PAY;
                        case "order.ship" -> DomainCommand.ORDER_SHIP;
                        default -> throw new IllegalArgumentException(commandKey);
                    };
                }
            }
            class CommandKeys {
                static String normalize(String raw) {
                    return raw.trim();
                }
            }
            enum DomainCommand { ORDER_PAY, ORDER_SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandEventWhenParameterIsPassedThroughImportedCrossPackageHelper(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("com/example/util"));
        Files.writeString(tempDir.resolve("com/example/util/EventNormalizer.java"), """
            package com.example.util;
            public class EventNormalizer {
                public static String normalize(String raw) {
                    return raw.toUpperCase();
                }
            }
            """);
        Files.writeString(tempDir.resolve("com/example/OrderGateway.java"), """
            package com.example;
            import com.example.util.EventNormalizer;
            public class OrderController {
                OrderGateway gateway;
                public void transition(String event) {
                    gateway.trigger(event);
                }
            }
            class OrderGateway {
                void trigger(String event) {
                    OrderEvent.valueOf(EventNormalizer.normalize(event));
                }
            }
            enum OrderEvent { PAY, SHIP }
            """);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.OrderController")
                .methodName("transition")
                .name("POST /api/order/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("event"))
                .containsExactlyInAnyOrder("PAY", "SHIP");
    }

    @Test
    void shouldNotExpandWhenSingleArgHelperReturnsConstantInsteadOfForwarding(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class OrderController {
                OrderGateway gateway;
                public void transition(String event) {
                    gateway.trigger(event);
                }
            }
            class OrderGateway {
                void trigger(String event) {
                    OrderEvent.valueOf(spoof(event));
                }
                String spoof(String raw) {
                    return "PAY";
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.OrderController")
                .methodName("transition")
                .name("POST /api/order/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldExpandEventWhenParameterIsPassedThroughStaticImportHelper(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("com/example/util"));
        Files.writeString(tempDir.resolve("com/example/util/EventNormalizer.java"), """
            package com.example.util;
            public class EventNormalizer {
                public static String normalize(String raw) {
                    return raw.toUpperCase();
                }
            }
            """);
        Files.writeString(tempDir.resolve("com/example/OrderGateway.java"), """
            package com.example;
            import static com.example.util.EventNormalizer.normalize;
            public class OrderController {
                OrderGateway gateway;
                public void transition(String event) {
                    gateway.trigger(event);
                }
            }
            class OrderGateway {
                void trigger(String event) {
                    OrderEvent.valueOf(normalize(event));
                }
            }
            enum OrderEvent { PAY, SHIP }
            """);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.OrderController")
                .methodName("transition")
                .name("POST /api/order/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("event"))
                .containsExactlyInAnyOrder("PAY", "SHIP");
    }

    @Test
    void shouldExpandEventWhenParameterIsPassedThroughChainedSingleArgHelpers(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class OrderController {
                OrderGateway gateway;
                public void transition(String event) {
                    gateway.trigger(event);
                }
            }
            class OrderGateway {
                void trigger(String event) {
                    OrderEvent.valueOf(toUpperCase(trim(event)));
                }
                String trim(String raw) {
                    return raw.trim();
                }
                String toUpperCase(String raw) {
                    return raw.toUpperCase();
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.OrderController")
                .methodName("transition")
                .name("POST /api/order/{event}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("event")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("event"))
                .containsExactlyInAnyOrder("PAY", "SHIP");
    }

    @Test
    void shouldExpandMachineTypeFromIfEqualsOnSingleArgHelper(@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(normalizeType(machineType))) {
                        fireOrder(eventString);
                    } else if ("DOCUMENT".equalsIgnoreCase(normalizeType(machineType))) {
                        fireDocument(eventString);
                    }
                }
                String normalizeType(String raw) {
                    return raw.trim();
                }
                void fireOrder(String eventString) {
                    OrderEvent.valueOf(eventString.toUpperCase());
                }
                void fireDocument(String eventString) {
                    DocumentEvent.valueOf(eventString.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

        assertThat(variants.stream().map(v -> v.get("machineType")).distinct())
                .containsExactlyInAnyOrder("ORDER", "DOCUMENT");
        assertThat(variants.stream()
                .filter(v -> "ORDER".equals(v.get("machineType")))
                .map(v -> v.get("event"))
                .toList())
                .containsExactlyInAnyOrder("PAY", "SHIP");
        assertThat(variants.stream()
                .filter(v -> "DOCUMENT".equals(v.get("machineType")))
                .map(v -> v.get("event"))
                .toList())
                .containsExactlyInAnyOrder("SUBMIT", "APPROVE");
    }

    @Test
    void shouldExpandMachineTypeFromArgumentEqualsOnSingleArgHelper(@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 (normalizeType(machineType).equalsIgnoreCase("ORDER")) {
                        fireOrder(eventString);
                    } else if (normalizeType(machineType).equalsIgnoreCase("DOCUMENT")) {
                        fireDocument(eventString);
                    }
                }
                String normalizeType(String raw) {
                    return raw.trim();
                }
                void fireOrder(String eventString) {
                    OrderEvent.valueOf(eventString.toUpperCase());
                }
                void fireDocument(String eventString) {
                    DocumentEvent.valueOf(eventString.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandMachineTypeFromObjectsEqualsOnSingleArgHelper(@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 (java.util.Objects.equals("ORDER", normalizeType(machineType))) {
                        fireOrder(eventString);
                    } else if (java.util.Objects.equals("DOCUMENT", normalizeType(machineType))) {
                        fireDocument(eventString);
                    }
                }
                String normalizeType(String raw) {
                    return raw.trim();
                }
                void fireOrder(String eventString) {
                    OrderEvent.valueOf(eventString.toUpperCase());
                }
                void fireDocument(String eventString) {
                    DocumentEvent.valueOf(eventString.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExtractMachineTypeCasesFromBooleanPredicateIfChain(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            class StateMachineDispatcher {
                void dispatch(String machineType, String eventString) {
                    if (matchesOrder(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (matchesDocument(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
                boolean matchesOrder(String raw) {
                    return "ORDER".equalsIgnoreCase(raw.trim());
                }
                boolean matchesDocument(String raw) {
                    return "DOCUMENT".equalsIgnoreCase(raw.trim());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        List<String> cases = EntryPointBindingExpander.extractStringSwitchCases(
                "com.example.StateMachineDispatcher.dispatch", "machineType", context);

        assertThat(cases).containsExactlyInAnyOrder("ORDER", "DOCUMENT");
    }

    @Test
    void shouldInlineBooleanPredicateEdgeConstraints(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            class StateMachineDispatcher {
                void dispatch(String machineType, String eventString) {
                    if (matchesOrder(machineType)) {
                        fireOrder(eventString);
                    } else if (matchesDocument(machineType)) {
                        fireDocument(eventString);
                    }
                }
                boolean matchesOrder(String raw) {
                    return "ORDER".equalsIgnoreCase(raw.trim());
                }
                boolean matchesDocument(String raw) {
                    return "DOCUMENT".equalsIgnoreCase(raw.trim());
                }
                void fireOrder(String eventString) {
                    OrderEvent.valueOf(eventString.toUpperCase());
                }
                void fireDocument(String eventString) {
                    DocumentEvent.valueOf(eventString.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        String caller = "com.example.StateMachineDispatcher.dispatch";
        String orderConstraint = EntryPointBindingExpander.inlineBooleanPredicateConstraint(
                "matchesOrder(machineType)", caller, context);
        String documentConstraint = EntryPointBindingExpander.inlineBooleanPredicateConstraint(
                "matchesDocument(machineType)", caller, context);

        assertThat(orderConstraint).contains("ORDER");
        assertThat(documentConstraint).contains("DOCUMENT");
        assertThat(BooleanConstraintEvaluator.isCompatibleWithBindings(
                orderConstraint, Map.of("machineType", "DOCUMENT"))).isFalse();
        assertThat(BooleanConstraintEvaluator.isCompatibleWithBindings(
                documentConstraint, Map.of("machineType", "DOCUMENT"))).isTrue();

        String elseIfConstraint = EntryPointBindingExpander.inlineBooleanPredicateConstraint(
                "!(matchesOrder(machineType)) && matchesDocument(machineType)", caller, context);
        assertThat(BooleanConstraintEvaluator.isCompatibleWithBindings(
                elseIfConstraint, Map.of("machineType", "DOCUMENT"))).isTrue();
        assertThat(BooleanConstraintEvaluator.isCompatibleWithBindings(
                elseIfConstraint, Map.of("machineType", "ORDER"))).isFalse();
    }

    @Test
    void shouldInlineNestedBooleanPredicateConstraint(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            class StateMachineDispatcher {
                void dispatch(String machineType, String eventString) {
                    if (matchesOrder(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    }
                }
                boolean matchesOrder(String raw) {
                    return isOrderKey(normalize(raw));
                }
                String normalize(String raw) {
                    return raw.trim();
                }
                boolean isOrderKey(String normalized) {
                    return "ORDER".equalsIgnoreCase(normalized);
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        String caller = "com.example.StateMachineDispatcher.dispatch";
        String constraint = EntryPointBindingExpander.inlineBooleanPredicateConstraint(
                "matchesOrder(machineType)", caller, context);
        assertThat(constraint).contains("normalize(machineType)");
        assertThat(BooleanConstraintEvaluator.isCompatibleWithBindings(
                constraint, Map.of("machineType", "ORDER"))).isTrue();
        assertThat(BooleanConstraintEvaluator.isCompatibleWithBindings(
                constraint, Map.of("machineType", "DOCUMENT"))).isFalse();
    }

    @Test
    void shouldFilterDelegatedBranchesUsingBooleanPredicateConstraints(@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 (matchesOrder(machineType)) {
                        fireOrder(eventString);
                    } else if (matchesDocument(machineType)) {
                        fireDocument(eventString);
                    }
                }
                boolean matchesOrder(String raw) {
                    return "ORDER".equalsIgnoreCase(raw.trim());
                }
                boolean matchesDocument(String raw) {
                    return "DOCUMENT".equalsIgnoreCase(raw.trim());
                }
                void fireOrder(String eventString) {
                    OrderEvent.valueOf(eventString.toUpperCase());
                }
                void fireDocument(String eventString) {
                    DocumentEvent.valueOf(eventString.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        List<String> machineTypes = EntryPointBindingExpander.expandPathVariableBindings(
                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()))
                        .build(),
                context,
                graph).stream().map(v -> v.get("machineType")).toList();

        assertThat(machineTypes).containsExactlyInAnyOrder("ORDER", "DOCUMENT");
    }

    @Test
    void shouldExpandMachineTypeFromBooleanPredicateHelper(@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 (matchesOrder(machineType)) {
                        fireOrder(eventString);
                    } else if (matchesDocument(machineType)) {
                        fireDocument(eventString);
                    }
                }
                boolean matchesOrder(String raw) {
                    return "ORDER".equalsIgnoreCase(raw.trim());
                }
                boolean matchesDocument(String raw) {
                    return "DOCUMENT".equalsIgnoreCase(raw.trim());
                }
                void fireOrder(String eventString) {
                    OrderEvent.valueOf(eventString.toUpperCase());
                }
                void fireDocument(String eventString) {
                    DocumentEvent.valueOf(eventString.toUpperCase());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

        assertThat(variants.stream().map(v -> v.get("machineType")).distinct())
                .containsExactlyInAnyOrder("ORDER", "DOCUMENT");
        assertThat(variants.stream()
                .filter(v -> "ORDER".equals(v.get("machineType")))
                .map(v -> v.get("event"))
                .toList())
                .containsExactlyInAnyOrder("PAY", "SHIP");
    }

    @Test
    void shouldExpandMachineTypeFromCrossClassBooleanPredicateHelper(@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 (TypeMatcher.matchesOrder(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (TypeMatcher.matchesDocument(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
            }
            class TypeMatcher {
                static boolean matchesOrder(String raw) {
                    return "ORDER".equalsIgnoreCase(raw.trim());
                }
                static boolean matchesDocument(String raw) {
                    return "DOCUMENT".equalsIgnoreCase(raw.trim());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldNotExpandWhenBooleanPredicateMatchesMultipleLiterals(@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 (matchesAny(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    }
                }
                boolean matchesAny(String raw) {
                    if ("ORDER".equalsIgnoreCase(raw)) {
                        return true;
                    }
                    return "DOCUMENT".equalsIgnoreCase(raw);
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldExpandMachineTypeFromStaticImportBooleanPredicate(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import static com.example.TypeMatcher.matchesOrder;
            import static com.example.TypeMatcher.matchesDocument;
            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 (matchesOrder(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (matchesDocument(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
            }
            class TypeMatcher {
                static boolean matchesOrder(String raw) {
                    return "ORDER".equalsIgnoreCase(raw.trim());
                }
                static boolean matchesDocument(String raw) {
                    return "DOCUMENT".equalsIgnoreCase(raw.trim());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandMachineTypeFromNestedBooleanPredicateHelper(@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 (matchesOrder(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (matchesDocument(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
                boolean matchesOrder(String raw) {
                    return isOrderKey(normalize(raw));
                }
                boolean matchesDocument(String raw) {
                    return isDocumentKey(normalize(raw));
                }
                String normalize(String raw) {
                    return raw.trim();
                }
                boolean isOrderKey(String normalized) {
                    return "ORDER".equalsIgnoreCase(normalized);
                }
                boolean isDocumentKey(String normalized) {
                    return "DOCUMENT".equalsIgnoreCase(normalized);
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandMachineTypeFromBooleanPredicateUsingObjectsEquals(@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 (matchesOrder(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (matchesDocument(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
                boolean matchesOrder(String raw) {
                    return java.util.Objects.equals("ORDER", raw.trim());
                }
                boolean matchesDocument(String raw) {
                    return java.util.Objects.equals("DOCUMENT", raw.trim());
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandMachineTypeFromDirectStringSwitch(@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) {
                    switch (machineType) {
                        case "ORDER" -> OrderEvent.valueOf(eventString.toUpperCase());
                        case "DOCUMENT" -> DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandMachineTypeFromBooleanPredicateUsingReversedObjectsEqualsOnHelper(@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 (matchesOrder(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    }
                }
                boolean matchesOrder(String raw) {
                    return java.util.Objects.equals(normalize(raw), "ORDER");
                }
                String normalize(String raw) {
                    return raw.trim();
                }
            }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("machineType")).containsExactly("ORDER");
    }

    @Test
    void shouldExpandCommandKeyFromStaticMapLookup(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final java.util.Map<String, OrderEvent> ROUTES = java.util.Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP);
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandMachineTypeFromBooleanPredicateUsingObjectsEqualsOnHelper(@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 (matchesOrder(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (matchesDocument(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
                boolean matchesOrder(String raw) {
                    return java.util.Objects.equals("ORDER", normalize(raw));
                }
                boolean matchesDocument(String raw) {
                    return java.util.Objects.equals("DOCUMENT", normalize(raw));
                }
                String normalize(String raw) {
                    return raw.trim();
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandCommandKeyFromCrossClassStaticMapLookup(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(Routes.ROUTES.get(commandKey));
                }
            }
            class Routes {
                static final java.util.Map<String, OrderEvent> ROUTES = java.util.Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP);
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticMapOfEntriesLookup(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final java.util.Map<String, OrderEvent> ROUTES = java.util.Map.ofEntries(
                        java.util.Map.entry("order.pay", OrderEvent.PAY),
                        java.util.Map.entry("order.ship", OrderEvent.SHIP));
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromSwitchExpression(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                void trigger(String commandKey) {
                    DomainCommand command = switch (commandKey) {
                        case "order.pay" -> DomainCommand.ORDER_PAY;
                        case "order.ship" -> DomainCommand.ORDER_SHIP;
                        default -> throw new IllegalArgumentException("Unknown command: " + commandKey);
                    };
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(command);
                }
            }
            enum DomainCommand { ORDER_PAY, ORDER_SHIP }
            class StateMachine { void sendEvent(DomainCommand command) {} }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandMachineTypeFromEqualsIgnoreCaseIfChain(@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)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if ("DOCUMENT".equalsIgnoreCase(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandCommandKeyFromStaticMapGetOrDefault(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP);
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.getOrDefault(commandKey, OrderEvent.PAY));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandMachineTypeFromBooleanPredicateUsingEqualsIgnoreCaseOnHelper(@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 (matchesOrder(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (matchesDocument(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
                boolean matchesOrder(String raw) {
                    return "ORDER".equalsIgnoreCase(normalize(raw));
                }
                boolean matchesDocument(String raw) {
                    return "DOCUMENT".equalsIgnoreCase(normalize(raw));
                }
                String normalize(String raw) {
                    return raw.trim();
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandCommandKeyFromFullyQualifiedCrossPackageMapLookup(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("com/example/routes"));
        Files.writeString(tempDir.resolve("com/example/routes/CommandRoutes.java"), """
            package com.example.routes;
            import java.util.Map;
            public class CommandRoutes {
                public static final Map<String, String> ROUTES = Map.of(
                        "order.pay", "OrderEvent.PAY",
                        "order.ship", "OrderEvent.SHIP");
            }
            """);
        Files.writeString(tempDir.resolve("com/example/App.java"), """
            package com.example;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                void trigger(String commandKey) {
                    OrderEvent event = OrderEvent.valueOf(
                            com.example.routes.CommandRoutes.ROUTES.get(commandKey));
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(event);
                }
            }
            enum OrderEvent { PAY, SHIP }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            """);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticImportMapLookup(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("com/example/routes"));
        Files.writeString(tempDir.resolve("com/example/routes/CommandRoutes.java"), """
            package com.example.routes;
            import java.util.Map;
            public class CommandRoutes {
                public static final Map<String, String> ROUTES = Map.of(
                        "order.pay", "OrderEvent.PAY",
                        "order.ship", "OrderEvent.SHIP");
            }
            """);
        Files.writeString(tempDir.resolve("com/example/App.java"), """
            package com.example;
            import static com.example.routes.CommandRoutes.ROUTES;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                void trigger(String commandKey) {
                    OrderEvent event = OrderEvent.valueOf(ROUTES.get(commandKey));
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(event);
                }
            }
            enum OrderEvent { PAY, SHIP }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            """);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticFinalStringConstantMapKeys(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final String PAY_KEY = "order.pay";
                private static final String SHIP_KEY = "order.ship";
                private static final Map<String, OrderEvent> ROUTES = Map.of(
                        PAY_KEY, OrderEvent.PAY,
                        SHIP_KEY, OrderEvent.SHIP);
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromCrossClassStaticFinalStringConstantMapKeys(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = Map.of(
                        CommandKeys.PAY_KEY, OrderEvent.PAY,
                        CommandKeys.SHIP_KEY, OrderEvent.SHIP);
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class CommandKeys {
                static final String PAY_KEY = "order.pay";
                static final String SHIP_KEY = "order.ship";
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldNotExpandCommandKeyWhenMapKeysAreComputed(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = Map.of(
                        prefix("order.pay"), OrderEvent.PAY,
                        prefix("order.ship"), OrderEvent.SHIP);
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
                static String prefix(String value) {
                    return value;
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldExpandCommandKeyFromSwitchExpressionWithStaticFinalCaseConstants(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final String PAY_KEY = "order.pay";
                private static final String SHIP_KEY = "order.ship";
                void trigger(String commandKey) {
                    DomainCommand command = switch (commandKey) {
                        case PAY_KEY -> DomainCommand.ORDER_PAY;
                        case SHIP_KEY -> DomainCommand.ORDER_SHIP;
                        default -> throw new IllegalArgumentException("Unknown command: " + commandKey);
                    };
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(command);
                }
            }
            enum DomainCommand { ORDER_PAY, ORDER_SHIP }
            class StateMachine { void sendEvent(DomainCommand command) {} }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromChainedStaticFinalStringConstantMapKeys(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final String PAY_BASE = "order.pay";
                private static final String PAY_KEY = PAY_BASE;
                private static final String SHIP_BASE = "order.ship";
                private static final String SHIP_KEY = SHIP_BASE;
                private static final Map<String, OrderEvent> ROUTES = Map.of(
                        PAY_KEY, OrderEvent.PAY,
                        SHIP_KEY, OrderEvent.SHIP);
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandMachineTypeFromIfEqualsOnStaticFinalConstant(@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 {
                private static final String ORDER = "ORDER";
                private static final String DOCUMENT = "DOCUMENT";
                void dispatch(String machineType, String eventString) {
                    if (ORDER.equals(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (DOCUMENT.equalsIgnoreCase(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandCommandKeyFromSwitchExpressionWithCrossClassCaseConstants(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                void trigger(String commandKey) {
                    DomainCommand command = switch (commandKey) {
                        case CommandKeys.PAY_KEY -> DomainCommand.ORDER_PAY;
                        case CommandKeys.SHIP_KEY -> DomainCommand.ORDER_SHIP;
                        default -> throw new IllegalArgumentException("Unknown command: " + commandKey);
                    };
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(command);
                }
            }
            class CommandKeys {
                static final String PAY_KEY = "order.pay";
                static final String SHIP_KEY = "order.ship";
            }
            enum DomainCommand { ORDER_PAY, ORDER_SHIP }
            class StateMachine { void sendEvent(DomainCommand command) {} }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandMachineTypeFromCrossClassStaticFinalConstantInIfEquals(@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 (MachineTypes.ORDER.equalsIgnoreCase(machineType)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (MachineTypes.DOCUMENT.equalsIgnoreCase(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
            }
            class MachineTypes {
                static final String ORDER = "ORDER";
                static final String DOCUMENT = "DOCUMENT";
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandMachineTypeFromStaticImportConstantInIfEquals(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import static com.example.MachineTypes.ORDER;
            import static com.example.MachineTypes.DOCUMENT;
            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)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (DOCUMENT.equalsIgnoreCase(machineType)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
            }
            class MachineTypes {
                static final String ORDER = "ORDER";
                static final String DOCUMENT = "DOCUMENT";
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandMachineTypeFromReversedCrossClassConstantEquals(@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 (machineType.equalsIgnoreCase(MachineTypes.ORDER)) {
                        OrderEvent.valueOf(eventString.toUpperCase());
                    } else if (machineType.equals(MachineTypes.DOCUMENT)) {
                        DocumentEvent.valueOf(eventString.toUpperCase());
                    }
                }
            }
            class MachineTypes {
                static final String ORDER = "ORDER";
                static final String DOCUMENT = "DOCUMENT";
            }
            enum OrderEvent { PAY, SHIP }
            enum DocumentEvent { SUBMIT, APPROVE }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        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();

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

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

    @Test
    void shouldExpandCommandKeyFromSwitchExpressionWithExternalConstantsClass(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("com/example/constants"));
        Files.writeString(tempDir.resolve("com/example/constants/CommandKeys.java"), """
            package com.example.constants;
            public class CommandKeys {
                public static final String PAY_KEY = "order.pay";
                public static final String SHIP_KEY = "order.ship";
            }
            """);
        Files.writeString(tempDir.resolve("com/example/App.java"), """
            package com.example;
            import com.example.constants.CommandKeys;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                void trigger(String commandKey) {
                    DomainCommand command = switch (commandKey) {
                        case CommandKeys.PAY_KEY -> DomainCommand.ORDER_PAY;
                        case CommandKeys.SHIP_KEY -> DomainCommand.ORDER_SHIP;
                        default -> throw new IllegalArgumentException("Unknown command: " + commandKey);
                    };
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(command);
                }
            }
            enum DomainCommand { ORDER_PAY, ORDER_SHIP }
            class StateMachine { void sendEvent(DomainCommand command) {} }
            """);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromSwitchExpressionWithStaticImportCaseConstants(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import static com.example.CommandKeys.PAY_KEY;
            import static com.example.CommandKeys.SHIP_KEY;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                void trigger(String commandKey) {
                    DomainCommand command = switch (commandKey) {
                        case PAY_KEY -> DomainCommand.ORDER_PAY;
                        case SHIP_KEY -> DomainCommand.ORDER_SHIP;
                        default -> throw new IllegalArgumentException("Unknown command: " + commandKey);
                    };
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(command);
                }
            }
            class CommandKeys {
                static final String PAY_KEY = "order.pay";
                static final String SHIP_KEY = "order.ship";
            }
            enum DomainCommand { ORDER_PAY, ORDER_SHIP }
            class StateMachine { void sendEvent(DomainCommand command) {} }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticInitializerMapPutEntries(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = new HashMap<>();
                    ROUTES.put("order.pay", OrderEvent.PAY);
                    ROUTES.put("order.ship", OrderEvent.SHIP);
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldNotExpandCommandKeyFromStaticInitializerMapWithComputedPutKeys(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = new HashMap<>();
                    ROUTES.put(prefix("order.pay"), OrderEvent.PAY);
                    ROUTES.put("order.ship", OrderEvent.SHIP);
                }
                static String prefix(String value) {
                    return value;
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldExpandCommandKeyFromDoubleBraceInitializerMapPutEntries(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = new HashMap<>() {{
                    put("order.pay", OrderEvent.PAY);
                    put("order.ship", OrderEvent.SHIP);
                }};
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldNotExpandCommandKeyFromDoubleBraceInitializerMapWithComputedPutKeys(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = new HashMap<>() {{
                    put(prefix("order.pay"), OrderEvent.PAY);
                    put("order.ship", OrderEvent.SHIP);
                }};
                static String prefix(String value) {
                    return value;
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldExpandCommandKeyFromStaticBlockLocalVariableMapPutEntries(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    Map<String, OrderEvent> routes = new HashMap<>();
                    routes.put("order.pay", OrderEvent.PAY);
                    routes.put("order.ship", OrderEvent.SHIP);
                    ROUTES = routes;
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticBlockMapOfAssignment(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP
                    );
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldNotExpandCommandKeyFromStaticBlockLocalVariableWithUnlinkedPuts(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    Map<String, OrderEvent> routes = new HashMap<>();
                    routes.put("order.pay", OrderEvent.PAY);
                    routes.put("order.ship", OrderEvent.SHIP);
                    ROUTES = Map.of("order.other", OrderEvent.PAY);
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactly("order.other");
    }

    @Test
    void shouldNotExpandCommandKeyFromStaticBlockLocalVariableWithComputedPutKeys(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    Map<String, OrderEvent> routes = new HashMap<>();
                    routes.put(prefix("order.pay"), OrderEvent.PAY);
                    routes.put("order.ship", OrderEvent.SHIP);
                    ROUTES = routes;
                }
                static String prefix(String value) {
                    return value;
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldExpandCommandKeyFromStaticBlockPutAllWithMapOf(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = new HashMap<>();
                    ROUTES.putAll(Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP
                    ));
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticBlockPutAllFromCrossClassMap(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = new HashMap<>();
                    ROUTES.putAll(CommandRoutes.SEED);
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class CommandRoutes {
                static final Map<String, OrderEvent> SEED = Map.of(
                    "order.pay", OrderEvent.PAY,
                    "order.ship", OrderEvent.SHIP
                );
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticInitializerMapPutEntriesWithStaticFinalKeys(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final String PAY_KEY = "order.pay";
                private static final String SHIP_KEY = "order.ship";
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = new HashMap<>();
                    ROUTES.put(PAY_KEY, OrderEvent.PAY);
                    ROUTES.put(SHIP_KEY, OrderEvent.SHIP);
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldNotExpandCommandKeyFromStaticBlockPutAllWithRuntimeMap(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = new HashMap<>();
                    ROUTES.putAll(buildRoutes());
                }
                static Map<String, OrderEvent> buildRoutes() {
                    Map<String, OrderEvent> routes = new HashMap<>();
                    routes.put("order.pay", OrderEvent.PAY);
                    return routes;
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldExpandCommandKeyFromStaticBlockLocalPutAllWithMapOf(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    Map<String, OrderEvent> routes = new HashMap<>();
                    routes.putAll(Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP
                    ));
                    ROUTES = routes;
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticBlockMixedPutAllAndPutOnLinkedLocal(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    Map<String, OrderEvent> routes = new HashMap<>();
                    routes.putAll(Map.of("order.pay", OrderEvent.PAY));
                    routes.put("order.ship", OrderEvent.SHIP);
                    ROUTES = routes;
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromDoubleBraceInitializerPutAllWithMapOf(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = new HashMap<>() {{
                    putAll(Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP
                    ));
                }};
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromInlineEmptyHashMapWithStaticBlockPutAll(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = new HashMap<>();
                static {
                    ROUTES.putAll(Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP
                    ));
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticBlockMapOfEntriesAssignment(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = Map.ofEntries(
                        Map.entry("order.pay", OrderEvent.PAY),
                        Map.entry("order.ship", OrderEvent.SHIP)
                    );
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticBlockPutAllViaStaticImport(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            import static com.example.CommandRoutes.SEED;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = new HashMap<>();
                    ROUTES.putAll(SEED);
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class CommandRoutes {
                static final Map<String, OrderEvent> SEED = Map.of(
                    "order.pay", OrderEvent.PAY,
                    "order.ship", OrderEvent.SHIP
                );
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromDoubleBraceInitializerMixedPutAndPutAll(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = new HashMap<>() {{
                    putAll(Map.of("order.pay", OrderEvent.PAY));
                    put("order.ship", OrderEvent.SHIP);
                }};
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticFactoryMethodReturningMapOf(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = routes();
                static Map<String, OrderEvent> routes() {
                    return Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP
                    );
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromCrossClassStaticFactoryMethodReturningMapOf(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = CommandRoutes.seed();
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class CommandRoutes {
                static Map<String, OrderEvent> seed() {
                    return Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP
                    );
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticBlockStaticFactoryAssignment(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = routes();
                }
                static Map<String, OrderEvent> routes() {
                    return Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP
                    );
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldNotExpandCommandKeyFromStaticFactoryBuildingMapAtRuntime(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = routes();
                static Map<String, OrderEvent> routes() {
                    Map<String, OrderEvent> built = new HashMap<>();
                    built.put("order.pay", OrderEvent.PAY);
                    built.put("order.ship", OrderEvent.SHIP);
                    return built;
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldNotExpandCommandKeyFromStaticFactoryWithMultipleReturns(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = routes(true);
                static Map<String, OrderEvent> routes(boolean full) {
                    if (full) {
                        return Map.of(
                            "order.pay", OrderEvent.PAY,
                            "order.ship", OrderEvent.SHIP
                        );
                    }
                    return Map.of("order.pay", OrderEvent.PAY);
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldExpandCommandKeyFromNestedStaticFactoryDelegation(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = routes();
                static Map<String, OrderEvent> routes() {
                    return seed();
                }
                static Map<String, OrderEvent> seed() {
                    return Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP
                    );
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticFactoryReturningStaticFieldMap(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = routes();
                private static final Map<String, OrderEvent> SEED = Map.of(
                    "order.pay", OrderEvent.PAY,
                    "order.ship", OrderEvent.SHIP
                );
                static Map<String, OrderEvent> routes() {
                    return SEED;
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticFactoryReturningMapOfEntries(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = routes();
                static Map<String, OrderEvent> routes() {
                    return Map.ofEntries(
                        Map.entry("order.pay", OrderEvent.PAY),
                        Map.entry("order.ship", OrderEvent.SHIP)
                    );
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldNotExpandCommandKeyFromStaticBlockLoopPopulation(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.List;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = new HashMap<>();
                    for (String key : List.of("order.pay", "order.ship")) {
                        ROUTES.put(key, OrderEvent.PAY);
                    }
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldNotExpandCommandKeyFromRecursiveStaticFactory(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = routes();
                static Map<String, OrderEvent> routes() {
                    return routes();
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldExpandCommandKeyFromInheritedStaticMapField(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            abstract class BaseRoutes {
                protected static final Map<String, OrderEvent> ROUTES = Map.of(
                    "order.pay", OrderEvent.PAY,
                    "order.ship", OrderEvent.SHIP
                );
            }
            class OrderGateway extends BaseRoutes {
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromInterfaceStaticMapField(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            interface RouteSource {
                Map<String, OrderEvent> ROUTES = Map.of(
                    "order.pay", OrderEvent.PAY,
                    "order.ship", OrderEvent.SHIP
                );
            }
            class OrderGateway implements RouteSource {
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromQualifiedInheritedStaticMapField(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            abstract class BaseRoutes {
                protected static final Map<String, OrderEvent> ROUTES = Map.of(
                    "order.pay", OrderEvent.PAY,
                    "order.ship", OrderEvent.SHIP
                );
            }
            class OrderGateway extends BaseRoutes {
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(BaseRoutes.ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromStaticMethodMapReceiver(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                static Map<String, OrderEvent> routes() {
                    return Map.of(
                        "order.pay", OrderEvent.PAY,
                        "order.ship", OrderEvent.SHIP
                    );
                }
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(routes().get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandCommandKeyFromInheritedStaticBlockMap(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.HashMap;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            abstract class BaseRoutes {
                protected static final Map<String, OrderEvent> ROUTES;
                static {
                    ROUTES = new HashMap<>();
                    ROUTES.put("order.pay", OrderEvent.PAY);
                    ROUTES.put("order.ship", OrderEvent.SHIP);
                }
            }
            class OrderGateway extends BaseRoutes {
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }

    @Test
    void shouldExpandRequestParamFromStaticMapLookup(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private static final Map<String, OrderEvent> ROUTES = Map.of(
                    "order.pay", OrderEvent.PAY,
                    "order.ship", OrderEvent.SHIP
                );
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(ROUTES.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("RequestParam"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
        assertThat(EntryPointBindingExpander.withResolvedPath(entryPoint, variants.get(0)).getName())
                .isEqualTo("POST /api/commands?commandKey=order.pay");
    }

    @Test
    void shouldNotExpandCommandKeyFromInstanceFieldMap(@TempDir Path tempDir) throws Exception {
        String source = """
            package com.example;
            import java.util.Map;
            public class GenericCommandController {
                OrderGateway gateway;
                public void execute(String commandKey) {
                    gateway.trigger(commandKey);
                }
            }
            class OrderGateway {
                private final Map<String, OrderEvent> routes = Map.of(
                    "order.pay", OrderEvent.PAY,
                    "order.ship", OrderEvent.SHIP
                );
                void trigger(String commandKey) {
                    StateMachine sm = new StateMachine();
                    sm.sendEvent(routes.get(commandKey));
                }
            }
            class StateMachine { void sendEvent(OrderEvent event) {} }
            enum OrderEvent { PAY, SHIP }
            """;
        Files.writeString(tempDir.resolve("App.java"), source);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).isEmpty();
    }

    @Test
    void shouldExpandCommandKeyFromInheritedStaticBlockMapInSeparateCompilationUnit(@TempDir Path tempDir) throws Exception {
        Files.createDirectories(tempDir.resolve("com/example/routes"));
        Files.writeString(tempDir.resolve("com/example/routes/BaseRoutes.java"), """
                package com.example.routes;
                import java.util.HashMap;
                import java.util.Map;
                public abstract class BaseRoutes {
                    protected static final Map<String, String> ROUTES;
                    static {
                        ROUTES = new HashMap<>();
                        ROUTES.put("order.pay", "PAY");
                        ROUTES.put("order.ship", "SHIP");
                    }
                }
                """);
        Files.writeString(tempDir.resolve("com/example/App.java"), """
                package com.example;
                import com.example.routes.BaseRoutes;
                import java.util.Map;
                public class GenericCommandController {
                    OrderGateway gateway;
                    public void execute(String commandKey) {
                        gateway.trigger(commandKey);
                    }
                }
                class OrderGateway extends BaseRoutes {
                    void trigger(String commandKey) {
                        StateMachine sm = new StateMachine();
                        sm.sendEvent(ROUTES.get(commandKey));
                    }
                }
                class StateMachine { void sendEvent(String event) {} }
                """);

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

        HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
        Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph =
                engine.buildCallGraph();

        EntryPoint entryPoint = EntryPoint.builder()
                .className("com.example.GenericCommandController")
                .methodName("execute")
                .name("POST /api/commands/{commandKey}")
                .parameters(List.of(EntryPoint.Parameter.builder()
                        .name("commandKey")
                        .type("String")
                        .annotations(List.of("PathVariable"))
                        .build()))
                .build();

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

        assertThat(variants).extracting(map -> map.get("commandKey"))
                .containsExactlyInAnyOrder("order.pay", "order.ship");
    }
}
