package click.kamil.springstatemachineexporter.analysis.index;

import click.kamil.springstatemachineexporter.analysis.service.ConstructorAnalyzer;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;

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

/**
 * Resolves constant values for fields referenced by indexed trivial accessors.
 */
public final class AccessorFieldResolver {

    private AccessorFieldResolver() {
    }

    public static List<String> resolveFieldConstants(
            AccessorSummary accessor,
            String requestingTypeFqn,
            CodebaseContext context,
            ConstructorAnalyzer constructorAnalyzer,
            CompilationUnit contextCu,
            Set<String> visited,
            BiConsumer<org.eclipse.jdt.core.dom.Expression, List<String>> constantExtractor) {
        if (accessor == null || !accessor.isGetter() || context == null) {
            return List.of();
        }
        if (accessor.kind() == click.kamil.springstatemachineexporter.analysis.index.AccessorKind.CONSTANT_GETTER
                || accessor.fieldName() == null) {
            return List.of();
        }

        String traceTypeFqn = requestingTypeFqn != null && !requestingTypeFqn.isBlank()
                ? requestingTypeFqn
                : accessor.declaringFqn();
        TypeDeclaration typeDeclaration = contextCu != null
                ? context.getTypeDeclaration(traceTypeFqn, contextCu)
                : null;
        if (typeDeclaration == null) {
            typeDeclaration = context.getTypeDeclaration(traceTypeFqn);
        }
        if (typeDeclaration == null) {
            return List.of();
        }

        List<String> constants = new ArrayList<>();
        if (constructorAnalyzer != null) {
            constants.addAll(constructorAnalyzer.traceFieldInConstructors(
                    typeDeclaration, accessor.fieldName(), context, visited));
        }

        if (constants.isEmpty()) {
            collectFieldInitializerConstants(
                    typeDeclaration, accessor.fieldName(), constantExtractor, constants, context);
        }
        return constants;
    }

    private static void collectFieldInitializerConstants(
            TypeDeclaration typeDeclaration,
            String fieldName,
            BiConsumer<org.eclipse.jdt.core.dom.Expression, List<String>> constantExtractor,
            List<String> constants,
            CodebaseContext context) {
        if (constantExtractor == null || typeDeclaration == null) {
            return;
        }
        for (FieldDeclaration fieldDeclaration : typeDeclaration.getFields()) {
            for (Object fragmentObj : fieldDeclaration.fragments()) {
                if (fragmentObj instanceof VariableDeclarationFragment fragment
                        && fragment.getName().getIdentifier().equals(fieldName)
                        && fragment.getInitializer() != null) {
                    constantExtractor.accept(fragment.getInitializer(), constants);
                }
            }
        }
        if (constants.isEmpty() && context != null) {
            String superFqn = context.getSuperclassFqn(typeDeclaration);
            if (superFqn != null && !superFqn.equals("java.lang.Object")) {
                TypeDeclaration superType = context.getTypeDeclaration(superFqn);
                if (superType != null) {
                    collectFieldInitializerConstants(superType, fieldName, constantExtractor, constants, context);
                }
            }
        }
    }
}
