package click.kamil.springstatemachineexporter.analysis.index;

import click.kamil.springstatemachineexporter.ast.common.AstUtils;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodInvocation;

import java.util.List;
import java.util.Optional;

public final class AccessorInlining {

    private AccessorInlining() {
    }

    public static Optional<AccessorSummary> lookupAccessor(
            CodebaseContext context,
            MethodInvocation mi,
            List<Expression> receiverDefinitions) {
        if (context == null || mi == null) {
            return Optional.empty();
        }

        String methodName = mi.getName().getIdentifier();
        if (isLikelyNonBeanGet(methodName, mi)) {
            return Optional.empty();
        }

        if (receiverDefinitions != null) {
            for (Expression receiverDef : receiverDefinitions) {
                if (receiverDef instanceof ClassInstanceCreation cic) {
                    Optional<AccessorSummary> concrete = lookupOnType(context, resolveTypeFqn(cic), methodName);
                    if (concrete.isPresent()) {
                        return concrete;
                    }
                }
            }
        }

        IMethodBinding methodBinding = mi.resolveMethodBinding();
        if (methodBinding != null && methodBinding.getDeclaringClass() != null) {
            ITypeBinding declaringClass = methodBinding.getDeclaringClass().getErasure();
            Optional<AccessorSummary> declared = lookupOnType(context, declaringClass.getQualifiedName(), methodName);
            if (declared.isPresent()) {
                return declared;
            }
        }

        return Optional.empty();
    }

    public static Optional<AccessorSummary> lookupAccessor(CodebaseContext context, MethodInvocation mi) {
        return lookupAccessor(context, mi, List.of());
    }

    private static Optional<AccessorSummary> lookupOnType(CodebaseContext context, String ownerFqn, String methodName) {
        if (ownerFqn == null || ownerFqn.isBlank() || ownerFqn.startsWith("java.")) {
            return Optional.empty();
        }
        return context.getAccessorIndex().lookup(ownerFqn, methodName);
    }

    public static String resolveTypeFqn(ClassInstanceCreation cic) {
        if (cic == null) {
            return null;
        }
        if (cic.resolveConstructorBinding() != null && cic.resolveConstructorBinding().getDeclaringClass() != null) {
            return cic.resolveConstructorBinding().getDeclaringClass().getErasure().getQualifiedName();
        }
        if (cic.resolveTypeBinding() != null) {
            return cic.resolveTypeBinding().getErasure().getQualifiedName();
        }
        return AstUtils.extractSimpleTypeName(cic.getType());
    }

    private static boolean isLikelyNonBeanGet(String methodName, MethodInvocation mi) {
        if (!"get".equals(methodName) && !"getOrDefault".equals(methodName)) {
            return false;
        }
        if (mi.arguments().size() != 1) {
            return false;
        }
        IMethodBinding methodBinding = mi.resolveMethodBinding();
        if (methodBinding == null || methodBinding.getDeclaringClass() == null) {
            return false;
        }
        String ownerFqn = methodBinding.getDeclaringClass().getErasure().getQualifiedName();
        return ownerFqn != null && ownerFqn.startsWith("java.util");
    }
}
