package click.kamil.springstatemachineexporter.ast.common;

import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.NameQualifiedType;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;

public final class AstUtils {
    private AstUtils() {
    }

    public static MethodDeclaration findEnclosingMethod(ASTNode node) {
        ASTNode parent = node.getParent();
        while (parent != null && !(parent instanceof MethodDeclaration)) {
            parent = parent.getParent();
        }
        return (MethodDeclaration) parent;
    }

    public static TypeDeclaration findEnclosingType(ASTNode node) {
        ASTNode parent = node.getParent();
        while (parent != null && !(parent instanceof TypeDeclaration)) {
            parent = parent.getParent();
        }
        return (TypeDeclaration) parent;
    }

    public static String simpleName(String fqn) {
        if (fqn == null || fqn.isEmpty()) {
            return fqn;
        }
        int dot = fqn.lastIndexOf('.');
        return dot >= 0 ? fqn.substring(dot + 1) : fqn;
    }

    public static String qualifier(String fqn) {
        if (fqn == null || !fqn.contains(".")) {
            return null;
        }
        return fqn.substring(0, fqn.lastIndexOf('.'));
    }

    public static String extractAnnotationMember(Annotation annotation, String memberName) {
        if (annotation instanceof SingleMemberAnnotation sma) {
            if ("value".equals(memberName)) {
                return sma.getValue().toString();
            }
        } else if (annotation instanceof NormalAnnotation na) {
            for (Object pairObj : na.values()) {
                MemberValuePair pair = (MemberValuePair) pairObj;
                if (pair.getName().getIdentifier().equals(memberName)) {
                    return pair.getValue().toString();
                }
            }
        }
        return "";
    }

    public static String extractSimpleTypeName(Type type) {
        if (type.isSimpleType()) {
            Name name = ((SimpleType) type).getName();
            if (name instanceof QualifiedName qn) {
                return qn.getName().getIdentifier();
            }
            return name.toString();
        } else if (type.isParameterizedType()) {
            Type rawType = ((ParameterizedType) type).getType();
            return extractSimpleTypeName(rawType);
        } else if (type.isQualifiedType()) {
            return ((QualifiedType) type).getName().getIdentifier();
        } else if (type.isNameQualifiedType()) {
            return ((NameQualifiedType) type).getName().getIdentifier();
        } else if (type.isArrayType()) {
            return extractSimpleTypeName(((ArrayType) type).getElementType());
        } else if (type.isPrimitiveType()) {
            return ((PrimitiveType) type).getPrimitiveTypeCode().toString();
        } else {
            return type.toString(); // fallback
        }
    }

    public static boolean isFunctionalCollectionMethod(org.eclipse.jdt.core.dom.MethodInvocation node) {
        if (node.getExpression() == null) {
            return false;
        }

        org.eclipse.jdt.core.dom.ITypeBinding typeBinding = node.getExpression().resolveTypeBinding();
        if (typeBinding != null) {
            if (isCollectionOrStreamType(typeBinding)) {
                return true;
            }
            return false;
        }
        
        String methodName = node.getName().getIdentifier();
        if (methodName.equals("forEach") || methodName.equals("map") || 
            methodName.equals("filter") || methodName.equals("flatMap") || 
            methodName.equals("anyMatch") || methodName.equals("allMatch") || 
            methodName.equals("noneMatch") || methodName.equals("reduce") ||
            methodName.equals("collect") || methodName.equals("removeIf") ||
            methodName.equals("ifPresent") || methodName.equals("ifPresentOrElse") ||
            methodName.equals("compute") || methodName.equals("computeIfAbsent") ||
            methodName.equals("computeIfPresent") || methodName.equals("replaceAll") ||
            methodName.equals("merge") || methodName.equals("peek") ||
            methodName.equals("doOnNext") || methodName.equals("doOnSuccess") ||
            methodName.equals("doOnError") || methodName.equals("concatMap") ||
            methodName.equals("switchMap")) {
            return true;
        }
        
        return false; 
    }

    private static boolean isCollectionOrStreamType(org.eclipse.jdt.core.dom.ITypeBinding typeBinding) {
        if (typeBinding == null) return false;
        String fqn = typeBinding.getErasure().getQualifiedName();
        if (fqn.startsWith("java.util.stream.") || 
            fqn.equals("java.lang.Iterable") || 
            fqn.equals("java.util.Collection") || 
            fqn.equals("java.util.List") || 
            fqn.equals("java.util.Set") || 
            fqn.equals("java.util.Map") || 
            fqn.equals("java.util.Iterator") || 
            fqn.equals("java.util.Optional") ||
            fqn.equals("reactor.core.publisher.Flux") ||
            fqn.equals("reactor.core.publisher.Mono")) {
            return true;
        }
        
        for (org.eclipse.jdt.core.dom.ITypeBinding intf : typeBinding.getInterfaces()) {
            if (isCollectionOrStreamType(intf)) return true;
        }
        
        if (typeBinding.getSuperclass() != null) {
            return isCollectionOrStreamType(typeBinding.getSuperclass());
        }
        
        return false;
    }

