package click.kamil.springstatemachineexporter.analysis.pipeline;

import click.kamil.springstatemachineexporter.analysis.service.ConstantExtractor;
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.TypeDeclaration;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;

/**
 * Scans trivial getter bodies for constant return values.
 */
public final class GetterBodyConstantScanner {

    private GetterBodyConstantScanner() {
    }

    public static List<String> extractConstantGetterReturn(
            CodebaseContext context,
            ConstantExtractor constantExtractor,
            ConstantResolver constantResolver,
            String ownerFqn,
            String methodName) {
        return extractConstantGetterReturn(context, constantExtractor, constantResolver, ownerFqn, methodName, null, null);
    }

    public static List<String> extractConstantGetterReturn(
            CodebaseContext context,
            ConstantExtractor constantExtractor,
            ConstantResolver constantResolver,
            String ownerFqn,
            String methodName,
            CompilationUnit contextCu,
            Set<String> ignoredVisited) {
        TypeDeclaration typeDeclaration = contextCu != null
                ? context.getTypeDeclaration(ownerFqn, contextCu)
                : null;
        if (typeDeclaration == null) {
            typeDeclaration = context.getTypeDeclaration(ownerFqn);
        }
        if (typeDeclaration == null) {
            return List.of();
        }
        MethodDeclaration methodDeclaration = context.findMethodDeclaration(typeDeclaration, methodName, true);
        if (methodDeclaration == null || methodDeclaration.getBody() == null) {
            return List.of();
        }

        List<String> constants = new ArrayList<>();
        BiConsumer<org.eclipse.jdt.core.dom.Expression, List<String>> extractor =
                constantExtractor != null
                        ? constantExtractor::extractConstantsFromExpression
                        : (expr, out) -> {
                            String resolved = constantResolver.resolve(expr, context);
                            if (resolved != null) {
                                out.add(resolved);
                            }
                        };
        methodDeclaration.getBody().accept(new ASTVisitor() {
            @Override
            public boolean visit(ReturnStatement node) {
                if (node.getExpression() != null) {
                    extractor.accept(node.getExpression(), constants);
                }
                return false;
            }
        });
        return constants;
    }
}
