package click.kamil.springstatemachineexporter.analysis.service;

import click.kamil.springstatemachineexporter.analysis.model.CallEdge;
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
import click.kamil.springstatemachineexporter.analysis.resolver.BooleanConstraintEvaluator;
import click.kamil.springstatemachineexporter.ast.common.AstUtils;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.eclipse.jdt.core.dom.*;

import java.util.*;

/**
 * Expands REST entry points whose {@code @PathVariable} parameters flow into string-keyed
 * switch mappers (e.g. {@code StringCommandMapper.fromString}) into concrete binding variants.
 */
public final class EntryPointBindingExpander {

    private EntryPointBindingExpander() {
    }

    /**
     * Expands an entry point into concrete binding variants from path/query parameters:
     * string switch/if arms and boolean ternary conditions provable in source.
     */
    public static List<Map<String, String>> expandEntryPointBindings(
            EntryPoint entryPoint,
            CodebaseContext context,
            Map<String, List<CallEdge>> callGraph) {
        List<Map<String, String>> pathVariants = expandPathVariableBindings(entryPoint, context, callGraph);
        List<Map<String, String>> booleanVariants = expandBooleanTernaryBindings(entryPoint, context, callGraph);
        if (pathVariants.isEmpty()) {
            return booleanVariants;
        }
        if (booleanVariants.isEmpty()) {
            return pathVariants;
        }
        List<Map<String, String>> combined = new ArrayList<>();
        for (Map<String, String> pathVariant : pathVariants) {
            for (Map<String, String> booleanVariant : booleanVariants) {
                Map<String, String> merged = new LinkedHashMap<>(pathVariant);
                merged.putAll(booleanVariant);
                combined.add(merged);
            }
        }
        return combined;
    }

    public static List<Map<String, String>> expandPathVariableBindings(
            EntryPoint entryPoint,
            CodebaseContext context,
            Map<String, List<CallEdge>> callGraph) {
        if (entryPoint == null || entryPoint.getParameters() == null || entryPoint.getParameters().isEmpty()) {
            return List.of();
        }
        List<Map<String, String>> variants = new ArrayList<>();
        variants.add(new LinkedHashMap<>());
        for (EntryPoint.Parameter parameter : entryPoint.getParameters()) {
            if (!isPathOrQueryVariable(parameter)) {
                continue;
            }
            if (isPlaceholderResolvedInPath(entryPoint, parameter.getName())) {
                continue;
            }
            List<Map<String, String>> expanded = new ArrayList<>();
            for (Map<String, String> base : variants) {
                List<String> cases = resolveBindingCasesForParameter(
                        entryPoint.getClassName() + "." + entryPoint.getMethodName(),
                        parameter.getName(),
                        context,
                        callGraph,
                        base);
                if (cases.isEmpty()) {
                    continue;
                }
                for (String caseValue : cases) {
                    Map<String, String> merged = new LinkedHashMap<>(base);
                    merged.put(parameter.getName(), caseValue);
                    expanded.add(merged);
                }
            }
            if (expanded.isEmpty()) {
                continue;
            }
            variants = expanded;
        }
        return variants.stream().filter(map -> !map.isEmpty()).toList();
    }