    public static String findConditionConstraint(org.eclipse.jdt.core.dom.ASTNode node) {
        java.util.List<String> conditions = new java.util.ArrayList<>();
        org.eclipse.jdt.core.dom.ASTNode current = node;
        while (current != null && !(current instanceof org.eclipse.jdt.core.dom.MethodDeclaration)) {
            org.eclipse.jdt.core.dom.ASTNode parent = current.getParent();
            if (parent instanceof org.eclipse.jdt.core.dom.IfStatement ifStmt) {
                if (ifStmt.getExpression() != current) {
                    org.eclipse.jdt.core.dom.Expression cond = ifStmt.getExpression();
                    if (current == ifStmt.getElseStatement()) {
                        conditions.add("!(" + cond.toString() + ")");
                    } else {
                        conditions.add(cond.toString());
                    }
                }
            }
            current = parent;
        }
        String switchConstraint = buildSwitchCaseConstraint(findEnclosingSwitchCase(node));
        if (switchConstraint != null) {
            conditions.add(switchConstraint);
        }
        if (conditions.isEmpty()) return null;
        java.util.Collections.reverse(conditions);
        return String.join(" && ", conditions);
    }

    static org.eclipse.jdt.core.dom.SwitchCase findEnclosingSwitchCase(org.eclipse.jdt.core.dom.ASTNode node) {
        org.eclipse.jdt.core.dom.ASTNode current = node;
        while (current != null && !(current instanceof org.eclipse.jdt.core.dom.MethodDeclaration)) {
            if (current instanceof org.eclipse.jdt.core.dom.SwitchCase switchCase) {
                return switchCase;
            }
            current = current.getParent();
        }
        current = node;
        while (current != null && !(current instanceof org.eclipse.jdt.core.dom.MethodDeclaration)) {
            if (current instanceof org.eclipse.jdt.core.dom.YieldStatement yieldStatement) {
                org.eclipse.jdt.core.dom.ASTNode parent = yieldStatement.getParent();
                if (parent instanceof org.eclipse.jdt.core.dom.SwitchStatement switchStatement) {
                    org.eclipse.jdt.core.dom.SwitchCase switchCase =
                            findSwitchCaseForStatement(switchStatement.statements(), yieldStatement);
                    if (switchCase != null) {
                        return switchCase;
                    }
                } else if (parent instanceof org.eclipse.jdt.core.dom.SwitchExpression switchExpression) {
                    org.eclipse.jdt.core.dom.SwitchCase switchCase =
                            findSwitchCaseForStatement(switchExpression.statements(), yieldStatement);
                    if (switchCase != null) {
                        return switchCase;
                    }
                }
            } else if (current instanceof org.eclipse.jdt.core.dom.ExpressionStatement expressionStatement) {
                org.eclipse.jdt.core.dom.ASTNode parent = expressionStatement.getParent();
                if (parent instanceof org.eclipse.jdt.core.dom.SwitchCase switchCase) {
                    return switchCase;
                }
            }
            current = current.getParent();
        }
        return null;
    }

    private static org.eclipse.jdt.core.dom.SwitchCase findSwitchCaseForStatement(
            java.util.List<?> statements, org.eclipse.jdt.core.dom.ASTNode bodyStatement) {
        int idx = statements.indexOf(bodyStatement);
        if (idx < 0) {
            return null;
        }
        for (int i = idx - 1; i >= 0; i--) {
            if (statements.get(i) instanceof org.eclipse.jdt.core.dom.SwitchCase switchCase) {
                return switchCase;
            }
        }
        return null;
    }

    private static String buildSwitchCaseConstraint(org.eclipse.jdt.core.dom.SwitchCase switchCase) {
        if (switchCase == null || switchCase.isDefault()) {
            return null;
        }
        org.eclipse.jdt.core.dom.Expression switchSelector = null;
        org.eclipse.jdt.core.dom.ASTNode switchParent = switchCase.getParent();
        if (switchParent instanceof org.eclipse.jdt.core.dom.SwitchStatement ss) {
            switchSelector = ss.getExpression();
        } else if (switchParent instanceof org.eclipse.jdt.core.dom.SwitchExpression se) {
            switchSelector = se.getExpression();
        }
        if (switchSelector == null) {
            return null;
        }
        java.util.List<String> caseLabels = new java.util.ArrayList<>();
        for (Object exprObj : switchCase.expressions()) {
            if (exprObj instanceof org.eclipse.jdt.core.dom.Expression caseExpr) {
                caseLabels.add(caseExpr.toString());
            }
        }
        if (caseLabels.isEmpty()) {
            return null;
        }
        String selector = switchSelector.toString();
        if (caseLabels.size() == 1) {
            return selector + " == " + caseLabels.get(0);
        }
        StringBuilder combined = new StringBuilder("(");
        for (int i = 0; i < caseLabels.size(); i++) {
            if (i > 0) {
                combined.append(" || ");
            }
            combined.append(selector).append(" == ").append(caseLabels.get(i));
        }
        combined.append(')');
        return combined.toString();
    }
}
