package click.kamil.springstatemachineexporter.analysis.spring;

import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;

import java.util.List;

public class InjectionPointAnalyzer {
    
    private final SpringDependencyResolver resolver;

    public InjectionPointAnalyzer(SpringDependencyResolver resolver) {
        this.resolver = resolver;
    }

    /**
     * Resolves the Spring bean injected into the given variable.
     * 
     * @param variableBinding The binding of the field or parameter being injected.
     * @return The FQN of the resolved concrete bean class, or null if unresolved.
     */
    public String resolveInjectedBeanFqn(IVariableBinding variableBinding) {
        if (variableBinding == null || variableBinding.getType() == null) {
            return null;
        }

        String requiredTypeFqn = variableBinding.getType().getQualifiedName();
        String injectionName = variableBinding.getName();
        String qualifier = extractQualifier(variableBinding);

        List<SpringBean> resolvedBeans = resolver.resolve(requiredTypeFqn, qualifier, injectionName);
        
        if (resolvedBeans != null && resolvedBeans.size() == 1) {
            return resolvedBeans.get(0).getTypeFqn();
        }

        return null;
    }

    private String extractQualifier(IVariableBinding binding) {
        String qualifier = getQualifierValue(binding.getAnnotations());
        if (qualifier != null) {
            return qualifier;
        }

        if (binding.isField()) {
            org.eclipse.jdt.core.dom.ITypeBinding declaringClass = binding.getDeclaringClass();
            if (declaringClass != null) {
                for (org.eclipse.jdt.core.dom.IMethodBinding method : declaringClass.getDeclaredMethods()) {
                    boolean isAutowiredMethod = false;
                    for (IAnnotationBinding ann : method.getAnnotations()) {
                        if (ann.getAnnotationType() != null && "org.springframework.beans.factory.annotation.Autowired".equals(ann.getAnnotationType().getQualifiedName())) {
                            isAutowiredMethod = true;
                            break;
                        }
                    }

                    if (method.isConstructor() || isAutowiredMethod) {
                        for (int i = 0; i < method.getParameterTypes().length; i++) {
                            org.eclipse.jdt.core.dom.ITypeBinding paramType = method.getParameterTypes()[i];
                            try {
                                IAnnotationBinding[] paramAnns = method.getParameterAnnotations(i);
                                String paramQual = getQualifierValue(paramAnns);
                                if (paramQual != null
                                        && paramType.getErasure().isEqualTo(binding.getType().getErasure())
                                        && parameterMatchesField(method, i, binding.getName())) {
                                    return paramQual;
                                }
                            } catch (Exception e) {
                                // Ignore
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    private String getQualifierValue(IAnnotationBinding[] annotations) {
        for (IAnnotationBinding ann : annotations) {
            if (ann.getAnnotationType() != null && "org.springframework.beans.factory.annotation.Qualifier".equals(ann.getAnnotationType().getQualifiedName())) {
                for (IMemberValuePairBinding pair : ann.getDeclaredMemberValuePairs()) {
                    if ("value".equals(pair.getName()) && pair.getValue() instanceof String) {
                        return (String) pair.getValue();
                    }
                }
            }
        }
        return null;
    }

    private static boolean parameterMatchesField(
            org.eclipse.jdt.core.dom.IMethodBinding method,
            int parameterIndex,
            String fieldName) {
        if (method == null || fieldName == null || fieldName.isBlank() || parameterIndex < 0) {
            return false;
        }
        String[] names;
        try {
            names = method.getParameterNames();
        } catch (Exception ignored) {
            names = null;
        }
        if (names != null && parameterIndex < names.length) {
            return fieldName.equals(names[parameterIndex]);
        }
        // If parameter names aren't available, only accept clear setter-like methods.
        String methodName = method.getName();
        if (methodName != null && methodName.startsWith("set") && method.getParameterTypes().length == 1) {
            String expected = "set" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
            return expected.equals(methodName);
        }
        return false;
    }
}