    public static EntryPoint withResolvedPath(EntryPoint entryPoint, Map<String, String> bindings) {
        if (entryPoint == null || bindings == null || bindings.isEmpty()) {
            return entryPoint;
        }
        String name = entryPoint.getName();
        Map<String, String> metadata = entryPoint.getMetadata() == null
                ? new LinkedHashMap<>()
                : new LinkedHashMap<>(entryPoint.getMetadata());
        StringBuilder querySuffix = new StringBuilder();
        for (Map.Entry<String, String> binding : bindings.entrySet()) {
            String placeholder = "{" + binding.getKey() + "}";
            boolean pathPlaceholder = name != null && name.contains(placeholder);
            String path = metadata.get("path");
            if (!pathPlaceholder && path != null) {
                pathPlaceholder = path.contains(placeholder);
            }
            if (pathPlaceholder) {
                if (name != null) {
                    name = name.replace(placeholder, binding.getValue());
                }
                if (path != null) {
                    metadata.put("path", path.replace(placeholder, binding.getValue()));
                }
            } else if (isBooleanBindingValue(binding.getValue())) {
                if (querySuffix.isEmpty()) {
                    querySuffix.append('?');
                } else {
                    querySuffix.append('&');
                }
                querySuffix.append(binding.getKey()).append('=').append(binding.getValue());
            }
        }
        if (!querySuffix.isEmpty()) {
            if (name != null) {
                name = name + querySuffix;
            }
            String path = metadata.get("path");
            if (path != null) {
                metadata.put("path", path + querySuffix);
            }
        }
        return EntryPoint.builder()
                .type(entryPoint.getType())
                .name(name)
                .className(entryPoint.getClassName())
                .methodName(entryPoint.getMethodName())
                .sourceFile(entryPoint.getSourceFile())
                .metadata(metadata)
                .parameters(entryPoint.getParameters())
                .build();
    }

    private static boolean isPlaceholderResolvedInPath(EntryPoint entryPoint, String paramName) {
        String placeholder = "{" + paramName + "}";
        String name = entryPoint.getName();
        if (name != null && !name.contains(placeholder)) {
            return true;
        }
        Map<String, String> metadata = entryPoint.getMetadata();
        if (metadata != null) {
            String path = metadata.get("path");
            if (path != null && !path.contains(placeholder)) {
                return true;
            }
        }
        return false;
    }

    public static List<Map<String, String>> expandBooleanTernaryBindings(
            EntryPoint entryPoint,
            CodebaseContext context,
            Map<String, List<CallEdge>> callGraph) {
        if (entryPoint == null || entryPoint.getParameters() == null || entryPoint.getParameters().isEmpty()) {
            return List.of();
        }
        for (EntryPoint.Parameter parameter : entryPoint.getParameters()) {
            if (!isBooleanType(parameter.getType())) {
                continue;
            }
            if (hasBooleanTernaryForParameter(
                    entryPoint.getClassName() + "." + entryPoint.getMethodName(),
                    parameter.getName(),
                    context,
                    callGraph)) {
                return List.of(
                        Map.of(parameter.getName(), "true"),
                        Map.of(parameter.getName(), "false"));
            }
        }
        return List.of();
    }

    private static boolean isBooleanType(String type) {
        return "boolean".equals(type) || "Boolean".equals(type);
    }

    private static boolean isBooleanBindingValue(String value) {
        return "true".equals(value) || "false".equals(value);
    }

    private static boolean hasBooleanTernaryForParameter(
            String startMethod,
            String paramName,
            CodebaseContext context,
            Map<String, List<CallEdge>> callGraph) {
        ArrayDeque<String> queue = new ArrayDeque<>();
        Set<String> visited = new HashSet<>();
        queue.add(startMethod);
        while (!queue.isEmpty()) {
            String methodFqn = queue.poll();
            if (!visited.add(methodFqn)) {
                continue;
            }
            if (visited.size() > 12) {
                break;
            }
            if (containsBooleanTernaryCondition(methodFqn, paramName, context)) {
                return true;
            }
            List<CallEdge> edges = callGraph.get(methodFqn);
            if (edges == null) {
                continue;
            }
            for (CallEdge edge : edges) {
                if (edge.getTargetMethod() != null) {
                    queue.add(edge.getTargetMethod());
                }
            }
        }
        return false;
    }

    static boolean containsBooleanTernaryCondition(String methodFqn, String paramName, CodebaseContext context) {
        MethodDeclaration method = findMethodDeclaration(methodFqn, context);
        if (method == null || method.getBody() == null) {
            return false;
        }
        if (!hasParameter(method, paramName)) {
            return false;
        }
        final boolean[] found = {false};
        method.getBody().accept(new ASTVisitor() {
            @Override
            public boolean visit(ConditionalExpression node) {
                if (isBooleanConditionForParameter(node.getExpression(), paramName)) {
                    found[0] = true;
                    return false;
                }
                return super.visit(node);
            }

            @Override
            public boolean visit(MethodInvocation node) {
                for (Object argObj : node.arguments()) {
                    if (argObj instanceof ConditionalExpression cond
                            && isBooleanConditionForParameter(cond.getExpression(), paramName)) {
                        found[0] = true;
                        return false;
                    }
                }
                return super.visit(node);
            }
        });
        return found[0];
    }

