package click.kamil.springstatemachineexporter.analysis.service;

import java.util.Set;

/**
 * Recognizes JDK functional interface parameters used as delayed event providers
 * ({@code Supplier.get()}, {@code Runnable.run()}, {@code Consumer.accept()}, etc.)
 * in call-graph argument tracing.
 */
public final class FunctionalInterfaceTypes {

    private static final Set<String> SAM_METHOD_NAMES = Set.of(
            "run", "get", "call", "accept", "apply", "test");

    private FunctionalInterfaceTypes() {
    }

    public static boolean isSamMethodName(String methodName) {
        return methodName != null && SAM_METHOD_NAMES.contains(methodName);
    }

    /**
     * Whether {@code typeName.methodName} is a single-abstract-method invocation on a functional interface
     * (e.g. {@code java.lang.Runnable.run}, {@code java.util.function.Consumer.accept}).
     */
    public static boolean isFunctionalSamMethod(String methodFqn) {
        if (methodFqn == null || !methodFqn.contains(".")) {
            return false;
        }
        int lastDot = methodFqn.lastIndexOf('.');
        String methodName = methodFqn.substring(lastDot + 1);
        String typeName = methodFqn.substring(0, lastDot);
        return isSamMethodName(methodName) && isFunctionalInterface(typeName);
    }

    public static boolean isFunctionalInterface(String typeName) {
        if (typeName == null || typeName.isBlank()) {
            return false;
        }
        String simple = typeName.contains(".") ? typeName.substring(typeName.lastIndexOf('.') + 1) : typeName;
        if (simple.contains("<")) {
            simple = simple.substring(0, simple.indexOf('<'));
        }
        return switch (simple) {
            case "Runnable", "Supplier", "Function", "Callable", "Consumer", "BiConsumer", "Predicate", "BiFunction" ->
                    true;
            default -> simple.contains("Supplier") || simple.contains("Function") || simple.contains("Callable");
        };
    }

    public static boolean isLambdaArgument(String argValue) {
        if (argValue == null) {
            return false;
        }
        String trimmed = argValue.trim();
        return trimmed.contains("->")
                && (trimmed.startsWith("(") || trimmed.matches("^\\w+\\s*->.*"));
    }

    /**
     * When the caller passes a lambda or enum literal into a functional parameter, skip
     * incompatible-type rejection between {@code Supplier<T>} and resolved {@code T}.
     */
    public static boolean isProvablyResolvedCallSiteArgument(String argValue, String expectedType) {
        if (argValue == null || argValue.isBlank() || expectedType == null) {
            return false;
        }
        if (!isFunctionalInterface(expectedType)) {
            return false;
        }
        if (looksLikeEnumConstant(argValue)) {
            return true;
        }
        return isLambdaArgument(argValue);
    }

    static boolean looksLikeEnumConstant(String value) {
        if (value == null || value.isBlank()) {
            return false;
        }
        String name = value.contains(".") ? value.substring(value.lastIndexOf('.') + 1) : value;
        return name.matches("[A-Z_][A-Z0-9_]*");
    }
}
