package click.kamil.springstatemachineexporter.analysis.resolver;

import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jdt.core.dom.*;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.LinkedHashSet;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Slf4j
public class ConstantResolver {

    public String resolve(Expression expr, CodebaseContext context) {
        return resolveInternal(expr, context, new HashSet<>());
    }

    /**
     * Resolves {@code map.get(key)} when the map receiver and key are statically known.
     */
    public String resolveKeyedMapLookup(MethodInvocation getCall, CodebaseContext context) {
        if (getCall == null || getCall.getExpression() == null || getCall.arguments().isEmpty()) {
            return null;
        }
        String methodName = getCall.getName().getIdentifier();
        if (!"get".equals(methodName) && !"getOrDefault".equals(methodName)) {
            return null;
        }
        return LiteralExpressionSupport.resolveMapGet(
                getCall, context, new HashSet<>(), (e, v) -> resolveInternal(e, context, v));
    }

    /**
     * Resolves a value from an encoded {@code MAP:...} literal or map-typed expression at {@code key}.
     */
    public String resolveKeyedMapLookup(Expression mapExpr, String key, CodebaseContext context) {
        if (mapExpr == null || key == null) {
            return null;
        }
        String encoded = resolveInternal(mapExpr, context, new HashSet<>());
        if (encoded == null || !encoded.startsWith("MAP:")) {
            return null;
        }
        Map<String, String> entries = LiteralExpressionSupport.parseMapLiteral(encoded);
        return entries != null ? entries.get(key) : null;
    }

    public static List<String> decodeMapLiteralValues(String encoded) {
        return LiteralExpressionSupport.mapLiteralValues(encoded);
    }

    public String evaluateSwitchWithParams(org.eclipse.jdt.core.dom.SwitchExpression se, java.util.Map<String, String> paramValues, CodebaseContext context) {
        return evaluateSwitchExpression(se, paramValues, context, new HashSet<>());
    }

    public String evaluateSwitchWithParams(org.eclipse.jdt.core.dom.SwitchStatement ss, java.util.Map<String, String> paramValues, CodebaseContext context) {
        return evaluateSwitchStatement(ss, paramValues, context, new HashSet<>());
    }

    /** Unified switch eval for expression or statement nodes. */
    public String evaluateSwitch(ASTNode switchNode, java.util.Map<String, String> paramValues, CodebaseContext context) {
        if (switchNode instanceof org.eclipse.jdt.core.dom.SwitchExpression se) {
            return evaluateSwitchWithParams(se, paramValues, context);
        }
        if (switchNode instanceof org.eclipse.jdt.core.dom.SwitchStatement ss) {
            return evaluateSwitchWithParams(ss, paramValues, context);
        }
        return null;
    }

    /**
     * Resolves the first return expression of {@code md} under {@code callBindings}
     * (switch returns go through {@link #evaluateSwitch}).
     */
    public String evaluateMethodReturn(
            MethodDeclaration md, java.util.Map<String, String> callBindings, CodebaseContext context) {
        if (md == null || md.getBody() == null) {
            return null;
        }
        final Expression[] returnExpr = new Expression[1];
        md.getBody().accept(new ASTVisitor() {
            @Override
            public boolean visit(ReturnStatement node) {
                if (returnExpr[0] == null && node.getExpression() != null) {
                    returnExpr[0] = node.getExpression();
                }
                return false;
            }
        });
        if (returnExpr[0] == null) {
            return null;
        }
        Expression ret = returnExpr[0];
        while (ret instanceof ParenthesizedExpression pe) {
            ret = pe.getExpression();
        }
        java.util.Map<String, String> params = callBindings != null ? callBindings : java.util.Map.of();
        if (ret instanceof org.eclipse.jdt.core.dom.SwitchExpression se) {
            return evaluateSwitchWithParams(se, params, context);
        }
        context.setAnalysisNamedBindings(params);
        try {
            return resolve(ret, context);
        } finally {
            context.clearAnalysisNamedBindings();
        }
    }