    private static boolean isBooleanConditionForParameter(Expression condition, String paramName) {
        if (condition instanceof SimpleName simpleName) {
            return paramName.equals(simpleName.getIdentifier());
        }
        if (condition instanceof PrefixExpression prefixExpression
                && prefixExpression.getOperator() == PrefixExpression.Operator.NOT
                && prefixExpression.getOperand() instanceof SimpleName simpleName) {
            return paramName.equals(simpleName.getIdentifier());
        }
        return false;
    }

    private static boolean isPathOrQueryVariable(EntryPoint.Parameter parameter) {
        if (parameter.getAnnotations() == null) {
            return false;
        }
        return parameter.getAnnotations().stream()
                .anyMatch(a -> "PathVariable".equals(a) || "RequestParam".equals(a));
    }

    private static List<String> resolveBindingCasesForParameter(
            String startMethod,
            String paramName,
            CodebaseContext context,
            Map<String, List<CallEdge>> callGraph,
            Map<String, String> partialBindings) {
        List<String> stringCases = resolveCasesWithParameterAliases(
                startMethod, paramName, context, callGraph, partialBindings,
                EntryPointBindingExpander::extractStringSwitchCasesForBindings);
        if (!stringCases.isEmpty()) {
            return stringCases;
        }
        return resolveCasesWithParameterAliases(
                startMethod, paramName, context, callGraph, partialBindings,
                EntryPointBindingExpander::extractEnumValueOfCasesForBindings);
    }

    @FunctionalInterface
    private interface ParameterCaseExtractor {
        List<String> extract(
                String methodFqn,
                String paramName,
                CodebaseContext context,
                Map<String, String> partialBindings);
    }

    private static List<String> extractStringSwitchCasesForBindings(
            String methodFqn,
            String paramName,
            CodebaseContext context,
            Map<String, String> partialBindings) {
        return extractStringSwitchCases(methodFqn, paramName, context);
    }

    private static List<String> extractEnumValueOfCasesForBindings(
            String methodFqn,
            String paramName,
            CodebaseContext context,
            Map<String, String> partialBindings) {
        return extractEnumValueOfCases(methodFqn, paramName, context, partialBindings);
    }

    private record ParameterAliasState(String methodFqn, String paramName) {
    }

    private static List<String> resolveCasesWithParameterAliases(
            String startMethod,
            String entryParamName,
            CodebaseContext context,
            Map<String, List<CallEdge>> callGraph,
            Map<String, String> partialBindings,
            ParameterCaseExtractor extractor) {
        ArrayDeque<ParameterAliasState> queue = new ArrayDeque<>();
        Set<String> visited = new HashSet<>();
        queue.add(new ParameterAliasState(startMethod, entryParamName));
        while (!queue.isEmpty()) {
            ParameterAliasState state = queue.poll();
            String visitKey = state.methodFqn() + "#" + state.paramName();
            if (!visited.add(visitKey)) {
                continue;
            }
            if (visited.size() > 24) {
                break;
            }
            MethodDeclaration method = findMethodDeclaration(state.methodFqn(), context);
            List<String> bindingNames = bindingNamesForParameter(method, state.paramName());
            for (String bindingName : bindingNames) {
                List<String> localCases = extractor.extract(
                        state.methodFqn(), bindingName, context, partialBindings);
                if (!localCases.isEmpty()) {
                    return localCases;
                }
            }
            enqueueParameterAliasSuccessors(state, context, callGraph, partialBindings, queue);
        }
        return List.of();
    }

