@Contract("null, _, _ -> null")
  @Nullable
  public static FunctionHelper create(PsiExpression expression, int paramCount, boolean allowReturns) {
    expression = PsiUtil.skipParenthesizedExprDown(expression);
    if(expression == null) return null;
    PsiType type = FunctionalExpressionUtils.getFunctionalExpressionType(expression);
    if(!(type instanceof PsiClassType)) return null;
    PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(type);
    if (interfaceMethod == null || interfaceMethod.getParameterList().getParametersCount() != paramCount) return null;
    PsiType returnType = interfaceMethod.getReturnType();
    if (returnType == null) return null;
    returnType = ((PsiClassType)type).resolveGenerics().getSubstitutor().substitute(returnType);
    if (expression instanceof PsiLambdaExpression lambda) {
      PsiParameterList list = lambda.getParameterList();
      if (list.getParametersCount() != paramCount) return null;
      String[] parameters = StreamEx.of(list.getParameters()).map(PsiVariable::getName).toArray(String[]::new);
      PsiElement body = lambda.getBody();
      PsiExpression lambdaExpression = LambdaUtil.extractSingleExpressionFromBody(body);
      if (lambdaExpression == null) {
        if (body instanceof PsiCodeBlock block) {
          List<PsiReturnStatement> returns = getReturns(body);
          if (PsiTypes.voidType().equals(returnType)) {
            if (!allowReturns && (!returns.isEmpty() || !ControlFlowUtils.codeBlockMayCompleteNormally(block))) return null;
            // Return inside loop is not supported yet
            for (PsiReturnStatement ret : returns) {
              if (PsiTreeUtil.getParentOfType(ret, PsiLoopStatement.class, true, PsiLambdaExpression.class) != null) {
                return null;
              }
            }
            return new VoidBlockLambdaFunctionHelper(block, parameters);
          } else if (returns.size() == 1 && ArrayUtil.getLastElement(block.getStatements()) == returns.get(0)) {
            PsiExpression trivialCall = JavaPsiFacade.getElementFactory(lambda.getProject())
              .createExpressionFromText("((" + type.getCanonicalText() + ")" + lambda.getText() + ")." +
                                        interfaceMethod.getName() + "(" + String.join(",", parameters) + ")", lambda);
            return new LambdaFunctionHelper(returnType, trivialCall, parameters);
          }
        }
        return null;
      }
      return new LambdaFunctionHelper(returnType, lambdaExpression, parameters);
    }
    if (expression instanceof PsiMethodReferenceExpression methodRef) {
      if (methodRef.resolve() == null) return null;
      String template = tryInlineMethodReference(paramCount, methodRef);
      if (template != null) {
        return new InlinedFunctionHelper(returnType, paramCount, template);
      }
      return new MethodReferenceFunctionHelper(returnType, type, methodRef);
    }
    if (expression instanceof PsiReferenceExpression && ExpressionUtils.isSafelyRecomputableExpression(expression)) {
      return new SimpleReferenceFunctionHelper(returnType, expression, interfaceMethod.getName());
    }
    if (expression instanceof PsiMethodCallExpression call) {
      if (MethodCallUtils.isCallToStaticMethod(call, CommonClassNames.JAVA_UTIL_FUNCTION_FUNCTION, "identity", 0)) {
        return paramCount == 1 ? new InlinedFunctionHelper(returnType, 1, "{0}") : null;
      }
      if (MethodCallUtils.isCallToStaticMethod(call, CommonClassNames.JAVA_UTIL_COMPARATOR, "naturalOrder", 0)) {
        return paramCount == 2 ? new InlinedFunctionHelper(returnType, 2, "{0}.compareTo({1})") : null;
      }
      if (MethodCallUtils.isCallToStaticMethod(call, CommonClassNames.JAVA_UTIL_COMPARATOR, "reverseOrder", 0) ||
          MethodCallUtils.isCallToStaticMethod(call, CommonClassNames.JAVA_UTIL_COLLECTIONS, "reverseOrder", 0)) {
        return paramCount == 2 ? new InlinedFunctionHelper(returnType, 2, "{1}.compareTo({0})") : null;
      }
    }
    return new ComplexExpressionFunctionHelper(returnType, type, interfaceMethod.getName(), expression);
  }
@Contract("null, _, _ -> null")
  @Nullable
  public static FunctionHelper create(PsiExpression expression, int paramCount, boolean allowReturns)