    private String resolveInternal(Expression expr, CodebaseContext context, Set<String> visited) {
        if (expr == null) return null;

        // Unwrap common wrappers
        if (expr instanceof CastExpression ce) {
            return resolveInternal(ce.getExpression(), context, visited);
        }
        if (expr instanceof ParenthesizedExpression pe) {
            return resolveInternal(pe.getExpression(), context, visited);
        }
        if (expr instanceof org.eclipse.jdt.core.dom.SwitchExpression se) {
            return evaluateSwitchExpression(se, java.util.Map.of(), context, visited);
        }
        if (expr instanceof ArrayAccess access) {
            String indexed = LiteralExpressionSupport.resolveArrayAccess(
                    access, context, visited, (e, v) -> resolveInternal(e, context, v));
            if (indexed != null) {
                return indexed;
            }
        }
        if (expr instanceof ArrayCreation creation && creation.getInitializer() != null) {
            List<String> elements = extractArrayElementsFromCreation(creation, context, visited);
            if (elements != null) {
                return LiteralExpressionSupport.encodeArrayLiteral(elements);
            }
        }
        if (expr instanceof ArrayInitializer initializer) {
            List<String> elements = extractArrayElementsFromInitializer(initializer, context, visited);
            if (elements != null) {
                return LiteralExpressionSupport.encodeArrayLiteral(elements);
            }
        }
        if (expr instanceof MethodInvocation mi && (mi.getName().getIdentifier().equals("requireNonNull") || mi.getName().getIdentifier().equals("notNull"))) {
            if (!mi.arguments().isEmpty()) {
                return resolveInternal((Expression) mi.arguments().get(0), context, visited);
            }
        }

        // 1. Literal?
        if (expr instanceof StringLiteral sl) {
            return sl.getLiteralValue();
        }
        if (expr instanceof NumberLiteral nl) {
            return nl.getToken();
        }
        if (expr instanceof BooleanLiteral bl) {
            return String.valueOf(bl.booleanValue());
        }

        // 2. JDT Bindings? (Most robust)
        IVariableBinding binding = null;
        if (expr instanceof SimpleName sn) {
            IBinding b = sn.resolveBinding();
            if (b instanceof IVariableBinding vb) binding = vb;
        } else if (expr instanceof QualifiedName qn) {
            IBinding b = qn.resolveBinding();
            if (b instanceof IVariableBinding vb) binding = vb;
        }

        if (binding != null && binding.isField()) {
            Object value = binding.getConstantValue();
            if (value != null) return value.toString();
        }

        // 3. Fallback: Manual AST Traversal (if bindings not available or resolved)
        if (expr instanceof InfixExpression infix) {
            return resolveInfix(infix, context, visited);
        }
        if (expr instanceof QualifiedName qn) {
            String val = resolveManual(qn, context, visited);
            return val != null ? val : qn.toString();
        }
        if (expr instanceof FieldAccess fa) {
            String staticField = resolveStaticFieldAccess(fa, context, visited);
            if (staticField != null) {
                return staticField;
            }
            return resolveManual(fa.getName(), context, visited);
        }
        if (expr instanceof SimpleName sn) {
            return resolveManual(sn, context, visited);
        }

        
        if (expr instanceof MethodInvocation mi) {
            String methodName = mi.getName().getIdentifier();
            if ("get".equals(methodName) || "getOrDefault".equals(methodName)) {
                String mapValue = LiteralExpressionSupport.resolveMapGet(
                        mi, context, visited, (e, v) -> resolveInternal(e, context, v));
                if (mapValue != null) {
                    return mapValue;
                }
            }
            String transformed = LiteralExpressionSupport.resolveLiteralStringTransform(
                    mi, visited, (e, v) -> resolveInternal(e, context, v));
            if (transformed != null) {
                return transformed;
            }
            java.util.Map<String, String> mapEntries = LiteralExpressionSupport.extractMapEntries(
                    mi, context, visited, (e, v) -> resolveInternal(e, context, v));
            if (mapEntries != null && isMapFactoryInvocation(mi)) {
                return LiteralExpressionSupport.encodeMapLiteral(mapEntries);
            }

            if ((methodName.equals("name") || methodName.equals("toString")) && mi.arguments().isEmpty()) {
                String val = resolveInternal(mi.getExpression(), context, visited);
                if (val != null) {
                    // If it was resolved as a fully qualified enum string like "MyEvents.ORDER_PAID", strip it
                    if (val.contains(".")) {
                        return val.substring(val.lastIndexOf('.') + 1);
                    }
                    return val;
                }
                
                // Fallback for unresolved AST nodes
                if (mi.getExpression() instanceof QualifiedName qn) {
                    return qn.getName().getIdentifier();
                } else if (mi.getExpression() instanceof SimpleName sn) {
                    return sn.getIdentifier();
                }
            }
                
            if ("valueOf".equals(mi.getName().getIdentifier())) {
                String enumFqn = null;
                Expression arg = null;
                if (mi.arguments().size() == 2) {
                    enumFqn = resolveEnumFqn((Expression) mi.arguments().get(0), context);
                    arg = (Expression) mi.arguments().get(1);
                } else if (mi.arguments().size() == 1 && mi.getExpression() != null) {
                    enumFqn = resolveEnumFqn(mi.getExpression(), context);
                    arg = (Expression) mi.arguments().get(0);
                }
                
                if (enumFqn != null) {
                    if (arg != null) {
                        String resolvedArg = resolveInternal(arg, context, visited);
                        if (resolvedArg != null && resolvedArg.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
                            return enumFqn + "." + resolvedArg;
                        }
                    }
                    return "<SYMBOLIC: " + enumFqn + ".*>";
                }
            }
            
            IMethodBinding mb = mi.resolveMethodBinding();
            if (mb != null) {
                if (!java.lang.reflect.Modifier.isStatic(mb.getModifiers())) {
                    Expression receiver = mi.getExpression();
                    if (!(receiver instanceof ClassInstanceCreation)) {
                        return null;
                    }
                }

            } else {
                TypeDeclaration td = null;
                if (mi.getExpression() != null) {
                    ITypeBinding typeBinding = mi.getExpression().resolveTypeBinding();
                    if (typeBinding != null) {
                        String typeName = typeBinding.getQualifiedName();
                        if (typeName != null && !typeName.isEmpty()) {
                            td = context.getTypeDeclaration(typeName);
                        }
                    }
                }
                
                if (td == null && mi.getExpression() instanceof SimpleName sn) {
                    String typeName = resolveLocalType(sn);
                    if (typeName != null) {
                        td = context.getTypeDeclaration(typeName);
                        if (td == null) {
                            CompilationUnit cu = (CompilationUnit) mi.getRoot();
                            td = context.getTypeDeclaration(typeName, cu);
                        }
                    }
                }
                
                if (td == null) {
                    td = findEnclosingType(mi);
                }
                
                if (td != null) {
                    MethodDeclaration md = context.findMethodDeclaration(td, mi.getName().getIdentifier(), true);
                    if (md != null) {
                        String evaluated = evaluateMethodOutput(mi, md, context, visited);
                        if (evaluated != null) return evaluated;
                        if (mi.getName().getIdentifier().startsWith("get") && md.getReturnType2() != null) {
                            java.util.List<String> values = context.getEnumValues(md.getReturnType2().toString());
                            if (values != null && !values.isEmpty()) {
                                return "ENUM_SET:" + String.join(",", values);
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    private String evaluateMethodOutput(MethodInvocation mi, MethodDeclaration md, CodebaseContext context, Set<String> visited) {
        if (md.getBody() == null || mi.arguments().size() != md.parameters().size()) return null;

        TypeDeclaration td = findEnclosingType(md);
        if (td == null) return null;
        String methodFqn = context.getFqn(td) + "." + md.getName().getIdentifier();
        
        if (!visited.add(methodFqn)) {
            if (log.isDebugEnabled()) {
                log.debug("Cycle detected in method evaluation for: {} (visited={})", methodFqn, visited);
            }
            return null; // Cycle detected in method evaluation
        }

        try {
            java.util.Map<String, String> localVars = new java.util.HashMap<>();
            java.util.Set<String> declaredLocals = new java.util.HashSet<>();

            for (int i = 0; i < mi.arguments().size(); i++) {
                Expression arg = (Expression) mi.arguments().get(i);
                String val = resolveInternal(arg, context, visited);
                if (val != null) {
                    org.eclipse.jdt.core.dom.SingleVariableDeclaration param =
                            (org.eclipse.jdt.core.dom.SingleVariableDeclaration) md.parameters().get(i);
                    String paramName = param.getName().getIdentifier();
                    localVars.put(paramName, val);
                    declaredLocals.add(paramName);
                }
            }

            return evaluateBody(md.getBody(), localVars, declaredLocals, context, visited);
        } finally {
            visited.remove(methodFqn);
        }
    }

    /**
     * Evaluates a method body with pre-supplied local values (e.g. field values derived
     * from the specific {@code ClassInstanceCreation} that created the receiver object).
     * Used by {@link click.kamil.springstatemachineexporter.analysis.service.AbstractCallGraphEngine}
     * to resolve transforming getters — methods that map a constructor-injected enum to a
     * different state-machine enum via switch expressions.
     */
    public String evaluateMethodBodyWithLocals(MethodDeclaration md,
                                        java.util.Map<String, String> prebuiltLocals,
                                        CodebaseContext context,
                                        Set<String> visited) {
        if (md.getBody() == null) return null;
        
        TypeDeclaration td = findEnclosingType(md);
        if (td == null) return null;
        String methodFqn = context.getFqn(td) + "." + md.getName().getIdentifier();
        
        if (!visited.add(methodFqn)) {
            if (log.isDebugEnabled()) {
                log.debug("Cycle detected in method body evaluation for: {} (visited={})", methodFqn, visited);
            }
            return null; // Cycle detected in method evaluation
        }

        try {
            java.util.Set<String> declaredLocals = new java.util.HashSet<>(prebuiltLocals.keySet());
            return evaluateBody(md.getBody(), prebuiltLocals, declaredLocals, context, visited);
        } finally {
            visited.remove(methodFqn);
        }
    }

    /**
     * Core body-evaluation logic shared by {@link #evaluateMethodOutput} and
     * {@link #evaluateMethodBodyWithLocals}.  Visits the block, tracks assignments,
     * evaluates switch statements/expressions, and captures the first resolved return value.
     */
    private String evaluateBody(org.eclipse.jdt.core.dom.Block body,
                                 java.util.Map<String, String> localVars,
                                 java.util.Set<String> declaredLocals,
                                 CodebaseContext context,
                                 Set<String> visited) {
        String[] finalResult = new String[1];

        body.accept(new ASTVisitor() {
            @Override
            public boolean visit(VariableDeclarationStatement vds) {
                for (Object fragmentObj : vds.fragments()) {
                    if (fragmentObj instanceof VariableDeclarationFragment fragment) {
                        String varName = fragment.getName().getIdentifier();
                        declaredLocals.add(varName);
                        if (fragment.getInitializer() != null) {
                            String rhsVal = resolveExpressionWithParams(fragment.getInitializer(), localVars, context, visited);
                            if (rhsVal == null) rhsVal = resolveInternal(fragment.getInitializer(), context, visited);
                            if (rhsVal != null) {
                                localVars.put(varName, rhsVal);
                            } else {
                                java.util.Map<String, String> bFields = extractBuilderFields(fragment.getInitializer(), localVars, context, visited);
                                if (bFields != null) {
                                    for (java.util.Map.Entry<String, String> e : bFields.entrySet()) {
                                        localVars.put(varName + "." + e.getKey(), e.getValue());
                                    }
                                }
                            }
                        }
                    }
                }
                return super.visit(vds);
            }

            @Override
            public boolean visit(org.eclipse.jdt.core.dom.MethodInvocation node) {
                MethodDeclaration sideEffectMd = findInvokedMethod(node, context);
                if (sideEffectMd != null && sideEffectMd.getBody() != null) {
                    TypeDeclaration currentTd = findEnclosingType(sideEffectMd);
                    if (sideEffectMd != null && sideEffectMd.getBody() != null) {
                        java.util.Map<String, String> inlineLocals = new java.util.HashMap<>();
                        for (java.util.Map.Entry<String, String> entry : localVars.entrySet()) {
                            if (entry.getKey().startsWith("this.")) {
                                inlineLocals.put(entry.getKey(), entry.getValue());
                            } else if (context.hasField(currentTd, entry.getKey())) {
                                inlineLocals.put(entry.getKey(), entry.getValue());
                                inlineLocals.put("this." + entry.getKey(), entry.getValue());
                            }
                        }
                        for (int i = 0; i < node.arguments().size() && i < sideEffectMd.parameters().size(); i++) {
                            Expression arg = (Expression) node.arguments().get(i);
                            String val = resolveExpressionWithParams(arg, localVars, context, visited);
                            if (val == null) val = resolveInternal(arg, context, visited);
                            if (val != null) {
                                org.eclipse.jdt.core.dom.SingleVariableDeclaration param = (org.eclipse.jdt.core.dom.SingleVariableDeclaration) sideEffectMd.parameters().get(i);
                                inlineLocals.put(param.getName().getIdentifier(), val);
                            }
                        }
                        evaluateMethodBodyWithLocals(sideEffectMd, inlineLocals, context, visited);
                        for (java.util.Map.Entry<String, String> entry : inlineLocals.entrySet()) {
                            if (entry.getKey().startsWith("this.")) {
                                localVars.put(entry.getKey(), entry.getValue());
                                String bareName = entry.getKey().substring(5);
                                if (!declaredLocals.contains(bareName)) localVars.put(bareName, entry.getValue());
                            } else {
                                localVars.put(entry.getKey(), entry.getValue());
                                if (!declaredLocals.contains(entry.getKey())) localVars.put("this." + entry.getKey(), entry.getValue());
                            }
                        }
                    }
                }
                return false;
            }

            @Override
            public boolean visit(Assignment assignment) {
                Expression lhs = assignment.getLeftHandSide();
                String varName = null;
                if (lhs instanceof SimpleName sn) {
                    varName = sn.getIdentifier();
                } else if (lhs instanceof FieldAccess fa) {
                    varName = "this." + fa.getName().getIdentifier();
                }
                if (varName != null) {
                    String rhsVal = resolveExpressionWithParams(assignment.getRightHandSide(), localVars, context, visited);
                    if (rhsVal == null) rhsVal = resolveInternal(assignment.getRightHandSide(), context, visited);
                    if (rhsVal != null) {
                        localVars.put(varName, rhsVal);
                    } else {
                        java.util.Map<String, String> bFields = extractBuilderFields(assignment.getRightHandSide(), localVars, context, visited);
                        if (bFields != null) {
                            for (java.util.Map.Entry<String, String> e : bFields.entrySet()) {
                                localVars.put(varName + "." + e.getKey(), e.getValue());
                            }
                        }
                    }
                    if (varName.startsWith("this.")) {
                        localVars.put(varName, rhsVal);
                        String bareName = varName.substring(5);
                        if (!declaredLocals.contains(bareName)) localVars.put(bareName, rhsVal);
                    } else {
                        localVars.put(varName, rhsVal);
                        if (!declaredLocals.contains(varName)) localVars.put("this." + varName, rhsVal);
                    }
                }
                return super.visit(assignment);
            }

            @Override
            public boolean visit(org.eclipse.jdt.core.dom.SwitchStatement ss) {
                if (finalResult[0] == null) {
                    String result = evaluateSwitchStatement(ss, localVars, context, visited);
                    if (result != null) finalResult[0] = result;
                }
                return false; // children handled inside evaluateSwitchStatement
            }

            @Override
            public boolean visit(org.eclipse.jdt.core.dom.ReturnStatement rs) {
                if (finalResult[0] == null) {
                    if (rs.getExpression() instanceof org.eclipse.jdt.core.dom.SwitchExpression se) {
                        String result = evaluateSwitchExpression(se, localVars, context, visited);
                        if (result != null) finalResult[0] = result;
                    } else if (rs.getExpression() != null) {
                        String result = resolveExpressionWithParams(rs.getExpression(), localVars, context, visited);
                        if (result == null) result = resolveInternal(rs.getExpression(), context, visited);
                        if (result != null) finalResult[0] = result;
                    }
                }
                return super.visit(rs);
            }
        });

        return finalResult[0];
    }

    private String evaluateSwitchStatement(org.eclipse.jdt.core.dom.SwitchStatement ss, java.util.Map<String, String> paramValues, CodebaseContext context, Set<String> visited) {
        String switchVar = concreteSwitchSelector(
                resolveExpressionWithParams(ss.getExpression(), paramValues, context, visited),
                ss.getExpression());
        if (switchVar == null) {
            if (click.kamil.springstatemachineexporter.analysis.pipeline.SwitchConstSupport.hasOpaqueArm(ss)) {
                return null;
            }
            Set<String> values = new LinkedHashSet<>();
            for (Expression armExpr : click.kamil.springstatemachineexporter.analysis.pipeline.SwitchConstSupport.armPayloadExpressions(ss)) {
                String val = resolveInternal(armExpr, context, visited);
                if (val != null) {
                    if (val.startsWith("ENUM_SET:")) {
                        values.addAll(Arrays.asList(val.substring(9).split(",")));
                    } else {
                        values.add(val);
                    }
                }
            }
            if (!values.isEmpty()) {
                return click.kamil.springstatemachineexporter.ast.common.AstUtils.formatEnumSet(values);
            }
            return null;
        }

        String switchType = null;
        if (switchVar.contains(".")) {
            switchType = switchVar.substring(0, switchVar.lastIndexOf('.'));
        }

        boolean matchingCase = false;
        boolean matchedNonDefault = false;
        for (Object stmtObj : ss.statements()) {
            if (stmtObj instanceof org.eclipse.jdt.core.dom.SwitchCase sc) {
                matchingCase = false;
                if (sc.isDefault()) {
                    matchingCase = !matchedNonDefault;
                } else {
                    for (Object exprObj : sc.expressions()) {
                        String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues, context, visited);
                        // Bare SimpleName case label (e.g. "case PAY"): use identifier as fallback
                        if (caseVal == null && exprObj instanceof org.eclipse.jdt.core.dom.SimpleName caseSn) {
                            caseVal = caseSn.getIdentifier();
                            if (switchType != null) {
                                  caseVal = switchType + "." + caseVal;
                            }
                        }
                        if (caseVal != null) {
                            if (switchVar.contains(".") && caseVal.contains(".")) {
                                if (switchVar.equals(caseVal)) {
                                    matchingCase = true;
                                    matchedNonDefault = true;
                                    break;
                                }
                            } else if (switchVar.endsWith(caseVal)) {
                                matchingCase = true;
                                matchedNonDefault = true;
                                break;
                            }
                        }
                    }
                }
            } else if (matchingCase) {
                if (stmtObj instanceof org.eclipse.jdt.core.dom.ReturnStatement rs) {
                    return resolveInternal(rs.getExpression(), context, visited);
                } else if (stmtObj instanceof org.eclipse.jdt.core.dom.YieldStatement ys) {
                    return resolveInternal(ys.getExpression(), context, visited);
                } else if (stmtObj instanceof org.eclipse.jdt.core.dom.ExpressionStatement es) {
                    return resolveInternal(es.getExpression(), context, visited);
                }
            }
        }
        return null;
    }

    private String evaluateSwitchExpression(org.eclipse.jdt.core.dom.SwitchExpression se, java.util.Map<String, String> paramValues, CodebaseContext context, Set<String> visited) {
        String switchVar = concreteSwitchSelector(
                resolveExpressionWithParams(se.getExpression(), paramValues, context, visited),
                se.getExpression());
        if (switchVar == null) {
            if (click.kamil.springstatemachineexporter.analysis.pipeline.SwitchConstSupport.hasOpaqueArm(se)) {
                // Throw-only / missing arms → fail-closed (do not publish partial last-arm sets).
                return null;
            }
            Set<String> values = new LinkedHashSet<>();
            for (Expression armExpr : click.kamil.springstatemachineexporter.analysis.pipeline.SwitchConstSupport.armPayloadExpressions(se)) {
                String val = resolveInternal(armExpr, context, visited);
                if (val != null) {
                    if (val.startsWith("ENUM_SET:")) {
                        values.addAll(Arrays.asList(val.substring(9).split(",")));
                    } else {
                        values.add(val);
                    }
                }
            }
            if (!values.isEmpty()) {
                return click.kamil.springstatemachineexporter.ast.common.AstUtils.formatEnumSet(values);
            }
            return null;
        }

        String switchType = null;
        if (switchVar.contains(".")) {
            switchType = switchVar.substring(0, switchVar.lastIndexOf('.'));
        }

        boolean matchingCase = false;
        boolean matchedNonDefault = false;
        for (Object stmtObj : se.statements()) {
            if (stmtObj instanceof org.eclipse.jdt.core.dom.SwitchCase sc) {
                matchingCase = false;
                if (sc.isDefault()) {
                    matchingCase = !matchedNonDefault;
                } else {
                    for (Object exprObj : sc.expressions()) {
                        String caseVal = resolveExpressionWithParams((Expression) exprObj, paramValues, context, visited);
                        if (caseVal == null && exprObj instanceof org.eclipse.jdt.core.dom.SimpleName caseSn) {
                            caseVal = caseSn.getIdentifier();
                            if (switchType != null) {
                                caseVal = switchType + "." + caseVal;
                            }
                        }
                        if (caseVal != null) {
                            if (switchVar.contains(".") && caseVal.contains(".")) {
                                if (switchVar.equals(caseVal)) {
                                    matchingCase = true;
                                    matchedNonDefault = true;
                                    break;
                                }
                            } else if (switchVar.endsWith(caseVal)) {
                                matchingCase = true;
                                matchedNonDefault = true;
                                break;
                            }
                        }
                    }
                }
            } else if (matchingCase) {
                Expression armExpr = null;
                if (stmtObj instanceof org.eclipse.jdt.core.dom.ExpressionStatement es) {
                    armExpr = es.getExpression();
                } else if (stmtObj instanceof org.eclipse.jdt.core.dom.YieldStatement ys) {
                    armExpr = ys.getExpression();
                } else if (stmtObj instanceof org.eclipse.jdt.core.dom.ReturnStatement rs) {
                    armExpr = rs.getExpression();
                } else if (stmtObj instanceof org.eclipse.jdt.core.dom.Block block) {
                    for (Object nested : block.statements()) {
                        if (nested instanceof org.eclipse.jdt.core.dom.YieldStatement ys) {
                            armExpr = ys.getExpression();
                            break;
                        } else if (nested instanceof org.eclipse.jdt.core.dom.ReturnStatement rs) {
                            armExpr = rs.getExpression();
                            break;
                        }
                    }
                }
                if (armExpr != null) {
                    return resolveInternal(armExpr, context, visited);
                }
            }
        }
        return null;
    }

    /**
     * Bound switch selectors only — identity names, blanks, and analysis tokens are unbound.
     * Shared with dataflow so CR and DFM stay aligned.
     */
    public static String concreteSwitchSelector(String resolved, Expression selectorExpr) {
        if (resolved == null || resolved.isBlank() || selectorExpr == null) {
            return null;
        }
        if (click.kamil.springstatemachineexporter.ast.common.AstUtils.isAnalysisToken(resolved)) {
            return null;
        }
        if (resolved.equals(selectorExpr.toString())) {
            return null;
        }
        return resolved;
    }

    private String resolveExpressionWithParams(
            Expression expr, java.util.Map<String, String> paramValues, CodebaseContext context, Set<String> visited) {
        if (expr instanceof SimpleName sn) {
            String name = sn.getIdentifier();
            if (paramValues.containsKey(name)) return paramValues.get(name);
            return null;
        } else if (expr instanceof FieldAccess fa) {
            String name = "this." + fa.getName().getIdentifier();
            if (paramValues.containsKey(name)) return paramValues.get(name);
            return null;
        } else if (expr instanceof QualifiedName qn) {
            return null;
        } else if (expr instanceof org.eclipse.jdt.core.dom.StringLiteral sl) {
            return sl.getLiteralValue();
        } else if (expr instanceof MethodInvocation mi) {
            if ("valueOf".equals(mi.getName().getIdentifier())) {
                Expression arg = null;
                if (mi.arguments().size() == 1) {
                    arg = (Expression) mi.arguments().get(0);
                } else if (mi.arguments().size() == 2) {
                    arg = (Expression) mi.arguments().get(1);
                }
                if (arg != null) {
                    String resolvedArg = resolveExpressionWithParams(arg, paramValues, context, visited);
                    if (resolvedArg != null && resolvedArg.matches("^[a-zA-Z_][a-zA-Z0-9_]*$")) {
                        String enumFqn = null;
                        if (mi.arguments().size() == 2) {
                            enumFqn = resolveEnumFqn((Expression) mi.arguments().get(0), context);
                        } else if (mi.arguments().size() == 1 && mi.getExpression() != null) {
                            enumFqn = resolveEnumFqn(mi.getExpression(), context);
                        }
                        if (enumFqn != null) {
                            return enumFqn + "." + resolvedArg;
                        } else {
                            // If we can't resolve the exact enum type here, we can just return the bare constant
                            return resolvedArg;
                        }
                    }
                }
            } else {
                if (mi.arguments().isEmpty() && mi.getExpression() instanceof SimpleName sn) {
                    String objName = sn.getIdentifier();
                    String methodName = mi.getName().getIdentifier();
                    String propName = null;
                    if (methodName.startsWith("get") && methodName.length() > 3) {
                        propName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
                    } else {
                        propName = methodName;
                    }
                    String val = paramValues.get(objName + "." + propName);
                    if (val == null && !propName.equals(methodName)) {
                        val = paramValues.get(objName + "." + methodName);
                    }
                    if (val != null) return val;
                }

                MethodDeclaration sideEffectMd = findInvokedMethod(mi, context);
                if (sideEffectMd != null && sideEffectMd.getBody() != null) {
                    TypeDeclaration currentTd = findEnclosingType(sideEffectMd);
                        java.util.Map<String, String> inlineLocals = new java.util.HashMap<>();
                        for (java.util.Map.Entry<String, String> entry : paramValues.entrySet()) {
                            if (entry.getKey().startsWith("this.")) {
                                inlineLocals.put(entry.getKey(), entry.getValue());
                            } else if (context.hasField(currentTd, entry.getKey())) {
                                inlineLocals.put(entry.getKey(), entry.getValue());
                                inlineLocals.put("this." + entry.getKey(), entry.getValue());
                            }
                        }
                        for (int i = 0; i < mi.arguments().size() && i < sideEffectMd.parameters().size(); i++) {
                            Expression arg = (Expression) mi.arguments().get(i);
                            String val = resolveExpressionWithParams(arg, paramValues, context, visited);
                            if (val == null) val = resolveInternal(arg, context, visited);
                            if (val != null) {
                                org.eclipse.jdt.core.dom.SingleVariableDeclaration param = (org.eclipse.jdt.core.dom.SingleVariableDeclaration) sideEffectMd.parameters().get(i);
                                inlineLocals.put(param.getName().getIdentifier(), val);
                            }
                        }
                        String ret = evaluateMethodBodyWithLocals(sideEffectMd, inlineLocals, context, visited);
                        return ret;
                    }
                }
            }
        return null;
    }

    private java.util.Map<String, String> extractBuilderFields(Expression expr, java.util.Map<String, String> localVars, CodebaseContext context, Set<String> visited) {
        if (!(expr instanceof MethodInvocation mi)) return null;
        java.util.Map<String, String> fields = new java.util.HashMap<>();
        MethodInvocation current = mi;
        while (current != null) {
            String methodName = current.getName().getIdentifier();
            if (!"build".equals(methodName) && !"builder".equals(methodName) && !"toBuilder".equals(methodName)) {
                if (current.arguments().size() == 1) {
                    Expression arg = (Expression) current.arguments().get(0);
                    String val = resolveExpressionWithParams(arg, localVars, context, visited);
                    if (val == null) val = resolveInternal(arg, context, visited);
                    if (val != null) fields.put(methodName, val);
                }
            }
            if (current.getExpression() instanceof MethodInvocation next) {
                current = next;
            } else {
                break;
            }
        }
        return fields.isEmpty() ? null : fields;
    }

    private List<String> extractArrayElementsFromCreation(ArrayCreation creation, CodebaseContext context, Set<String> visited) {
        if (creation.getInitializer() == null) {
            return null;
        }
        List<String> values = new ArrayList<>();
        for (Object expressionObj : creation.getInitializer().expressions()) {
            String value = resolveInternal((Expression) expressionObj, context, visited);
            if (value == null || value.startsWith("ARRAY:") || value.startsWith("MAP:")) {
                return null;
            }
            values.add(value);
        }
        return values;
    }

    private List<String> extractArrayElementsFromInitializer(ArrayInitializer initializer, CodebaseContext context, Set<String> visited) {
        List<String> values = new ArrayList<>();
        for (Object expressionObj : initializer.expressions()) {
            String value = resolveInternal((Expression) expressionObj, context, visited);
            if (value == null || value.startsWith("ARRAY:") || value.startsWith("MAP:")) {
                return null;
            }
            values.add(value);
        }
        return values;
    }

    private boolean isMapFactoryInvocation(MethodInvocation mi) {
        if (!"of".equals(mi.getName().getIdentifier()) && !"ofEntries".equals(mi.getName().getIdentifier())) {
            return false;
        }
        Expression receiver = mi.getExpression();
        if (receiver == null) {
            return false;
        }
        if (receiver instanceof SimpleName sn) {
            return "Map".equals(sn.getIdentifier());
        }
        if (receiver instanceof QualifiedName qn) {
            return "Map".equals(qn.getName().getIdentifier());
        }
        return false;
    }

    private String resolveInfix(InfixExpression expr, CodebaseContext context, Set<String> visited) {
        if (expr.getOperator() != InfixExpression.Operator.PLUS) return null;

        String left = resolveInternal(expr.getLeftOperand(), context, visited);
        String right = resolveInternal(expr.getRightOperand(), context, visited);

        if (left == null || right == null) return null;

        StringBuilder sb = new StringBuilder(left + right);
        if (expr.hasExtendedOperands()) {
            for (Object operand : expr.extendedOperands()) {
                String val = resolveInternal((Expression) operand, context, visited);
                if (val == null) return null;
                sb.append(val);
            }
        }
        return sb.toString();
    }

    private String resolveManual(SimpleName sn, CodebaseContext context, Set<String> visited) {
        MethodDeclaration enclosingMethod = findEnclosingMethod(sn);
        if (enclosingMethod != null) {
            for (Object paramObj : enclosingMethod.parameters()) {
                if (paramObj instanceof SingleVariableDeclaration svd) {
                    if (svd.getName().getIdentifier().equals(sn.getIdentifier())) {
                        return null;
                    }
                }
            }
            if (enclosingMethod.getBody() != null) {
                boolean[] isLocal = {false};
                enclosingMethod.getBody().accept(new ASTVisitor() {
                    @Override
                    public boolean visit(VariableDeclarationFragment node) {
                        if (node.getName().getIdentifier().equals(sn.getIdentifier())) {
                            isLocal[0] = true;
                        }
                        return super.visit(node);
                    }
                });
                if (isLocal[0]) {
                    return null;
                }
            }
        }

        TypeDeclaration td = findEnclosingType(sn);
        if (td != null) {
            String fqn = context.getFqn(td);
            String result = resolveFieldInType(td, sn.getIdentifier(), fqn, context, visited);
            if (result != null) {
                return result;
            }
        }

        for (String classFqn : classesFromCurrentCallPath(context)) {
            TypeDeclaration pathTd = context.getTypeDeclaration(classFqn);
            if (pathTd != null) {
                String result = resolveFieldInType(pathTd, sn.getIdentifier(), classFqn, context, visited);
                if (result != null) {
                    return result;
                }
            }
        }
        
        // Fallback: Static imports
        ASTNode root = sn.getRoot();
        if (root instanceof CompilationUnit cu) {
            TypeDeclaration staticTd = context.resolveStaticImport(sn.getIdentifier(), cu);
            if (staticTd != null) {
                return resolveFieldInType(staticTd, sn.getIdentifier(), context.getFqn(staticTd), context, visited);
            }
        }
        
        return null;
    }

    private String resolveManual(QualifiedName qn, CodebaseContext context, Set<String> visited) {
        String qualifier = qn.getQualifier().toString();
        String name = qn.getName().getIdentifier();
        
        CompilationUnit contextCu = (CompilationUnit) qn.getRoot();
        TypeDeclaration td = context.getTypeDeclaration(qualifier, contextCu);
        if (td == null) {
            td = resolveTypeInContext(qualifier, qn, context);
        }
        
        if (td != null) {
            String fqn = context.getFqn(td);
            return resolveFieldInType(td, name, fqn, context, visited);
        }
        
        return null;
    }

    private String resolveStaticFieldAccess(FieldAccess fa, CodebaseContext context, Set<String> visited) {
        Expression receiver = fa.getExpression();
        if (receiver instanceof ThisExpression) {
            return null;
        }
        String typeName = null;
        if (receiver instanceof SimpleName sn) {
            typeName = sn.getIdentifier();
        } else if (receiver instanceof QualifiedName qn) {
            typeName = qn.getFullyQualifiedName();
        }
        if (typeName == null) {
            return null;
        }
        TypeDeclaration td = resolveTypeInContext(typeName, fa, context);
        if (td == null) {
            td = typeFromCurrentCallPath(context);
        }
        if (td == null) {
            return null;
        }
        return resolveFieldInType(td, fa.getName().getIdentifier(), context.getFqn(td), context, visited);
    }

    private TypeDeclaration resolveTypeInContext(String typeName, ASTNode node, CodebaseContext context) {
        CompilationUnit contextCu = node.getRoot() instanceof CompilationUnit cu ? cu : null;
        TypeDeclaration td = contextCu != null
                ? context.getTypeDeclaration(typeName, contextCu)
                : context.getTypeDeclaration(typeName);
        if (td != null) {
            return td;
        }
        String entryClass = getCurrentCallPathEntryClass(context);
        if (entryClass == null) {
            return null;
        }
        TypeDeclaration entryTd = context.getTypeDeclaration(entryClass);
        if (entryTd == null || !(entryTd.getRoot() instanceof CompilationUnit entryCu)) {
            return null;
        }
        return context.getTypeDeclaration(typeName, entryCu);
    }

    private TypeDeclaration typeFromCurrentCallPath(CodebaseContext context) {
        List<String> classes = classesFromCurrentCallPath(context);
        if (classes.isEmpty()) {
            return null;
        }
        return context.getTypeDeclaration(classes.get(0));
    }

    private List<String> classesFromCurrentCallPath(CodebaseContext context) {
        List<String> path = context.getAnalysisCallPath();
        if (path == null || path.isEmpty()) {
            return List.of();
        }
        List<String> classes = new ArrayList<>();
        for (int i = path.size() - 1; i >= 0; i--) {
            String methodFqn = path.get(i);
            int dot = methodFqn.lastIndexOf('.');
            if (dot > 0) {
                String className = methodFqn.substring(0, dot);
                if (!classes.contains(className)) {
                    classes.add(className);
                }
            }
        }
        return classes;
    }

    private String getCurrentCallPathEntryClass(CodebaseContext context) {
        List<String> classes = classesFromCurrentCallPath(context);
        return classes.isEmpty() ? null : classes.get(classes.size() - 1);
    }

    private String resolveFieldInType(TypeDeclaration td, String fieldName, String typeFqn, CodebaseContext context, Set<String> visited) {
        String fieldId = typeFqn + "#" + fieldName;
        if (visited.contains(fieldId)) {
            log.warn("Circular reference detected during constant resolution: {}", fieldId);
            return null;
        }
        visited.add(fieldId);

        try {
            for (FieldDeclaration fd : td.getFields()) {
                for (Object fragmentObj : fd.fragments()) {
                    if (fragmentObj instanceof VariableDeclarationFragment fragment) {
                        if (fragment.getName().getIdentifier().equals(fieldName)) {
                            // Check for @Value annotation first
                            String valueFromAnnotation = findValueFromAnnotation(fd);
                            if (valueFromAnnotation != null) return valueFromAnnotation;

                            Expression initializer = fragment.getInitializer();
                            if (initializer != null) {
                                return resolveInternal(initializer, context, visited);
                            }
                        }
                    }
                }
            }
            
            // Check constructors for assignments
            for (MethodDeclaration md : td.getMethods()) {
                if (md.isConstructor() && md.getBody() != null) {
                    for (Object stmtObj : md.getBody().statements()) {
                        if (stmtObj instanceof ExpressionStatement es) {
                            if (es.getExpression() instanceof Assignment assignment) {
                                Expression lhs = assignment.getLeftHandSide();
                                boolean matches = false;
                                if (lhs instanceof SimpleName snLhs && snLhs.getIdentifier().equals(fieldName)) {
                                    matches = true;
                                } else if (lhs instanceof FieldAccess fa && fa.getName().getIdentifier().equals(fieldName) && fa.getExpression() instanceof ThisExpression) {
                                    matches = true;
                                }
                                if (matches) {
                                    return resolveInternal(assignment.getRightHandSide(), context, visited);
                                }
                            }
                        }
                    }
                }
            }
            
            // Check superclass
            if (td.getSuperclassType() != null) {
                String superclassName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(td.getSuperclassType());
                String resolvedSuperclass = resolveTypeFqn(superclassName, context, td);
                if (resolvedSuperclass != null) {
                    TypeDeclaration superTd = context.getTypeDeclaration(resolvedSuperclass);
                    if (superTd != null) {
                        String result = resolveFieldInType(superTd, fieldName, resolvedSuperclass, context, visited);
                        if (result != null) return result;
                    }
                }
            }
            
            // Check superinterfaces
            for (Object intfObj : td.superInterfaceTypes()) {
                Type intfType = (Type) intfObj;
                String intfName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(intfType);
                String resolvedIntf = resolveTypeFqn(intfName, context, td);
                if (resolvedIntf != null) {
                    TypeDeclaration intfTd = context.getTypeDeclaration(resolvedIntf);
                    if (intfTd != null) {
                        String result = resolveFieldInType(intfTd, fieldName, resolvedIntf, context, visited);
                        if (result != null) return result;
                    }
                }
            }
        } finally {
            visited.remove(fieldId);
        }
        return null;
    }
    
    private String resolveTypeFqn(String simpleName, CodebaseContext context, TypeDeclaration td) {
        CompilationUnit cu = (CompilationUnit) td.getRoot();
        for (Object impObj : cu.imports()) {
            ImportDeclaration imp = (ImportDeclaration) impObj;
            String name = imp.getName().getFullyQualifiedName();
            if (name.endsWith("." + simpleName)) {
                return name;
            }
        }
        return cu.getPackage().getName().getFullyQualifiedName() + "." + simpleName;
    }

    private String findValueFromAnnotation(FieldDeclaration fd) {
        for (Object mod : fd.modifiers()) {
            if (mod instanceof Annotation ann) {
                String name = ann.getTypeName().getFullyQualifiedName();
                if (name.endsWith("Value")) {
                    return extractAnnotationValue(ann);
                }
            }
        }
        return null;
    }

    private String extractAnnotationValue(Annotation ann) {
        Expression valueExpr = null;
        if (ann instanceof SingleMemberAnnotation sma) {
            valueExpr = sma.getValue();
        } else if (ann instanceof NormalAnnotation na) {
            String raw = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractAnnotationMember(ann, "value");
            if (!raw.isEmpty()) {
                if (raw.startsWith("\"") && raw.endsWith("\"")) {
                    raw = raw.substring(1, raw.length() - 1);
                }
                if (raw.contains("${")) {
                    return click.kamil.springstatemachineexporter.analysis.util.PlaceholderResolver.resolve(
                            raw, java.util.Collections.emptyMap());
                }
                return raw;
            }
            return null;
        }
        
        if (valueExpr instanceof StringLiteral sl) {
            String literal = sl.getLiteralValue();
            if (literal != null && literal.contains("${")) {
                return click.kamil.springstatemachineexporter.analysis.util.PlaceholderResolver.resolve(
                        literal, java.util.Collections.emptyMap());
            }
            return literal;
        }
        String raw = valueExpr != null ? valueExpr.toString().replaceAll("^\"|\"$", "") : null;
        if (raw != null && raw.contains("${")) {
            return click.kamil.springstatemachineexporter.analysis.util.PlaceholderResolver.resolve(
                    raw, java.util.Collections.emptyMap());
        }
        return raw;
    }

    private TypeDeclaration findEnclosingType(ASTNode node) {
        return click.kamil.springstatemachineexporter.ast.common.AstUtils.findEnclosingType(node);
    }

    private MethodDeclaration findInvokedMethod(org.eclipse.jdt.core.dom.MethodInvocation mi, CodebaseContext context) {
        org.eclipse.jdt.core.dom.IMethodBinding methodBinding = mi.resolveMethodBinding();
        if (methodBinding != null) {
            org.eclipse.jdt.core.dom.ITypeBinding declaringClass = methodBinding.getDeclaringClass();
            if (declaringClass != null) {
                TypeDeclaration targetTd = context.getTypeDeclaration(declaringClass.getErasure().getQualifiedName());
                if (targetTd != null) {
                    return context.findMethodDeclaration(targetTd, mi.getName().getIdentifier(), false);
                }
            }
        }
        TypeDeclaration currentTd = findEnclosingType(mi);
        if (currentTd != null && (mi.getExpression() == null || mi.getExpression() instanceof org.eclipse.jdt.core.dom.ThisExpression)) {
            return context.findMethodDeclaration(currentTd, mi.getName().getIdentifier(), true);
        }
        return null;
    }

    private MethodDeclaration findEnclosingMethod(ASTNode node) {
        return click.kamil.springstatemachineexporter.ast.common.AstUtils.findEnclosingMethod(node);
    }

    private String resolveLocalType(SimpleName sn) {
        String varName = sn.getIdentifier();
        MethodDeclaration enclosingMethod = findEnclosingMethod(sn);
        if (enclosingMethod != null && enclosingMethod.getBody() != null) {
            String[] typeName = new String[1];
            enclosingMethod.getBody().accept(new ASTVisitor() {
                @Override
                public boolean visit(VariableDeclarationStatement node) {
                    for (Object fragObj : node.fragments()) {
                        VariableDeclarationFragment frag = (VariableDeclarationFragment) fragObj;
                        if (frag.getName().getIdentifier().equals(varName)) {
                            typeName[0] = node.getType().toString();
                        }
                    }
                    return super.visit(node);
                }
            });
            if (typeName[0] != null) return typeName[0];
        }
        
        TypeDeclaration enclosingType = findEnclosingType(sn);
        if (enclosingType != null) {
            for (FieldDeclaration field : enclosingType.getFields()) {
                for (Object fragObj : field.fragments()) {
                    VariableDeclarationFragment frag = (VariableDeclarationFragment) fragObj;
                    if (frag.getName().getIdentifier().equals(varName)) {
                        return field.getType().toString();
                    }
                }
            }
        }
        return null;
    }

    private String resolveEnumFqn(Expression expr, CodebaseContext context) {
        if (expr == null) return null;
        CompilationUnit contextCu = (expr.getRoot() instanceof CompilationUnit) ? (CompilationUnit) expr.getRoot() : null;

        if (expr instanceof TypeLiteral tl) {
            ITypeBinding binding = tl.getType().resolveBinding();
            if (binding != null) {
                return binding.getQualifiedName();
            }
            String typeName = tl.getType().toString();
            if (typeName.endsWith(".class")) {
                typeName = typeName.substring(0, typeName.length() - 6);
            }
            String resolved = resolveTypeNameToFqn(typeName, contextCu, context);
            if (resolved != null) return resolved;
            return typeName;
        }

        ITypeBinding binding = expr.resolveTypeBinding();
        if (binding != null) {
            if (binding.isClass() && "java.lang.Class".equals(binding.getErasure().getQualifiedName())) {
                ITypeBinding[] typeArgs = binding.getTypeArguments();
                if (typeArgs.length > 0) {
                    return typeArgs[0].getQualifiedName();
                }
            }
            return binding.getQualifiedName();
        }

        String exprStr = expr.toString();
        if (exprStr.endsWith(".class")) {
            exprStr = exprStr.substring(0, exprStr.length() - 6);
        }
        String resolved = resolveTypeNameToFqn(exprStr, contextCu, context);
        if (resolved != null) return resolved;
        return exprStr;
    }

    private String resolveTypeNameToFqn(String name, CompilationUnit contextCu, CodebaseContext context) {
        if (name == null || name.isEmpty()) return null;
        
        if (context.getTypeDeclaration(name) != null || context.getEnumValuesMap().containsKey(name)) {
            return name;
        }
        
        if (contextCu != null) {
            for (Object typeObj : contextCu.types()) {
                if (typeObj instanceof AbstractTypeDeclaration atd) {
                    if (atd.getName().getIdentifier().equals(name)) {
                        String pkg = contextCu.getPackage() != null ? contextCu.getPackage().getName().getFullyQualifiedName() : "";
                        return pkg.isEmpty() ? name : pkg + "." + name;
                    }
                }
            }
            
            for (Object impObj : contextCu.imports()) {
                ImportDeclaration imp = (ImportDeclaration) impObj;
                String impName = imp.getName().getFullyQualifiedName();
                if (!imp.isStatic() && !imp.isOnDemand()) {
                    if (impName.endsWith("." + name)) {
                        return impName;
                    }
                }
            }
            
            if (contextCu.getPackage() != null) {
                String pkg = contextCu.getPackage().getName().getFullyQualifiedName();
                String pkgFqn = pkg + "." + name;
                if (context.getEnumValuesMap().containsKey(pkgFqn) || context.getTypeDeclaration(pkgFqn) != null) {
                    return pkgFqn;
                }
            }
        }
        
        return null;
    }
}