    private static List<String> bindingNamesForParameter(MethodDeclaration method, String paramName) {
        if (paramName == null || paramName.isBlank()) {
            return List.of();
        }
        LinkedHashSet<String> names = new LinkedHashSet<>();
        names.add(paramName);
        if (method != null) {
            names.addAll(collectIntraMethodParameterAliases(method, paramName));
        }
        return List.copyOf(names);
    }

    /**
     * Names assigned directly from a parameter within the same method, including transitive renames
     * ({@code String code = event}, {@code String normalized = code.trim()}).
     */
    static Set<String> collectIntraMethodParameterAliases(MethodDeclaration method, String paramName) {
        Set<String> aliases = new LinkedHashSet<>();
        if (method == null || method.getBody() == null || paramName == null || paramName.isBlank()) {
            return aliases;
        }
        Set<String> known = new LinkedHashSet<>();
        known.add(paramName);
        boolean growing = true;
        while (growing) {
            growing = false;
            int before = aliases.size();
            method.getBody().accept(new ASTVisitor() {
                @Override
                public boolean visit(VariableDeclarationFragment node) {
                    Expression initializer = node.getInitializer();
                    if (initializer != null && expressionReferencesKnownBinding(initializer, known)) {
                        aliases.add(node.getName().getIdentifier());
                    }
                    return super.visit(node);
                }

                @Override
                public boolean visit(Assignment node) {
                    if (node.getLeftHandSide() instanceof SimpleName left
                            && expressionReferencesKnownBinding(node.getRightHandSide(), known)) {
                        aliases.add(left.getIdentifier());
                    }
                    return super.visit(node);
                }
            });
            known.addAll(aliases);
            growing = aliases.size() > before;
        }
        return aliases;
    }

    private static boolean expressionReferencesKnownBinding(Expression expression, Set<String> knownNames) {
        if (expression instanceof SimpleName simpleName) {
            return knownNames.contains(simpleName.getIdentifier());
        }
        if (expression instanceof MethodInvocation invocation
                && invocation.arguments().isEmpty()
                && invocation.getExpression() instanceof SimpleName receiver) {
            String methodName = invocation.getName().getIdentifier();
            if (("toUpperCase".equals(methodName) || "toLowerCase".equals(methodName) || "trim".equals(methodName))
                    && knownNames.contains(receiver.getIdentifier())) {
                return true;
            }
        }
        return false;
    }

    private static void enqueueParameterAliasSuccessors(
            ParameterAliasState state,
            CodebaseContext context,
            Map<String, List<CallEdge>> callGraph,
            Map<String, String> partialBindings,
            ArrayDeque<ParameterAliasState> queue) {
        List<CallEdge> edges = callGraph.get(state.methodFqn());
        if (edges == null) {
            return;
        }
        for (CallEdge edge : edges) {
            if (edge.getTargetMethod() == null || !isEdgeCompatibleWithBindings(edge, partialBindings)) {
                continue;
            }
            List<String> args = edge.getArguments();
            if (args == null || args.isEmpty()) {
                continue;
            }
            List<String> targetParamNames = getParameterNames(edge.getTargetMethod(), context);
            if (targetParamNames == null) {
                continue;
            }
            int limit = Math.min(args.size(), targetParamNames.size());
            for (int i = 0; i < limit; i++) {
                if (!argumentForwardsParameter(args.get(i), state.paramName())) {
                    continue;
                }
                queue.add(new ParameterAliasState(edge.getTargetMethod(), targetParamNames.get(i)));
            }
        }
    }

    private static boolean isEdgeCompatibleWithBindings(CallEdge edge, Map<String, String> partialBindings) {
        if (partialBindings == null || partialBindings.isEmpty()) {
            return true;
        }
        String constraint = edge.getConstraint();
        if (constraint == null || constraint.isBlank()) {
            return true;
        }
        return BooleanConstraintEvaluator.isCompatibleWithBindings(constraint, partialBindings);
    }

