class CostEffectivenessVisualsController extends GenericOCDSController {

    @ApiOperation(value = "Aggregated version of /api/costEffectivenessTenderAmount and "
            + "/api/costEffectivenessAwardAmount."
            + "This endpoint aggregates the responses from the specified endpoints, per year. "
            + "Responds to the same filters.")
    @RequestMapping(value = "/api/costEffectivenessTenderAwardAmount", method = {RequestMethod.POST,
            RequestMethod.GET}, produces = "application/json")
    public ArrayList<Document> costEffectivenessTenderAwardAmount(
            @ModelAttribute @Valid final GroupingFilterPagingRequest filter) {

        Future<List<Document>> costEffectivenessAwardAmountFuture = controllerLookupService
                .asyncInvoke(new AsyncBeanParamControllerMethodCallable<List<Document>, GroupingFilterPagingRequest>() {
                    @Override
                    public List<Document> invokeControllerMethod(GroupingFilterPagingRequest filter) {
                        return costEffectivenessAwardAmount(filter);
                    }
                }, filter);


        Future<List<Document>> costEffectivenessTenderAmountFuture = controllerLookupService
                .asyncInvoke(new AsyncBeanParamControllerMethodCallable<List<Document>, GroupingFilterPagingRequest>() {
                    @Override
                    public List<Document> invokeControllerMethod(GroupingFilterPagingRequest filter) {
                        return costEffectivenessTenderAmount(filter);
                    }
                }, filter);


        //this is completely unnecessary since the #get methods are blocking
        //controllerLookupService.waitTillDone(costEffectivenessAwardAmountFuture, costEffectivenessTenderAmountFuture);


        LinkedHashMap<Object, Document> response = new LinkedHashMap<>();

        try {

            costEffectivenessAwardAmountFuture.get()
                    .forEach(dbobj -> response.put(getYearMonthlyKey(filter, dbobj), dbobj));
            costEffectivenessTenderAmountFuture.get().forEach(dbobj -> {
                if (response.containsKey(getYearMonthlyKey(filter, dbobj))) {
                    HashMap<String, Object> map = new HashMap<>(dbobj);
                    map.remove(Keys.YEAR);
                    if (filter.getMonthly()) {
                        map.remove(Keys.MONTH);
                    }
                    response.get(getYearMonthlyKey(filter, dbobj)).putAll(map);
                } else {
                    response.put(getYearMonthlyKey(filter, dbobj), dbobj);
                }
            });

        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }

        Collection<Document> respCollection = response.values();

        respCollection.forEach(dbobj -> {

            BigDecimal totalTenderAmount = BigDecimal
                    .valueOf(dbobj.get(Keys.TOTAL_TENDER_AMOUNT) == null ? 0d
                            : ((Number) dbobj.get(Keys.TOTAL_TENDER_AMOUNT)).doubleValue());

            BigDecimal totalAwardAmount = BigDecimal
                    .valueOf(dbobj.get(Keys.TOTAL_AWARD_AMOUNT) == null ? 0d
                            : ((Number) dbobj.get(Keys.TOTAL_AWARD_AMOUNT)).doubleValue());

            dbobj.put(
                    Keys.DIFF_TENDER_AWARD_AMOUNT,
                    totalTenderAmount
                            .subtract(totalAwardAmount)
            );

            dbobj.put(
                    Keys.PERCENTAGE_AWARD_AMOUNT,
                    totalTenderAmount.compareTo(BigDecimal.ZERO) != 0
                            ? (totalAwardAmount.setScale(15)
                            .divide(totalTenderAmount, BigDecimal.ROUND_HALF_UP)
                            .multiply(ONE_HUNDRED)) : BigDecimal.ZERO
            );

            dbobj.put(
                    Keys.PERCENTAGE_DIFF_AMOUNT,
                    totalTenderAmount.compareTo(BigDecimal.ZERO) != 0
                            ? (((BigDecimal) dbobj.get(Keys.DIFF_TENDER_AWARD_AMOUNT)).setScale(15)
                            .divide(totalTenderAmount, BigDecimal.ROUND_HALF_UP)
                            .multiply(ONE_HUNDRED)) : BigDecimal.ZERO
            );

        });

        return new ArrayList<>(respCollection);
    }

}