package click.kamil.springstatemachineexporter.analysis.service;

import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import org.eclipse.jdt.core.dom.*;
import java.util.*;

public class TypeResolver {
    private final CodebaseContext context;

    public TypeResolver(CodebaseContext context) {
        this.context = context;
    }

    public int getParameterIndex(String methodFqn, String paramName) {
        if (methodFqn == null || !methodFqn.contains(".")) return -1;
        String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
        String methodName = methodFqn.substring(methodFqn.lastIndexOf('.') + 1);
        TypeDeclaration td = context.getTypeDeclaration(className);
        if (td != null) {
            MethodDeclaration md = context.findMethodDeclaration(td, methodName, true);
            if (md != null) {
                for (int i = 0; i < md.parameters().size(); i++) {
                    SingleVariableDeclaration svd = (SingleVariableDeclaration) md.parameters().get(i);
                    if (svd.getName().getIdentifier().equals(paramName)) {
                        return i;
                    }
                }
            }
        }
        return -1;
    }

    public String getParameterName(String methodFqn, int index) {
        if (methodFqn == null || !methodFqn.contains(".") || index < 0) return null;
        String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
        String methodName = methodFqn.substring(methodFqn.lastIndexOf('.') + 1);
        TypeDeclaration td = context.getTypeDeclaration(className);
        if (td != null) {
            MethodDeclaration md = context.findMethodDeclaration(td, methodName, true);
            if (md != null && index < md.parameters().size()) {
                SingleVariableDeclaration svd = (SingleVariableDeclaration) md.parameters().get(index);
                return svd.getName().getIdentifier();
            }
        }
        return null;
    }

    public String getParameterType(String methodFqn, int index) {
        if (methodFqn == null || !methodFqn.contains(".") || index < 0) return null;
        String className = methodFqn.substring(0, methodFqn.lastIndexOf('.'));
        String methodName = methodFqn.substring(methodFqn.lastIndexOf('.') + 1);
        TypeDeclaration td = context.getTypeDeclaration(className);
        if (td != null) {
            MethodDeclaration md = context.findMethodDeclaration(td, methodName, true);
            if (md != null && index < md.parameters().size()) {
                SingleVariableDeclaration svd = (SingleVariableDeclaration) md.parameters().get(index);
                return svd.getType().toString();
            }
        }
        return null;
    }

    public boolean isTypeCompatible(String actualType, String expectedType) {
        if (actualType == null || expectedType == null) return true;
        if (expectedType.equals("Object") || expectedType.equals("java.lang.Object")) return true;
        if (actualType.equals("Object") || actualType.equals("java.lang.Object")) return true;
        if (expectedType.length() == 1 && Character.isUpperCase(expectedType.charAt(0))) return true;
        if (actualType.length() == 1 && Character.isUpperCase(actualType.charAt(0))) return true;
        
        String originalExpectedType = expectedType;

        if (actualType.contains("<")) {
            String rawType = actualType.substring(0, actualType.indexOf('<'));
            if (rawType.equals("List") || rawType.equals("Set") || rawType.equals("Collection") || rawType.equals("Iterable") || rawType.endsWith("List") || rawType.endsWith("Set") || rawType.endsWith("Collection")) {
                int start = actualType.indexOf('<');
                int end = actualType.lastIndexOf('>');
                if (start >= 0 && end > start) {
                    actualType = actualType.substring(start + 1, end);
                }
            }
        }
        if (expectedType.contains("<")) {
            String rawType = expectedType.substring(0, expectedType.indexOf('<'));
            if (rawType.equals("List") || rawType.equals("Set") || rawType.equals("Collection") || rawType.equals("Iterable") || rawType.endsWith("List") || rawType.endsWith("Set") || rawType.endsWith("Collection")) {
                int start = expectedType.indexOf('<');
                int end = expectedType.lastIndexOf('>');
                if (start >= 0 && end > start) {
                    expectedType = expectedType.substring(start + 1, end);
                }
            }
        }

        if (actualType.contains("<")) {
            actualType = actualType.substring(0, actualType.indexOf('<'));
        }
        if (expectedType.contains("<")) {
            expectedType = expectedType.substring(0, expectedType.indexOf('<'));
        }
        
        if (actualType.equals(expectedType)) return true;
        if (actualType.endsWith("." + expectedType) || expectedType.endsWith("." + actualType)) return true;

        AbstractTypeDeclaration td = context.getAbstractTypeDeclaration(actualType);
        if (td != null) {
            if (td instanceof TypeDeclaration typeDecl) {
                String superFqn = context.getSuperclassFqn(typeDecl);
                while (superFqn != null) {
                    if (superFqn.contains("<")) {
                        superFqn = superFqn.substring(0, superFqn.indexOf('<'));
                    }
                    if (superFqn.equals(expectedType) || superFqn.endsWith("." + expectedType) || expectedType.endsWith("." + superFqn)) return true;
                    TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
                    superFqn = superTd != null ? context.getSuperclassFqn(superTd) : null;
                }
            }
            List interfaces = java.util.Collections.emptyList();
            if (td instanceof TypeDeclaration typeDecl) {
                interfaces = typeDecl.superInterfaceTypes();
            } else if (td instanceof EnumDeclaration enumDecl) {
                interfaces = enumDecl.superInterfaceTypes();
            } else if (td instanceof RecordDeclaration recordDecl) {
                interfaces = recordDecl.superInterfaceTypes();
            }
            for (Object interfaceType : interfaces) {
                String itStr = interfaceType.toString();
                if (itStr.contains("<")) {
                    itStr = itStr.substring(0, itStr.indexOf('<'));
                }
                if (itStr.equals(expectedType) || itStr.endsWith("." + expectedType) || expectedType.endsWith("." + itStr)) return true;
            }
        }

        if (originalExpectedType.contains("<")) {
            int start = originalExpectedType.indexOf('<');
            int end = originalExpectedType.lastIndexOf('>');
            if (start >= 0 && end > start) {
                String sub = originalExpectedType.substring(start + 1, end);
                for (String part : sub.split(",")) {
                    String typeArg = part.trim();
                    if (isTypeCompatible(actualType, typeArg)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
}