    private static boolean argumentForwardsParameter(String argument, String paramName) {
        if (argument == null || paramName == null) {
            return false;
        }
        String trimmed = argument.trim();
        if (paramName.equals(trimmed)) {
            return true;
        }
        return ("this." + paramName).equals(trimmed);
    }

    private static List<String> getParameterNames(String methodFqn, CodebaseContext context) {
        MethodDeclaration method = findMethodDeclaration(methodFqn, context);
        if (method == null) {
            return null;
        }
        List<String> names = new ArrayList<>();
        for (Object paramObj : method.parameters()) {
            if (paramObj instanceof SingleVariableDeclaration param) {
                names.add(param.getName().getIdentifier());
            }
        }
        return names;
    }

    static List<String> extractEnumValueOfCases(
            String methodFqn,
            String paramName,
            CodebaseContext context,
            Map<String, String> partialBindings) {
        MethodDeclaration method = findMethodDeclaration(methodFqn, context);
        if (method == null || method.getBody() == null || context == null) {
            return List.of();
        }
        if (!tracksBindingName(method, paramName)) {
            return List.of();
        }
        LinkedHashSet<String> enumTypes = new LinkedHashSet<>();
        method.getBody().accept(new ASTVisitor() {
            @Override
            public boolean visit(MethodInvocation node) {
                if (!"valueOf".equals(node.getName().getIdentifier()) || node.arguments().size() != 1) {
                    return super.visit(node);
                }
                Expression argument = (Expression) node.arguments().get(0);
                if (!argumentReferencesParameter(argument, paramName, method, context)) {
                    return super.visit(node);
                }
                if (!isValueOfSiteCompatibleWithBindings(node, partialBindings)) {
                    return super.visit(node);
                }
                String enumType = resolveValueOfEnumType(node, context);
                if (enumType != null) {
                    enumTypes.add(enumType);
                }
                return super.visit(node);
            }
        });
        if (enumTypes.size() != 1) {
            return List.of();
        }
        List<String> constants = context.getEnumValues(enumTypes.iterator().next());
        if (constants == null || constants.isEmpty()) {
            return List.of();
        }
        LinkedHashSet<String> pathValues = new LinkedHashSet<>();
        for (String constant : constants) {
            if (constant == null || constant.isBlank()) {
                continue;
            }
            int lastDot = constant.lastIndexOf('.');
            pathValues.add(lastDot >= 0 ? constant.substring(lastDot + 1) : constant);
        }
        return List.copyOf(pathValues);
    }

    private static boolean isValueOfSiteCompatibleWithBindings(
            MethodInvocation valueOfInvocation,
            Map<String, String> partialBindings) {
        if (partialBindings == null || partialBindings.isEmpty()) {
            return true;
        }
        String branchConstraint = AstUtils.findConditionConstraint(valueOfInvocation);
        if (branchConstraint == null || branchConstraint.isBlank()) {
            return true;
        }
        return BooleanConstraintEvaluator.isCompatibleWithBindings(branchConstraint, partialBindings);
    }

    private static boolean argumentReferencesParameter(Expression argument, String paramName) {
        return argumentReferencesParameter(argument, paramName, null, null);
    }

    private static boolean argumentReferencesParameter(
            Expression argument,
            String paramName,
            MethodDeclaration ownerMethod,
            CodebaseContext context) {
        if (argument instanceof SimpleName simpleName) {
            return paramName.equals(simpleName.getIdentifier());
        }
        if (argument instanceof MethodInvocation invocation
                && invocation.arguments().isEmpty()
                && invocation.getExpression() instanceof SimpleName simpleName) {
            String methodName = invocation.getName().getIdentifier();
            if (("toUpperCase".equals(methodName) || "toLowerCase".equals(methodName) || "trim".equals(methodName))
                    && paramName.equals(simpleName.getIdentifier())) {
                return true;
            }
        }
        if (argument instanceof MethodInvocation invocation
                && ownerMethod != null
                && context != null
                && isSingleArgHelperForwardingParameter(invocation, paramName, ownerMethod, context)) {
            return true;
        }
        return false;
    }

