public static <
            T> ConsumerPath<T> matchBestPath(String requestMethod, String requestPath, List<ConsumerPath<T>> consumerPaths) {
        ConsumerPath<T> answer = null;

        List<ConsumerPath<T>> candidates = new ArrayList<>();

        // first match by http method
        for (ConsumerPath<T> entry : consumerPaths) {
            if (matchRestMethod(requestMethod, entry.getRestrictMethod())) {
                candidates.add(entry);
            }
        }

        // then see if we got a direct match
        Iterator<ConsumerPath<T>> it = candidates.iterator();
        while (it.hasNext()) {
            ConsumerPath<T> consumer = it.next();
            if (matchRestPath(requestPath, consumer.getConsumerPath(), false)) {
                answer = consumer;
                break;
            }
        }

        // we could not find a direct match, and if the request is OPTIONS then we need all candidates
        if (answer == null && isOptionsMethod(requestMethod)) {
            candidates.clear();
            candidates.addAll(consumerPaths);

            // then try again to see if we can find a direct match
            it = candidates.iterator();
            while (it.hasNext()) {
                ConsumerPath<T> consumer = it.next();
                if (matchRestPath(requestPath, consumer.getConsumerPath(), false)) {
                    answer = consumer;
                    break;
                }
            }
        }

        // if there are no uri template, then select the matching with the longest path
        boolean noCurlyBraces = candidates.stream().allMatch(p -> countCurlyBraces(p.getConsumerPath()) == 0);
        if (noCurlyBraces) {
            // grab first which is the longest that matched the request path
            answer = candidates.stream()
                    .filter(c -> matchPath(requestPath, c.getConsumerPath(), c.isMatchOnUriPrefix()))
                    // sort by longest by inverting the sort by multiply with -1
                    .sorted(Comparator.comparingInt(o -> -1 * o.getConsumerPath().length())).findFirst().orElse(null);
        }

        // is there a direct match by with a different VERB, as then this call is not allowed
        if (answer == null) {
            for (ConsumerPath<T> entry : consumerPaths) {
                if (matchRestPath(requestPath, entry.getConsumerPath(), false)) {
                    // okay we have direct match but for another VERB so this call is not allowed
                    return null;
                }
            }
        }

        if (answer != null) {
            return answer;
        }

        // then match by uri template path
        it = candidates.iterator();
        List<ConsumerPath<T>> uriTemplateCandidates = new ArrayList<>();
        while (it.hasNext()) {
            ConsumerPath<T> consumer = it.next();
            // filter non matching paths
            if (matchRestPath(requestPath, consumer.getConsumerPath(), true)) {
                uriTemplateCandidates.add(consumer);
            }
        }

        // if there is multiple candidates with uri template then pick anyone with the least number of uri template
        ConsumerPath<T> best = null;
        Map<Integer, List<ConsumerPath<T>>> pathMap = new HashMap<>();
        if (uriTemplateCandidates.size() > 1) {
            it = uriTemplateCandidates.iterator();
            while (it.hasNext()) {
                ConsumerPath<T> entry = it.next();
                int curlyBraces = countCurlyBraces(entry.getConsumerPath());
                if (curlyBraces > 0) {
                    List<ConsumerPath<T>> consumerPathsList = pathMap.computeIfAbsent(curlyBraces, key -> new ArrayList<>());
                    consumerPathsList.add(entry);
                }
            }

            OptionalInt min = pathMap.keySet().stream().mapToInt(Integer::intValue).min();
            if (min.isPresent()) {
                List<ConsumerPath<T>> bestConsumerPaths = pathMap.get(min.getAsInt());
                if (bestConsumerPaths.size() > 1 && !canBeAmbiguous(requestMethod, requestMethod)) {
                    String exceptionMsg = "Ambiguous paths " + bestConsumerPaths.stream().map(ConsumerPath::getConsumerPath)
                            .collect(Collectors.joining(",")) + " for request path " + requestPath;
                    throw new IllegalStateException(exceptionMsg);
                }
                best = bestConsumerPaths.get(0);
            }

            if (best != null) {
                // pick the best among uri template
                answer = best;
            }
        }

        // if there is one left then it's our answer
        if (answer == null && uriTemplateCandidates.size() == 1) {
            return uriTemplateCandidates.get(0);
        }

        // last match by wildcard path
        it = candidates.iterator();
        while (it.hasNext()) {
            ConsumerPath<T> consumer = it.next();
            // filter non matching paths
            if (matchWildCard(requestPath, consumer.getConsumerPath())) {
                answer = consumer;
                break;
            }
        }

        return answer;
    }
@Test
    public void testRestConsumerContextPathMatcherSuccess() {
        List<RestConsumerContextPathMatcher.ConsumerPath<MockConsumerPath>> consumerPaths = new ArrayList<>();
        consumerPaths.add(new MockConsumerPath("GET", "/camel/a/b/{c}"));
        consumerPaths.add(new MockConsumerPath("GET", "/camel/aa/{b}/{c}"));

        RestConsumerContextPathMatcher.ConsumerPath<?> path = RestConsumerContextPathMatcher.matchBestPath("GET",
                "/camel/a/b/3", consumerPaths);
        assertEquals("/camel/a/b/{c}", path.getConsumerPath());
    }