    private static boolean isSingleArgHelperForwardingParameter(
            MethodInvocation helperInvocation,
            String paramName,
            MethodDeclaration ownerMethod,
            CodebaseContext context) {
        if (helperInvocation.arguments().size() != 1) {
            return false;
        }
        Expression forwarded = (Expression) helperInvocation.arguments().get(0);
        if (!argumentReferencesParameter(forwarded, paramName, ownerMethod, context)) {
            return false;
        }
        MethodDeclaration helper = findHelperMethod(ownerMethod, helperInvocation, context);
        return helper != null && isTrivialSingleArgStringHelper(helper);
    }

    private static MethodDeclaration findHelperMethod(
            MethodDeclaration ownerMethod,
            MethodInvocation helperInvocation,
            CodebaseContext context) {
        String helperName = helperInvocation.getName().getIdentifier();
        CompilationUnit compilationUnit = ownerMethod.getRoot() instanceof CompilationUnit cu ? cu : null;
        Expression receiver = helperInvocation.getExpression();
        if (receiver == null) {
            MethodDeclaration ownerHelper = findInstanceHelperOnOwnerType(ownerMethod, helperName, context);
            if (ownerHelper != null) {
                return ownerHelper;
            }
            return findStaticImportHelper(helperName, compilationUnit, context);
        }
        TypeDeclaration declaringType = resolveHelperDeclaringType(receiver, compilationUnit, context);
        if (declaringType == null) {
            return null;
        }
        return context.findMethodDeclaration(declaringType, helperName, true);
    }

    private static MethodDeclaration findInstanceHelperOnOwnerType(
            MethodDeclaration ownerMethod,
            String helperName,
            CodebaseContext context) {
        ASTNode parent = ownerMethod.getParent();
        if (!(parent instanceof TypeDeclaration typeDeclaration)) {
            return null;
        }
        return context.findMethodDeclaration(typeDeclaration, helperName, true);
    }

    private static MethodDeclaration findStaticImportHelper(
            String helperName,
            CompilationUnit compilationUnit,
            CodebaseContext context) {
        if (compilationUnit == null || helperName == null || helperName.isBlank()) {
            return null;
        }
        for (Object importObject : compilationUnit.imports()) {
            if (!(importObject instanceof ImportDeclaration importDeclaration) || !importDeclaration.isStatic()) {
                continue;
            }
            String importedName = importDeclaration.getName().getFullyQualifiedName();
            if (importDeclaration.isOnDemand()) {
                MethodDeclaration helper = findHelperOnType(importedName, helperName, context);
                if (helper != null) {
                    return helper;
                }
                continue;
            }
            if (!importedName.endsWith("." + helperName)) {
                continue;
            }
            String className = importedName.substring(0, importedName.lastIndexOf('.'));
            return findHelperOnType(className, helperName, context);
        }
        return null;
    }

    private static MethodDeclaration findHelperOnType(
            String typeName,
            String helperName,
            CodebaseContext context) {
        TypeDeclaration typeDeclaration = context.getTypeDeclaration(typeName);
        if (typeDeclaration == null) {
            return null;
        }
        return context.findMethodDeclaration(typeDeclaration, helperName, true);
    }

    private static TypeDeclaration resolveHelperDeclaringType(
            Expression receiver,
            CompilationUnit compilationUnit,
            CodebaseContext context) {
        org.eclipse.jdt.core.dom.ITypeBinding binding = receiver.resolveTypeBinding();
        if (binding != null) {
            TypeDeclaration typeDeclaration = context.getTypeDeclaration(binding.getQualifiedName());
            if (typeDeclaration != null) {
                return typeDeclaration;
            }
        }
        if (receiver instanceof QualifiedName qualifiedName) {
            return context.getTypeDeclaration(qualifiedName.getFullyQualifiedName());
        }
        if (receiver instanceof SimpleName simpleName) {
            return context.getTypeDeclaration(simpleName.getIdentifier(), compilationUnit);
        }
        return null;
    }

    private static boolean isTrivialSingleArgStringHelper(MethodDeclaration helper) {
        if (helper.parameters().size() != 1 || helper.getBody() == null) {
            return false;
        }
        String helperParam = null;
        Object paramObj = helper.parameters().get(0);
        if (paramObj instanceof SingleVariableDeclaration param) {
            helperParam = param.getName().getIdentifier();
        }
        if (helperParam == null) {
            return false;
        }
        Set<String> known = new LinkedHashSet<>();
        known.add(helperParam);
        known.addAll(collectIntraMethodParameterAliases(helper, helperParam));
        final boolean[] returnsKnownBinding = {false};
        helper.getBody().accept(new ASTVisitor() {
            @Override
            public boolean visit(ReturnStatement node) {
                if (expressionReferencesKnownBinding(node.getExpression(), known)) {
                    returnsKnownBinding[0] = true;
                }
                return super.visit(node);
            }
        });
        return returnsKnownBinding[0];
    }

    private static String resolveValueOfEnumType(MethodInvocation valueOfInvocation, CodebaseContext context) {
        Expression receiver = valueOfInvocation.getExpression();
        if (receiver != null) {
            org.eclipse.jdt.core.dom.ITypeBinding binding = receiver.resolveTypeBinding();
            if (binding != null && binding.isEnum()) {
                return binding.getQualifiedName();
            }
        }
        if (receiver instanceof QualifiedName qualifiedName) {
            return qualifiedName.getFullyQualifiedName();
        }
        if (receiver instanceof SimpleName simpleName && context != null) {
            List<String> values = context.getEnumValues(simpleName.getIdentifier());
            if (values != null && !values.isEmpty()) {
                return simpleName.getIdentifier();
            }
        }
        return null;
    }

    static List<String> extractStringSwitchCases(String methodFqn, String paramName, CodebaseContext context) {
        MethodDeclaration method = findMethodDeclaration(methodFqn, context);
        if (method == null || method.getBody() == null) {
            return List.of();
        }
        if (!tracksBindingName(method, paramName)) {
            return List.of();
        }
        LinkedHashSet<String> cases = new LinkedHashSet<>();
        method.getBody().accept(new ASTVisitor() {
            @Override
            public boolean visit(SwitchStatement node) {
                collectCasesFromSwitch(node.getExpression(), node.statements(), paramName, cases, method, context);
                return super.visit(node);
            }

            @Override
            public boolean visit(SwitchExpression node) {
                collectCasesFromSwitch(node.getExpression(), node.statements(), paramName, cases, method, context);
                return super.visit(node);
            }

            @Override
            public boolean visit(IfStatement node) {
                collectCasesFromIfChain(node, paramName, cases, method, context);
                return super.visit(node);
            }
        });
        return List.copyOf(cases);
    }

    private static void collectCasesFromSwitch(
            Expression selector,
            List<?> statements,
            String paramName,
            Set<String> cases,
            MethodDeclaration ownerMethod,
            CodebaseContext context) {
        if (!switchSelectorReferencesParameter(selector, paramName, ownerMethod, context)) {
            return;
        }
        for (Object statementObj : statements) {
            if (statementObj instanceof SwitchCase switchCase && !switchCase.isDefault()) {
                for (Object exprObj : switchCase.expressions()) {
                    if (exprObj instanceof StringLiteral stringLiteral) {
                        cases.add(stringLiteral.getLiteralValue());
                    }
                }
            }
        }
    }

    private static boolean switchSelectorReferencesParameter(
            Expression selector,
            String paramName,
            MethodDeclaration ownerMethod,
            CodebaseContext context) {
        if (selector instanceof SimpleName simpleName) {
            return paramName.equals(simpleName.getIdentifier());
        }
        if (ownerMethod != null && context != null && selector instanceof MethodInvocation invocation) {
            return isSingleArgHelperForwardingParameter(invocation, paramName, ownerMethod, context);
        }
        return false;
    }

    private static void collectCasesFromIfChain(
            IfStatement ifStatement,
            String paramName,
            Set<String> cases,
            MethodDeclaration ownerMethod,
            CodebaseContext context) {
        IfStatement current = ifStatement;
        while (current != null) {
            String literal = extractEqualsLiteralForParameter(
                    current.getExpression(), paramName, ownerMethod, context);
            if (literal != null) {
                cases.add(literal);
            }
            Statement elseStatement = current.getElseStatement();
            if (elseStatement instanceof IfStatement nestedIf) {
                current = nestedIf;
            } else {
                if (elseStatement != null) {
                    String elseLiteral = extractEqualsLiteralForParameterFromStatement(
                            elseStatement, paramName, ownerMethod, context);
                    if (elseLiteral != null) {
                        cases.add(elseLiteral);
                    }
                }
                break;
            }
        }
    }

    private static String extractEqualsLiteralForParameterFromStatement(
            Statement statement,
            String paramName,
            MethodDeclaration ownerMethod,
            CodebaseContext context) {
        if (statement instanceof IfStatement ifStatement) {
            return extractEqualsLiteralForParameter(
                    ifStatement.getExpression(), paramName, ownerMethod, context);
        }
        return null;
    }

    private static String extractEqualsLiteralForParameter(Expression expression, String paramName) {
        return extractEqualsLiteralForParameter(expression, paramName, null, null);
    }

    private static String extractEqualsLiteralForParameter(
            Expression expression,
            String paramName,
            MethodDeclaration ownerMethod,
            CodebaseContext context) {
        if (!(expression instanceof MethodInvocation invocation)) {
            return null;
        }
        String methodName = invocation.getName().getIdentifier();
        if (!"equals".equals(methodName) && !"equalsIgnoreCase".equals(methodName)) {
            return null;
        }
        if (invocation.arguments().size() != 1) {
            return null;
        }
        Expression argument = (Expression) invocation.arguments().get(0);
        Expression receiver = invocation.getExpression();

        if (receiver instanceof StringLiteral stringLiteral
                && argument instanceof SimpleName simpleName
                && paramName.equals(simpleName.getIdentifier())) {
            return stringLiteral.getLiteralValue();
        }
        if (receiver instanceof StringLiteral stringLiteral
                && ownerMethod != null
                && context != null
                && switchSelectorReferencesParameter(argument, paramName, ownerMethod, context)) {
            return stringLiteral.getLiteralValue();
        }
        if (receiver instanceof SimpleName simpleName
                && paramName.equals(simpleName.getIdentifier())
                && argument instanceof StringLiteral paramLiteral) {
            return paramLiteral.getLiteralValue();
        }
        if (ownerMethod != null
                && context != null
                && switchSelectorReferencesParameter(receiver, paramName, ownerMethod, context)
                && argument instanceof StringLiteral paramLiteral) {
            return paramLiteral.getLiteralValue();
        }
        return null;
    }

    private static boolean tracksBindingName(MethodDeclaration method, String name) {
        if (hasParameter(method, name)) {
            return true;
        }
        for (Object paramObj : method.parameters()) {
            if (paramObj instanceof SingleVariableDeclaration param) {
                String paramName = param.getName().getIdentifier();
                if (collectIntraMethodParameterAliases(method, paramName).contains(name)) {
                    return true;
                }
            }
        }
        return false;
    }

    private static boolean hasParameter(MethodDeclaration method, String paramName) {
        for (Object paramObj : method.parameters()) {
            if (paramObj instanceof SingleVariableDeclaration param
                    && paramName.equals(param.getName().getIdentifier())) {
                return true;
            }
        }
        return false;
    }

    private static MethodDeclaration findMethodDeclaration(String methodFqn, CodebaseContext context) {
        if (methodFqn == null || !methodFqn.contains(".")) {
            return null;
        }
        String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
        String methodName = methodFqn.substring(methodFqn.lastIndexOf('.') + 1);
        TypeDeclaration typeDeclaration = context.getTypeDeclaration(className);
        if (typeDeclaration == null) {
            return null;
        }
        return context.findMethodDeclaration(typeDeclaration, methodName, true);
    }
}
