private void report(
      IGoogleLandscapeInterpolatorUserConfiguration userConfig,
      List<LandscapeData> landscapeData,
      Map<String, Account> accounts)
      throws Exception {

    Map<CriterionId, Criterion> criterions = getCriterionsMap(accounts);

    settingsLoader.downloadSettings(
        accounts,
        criterion -> {
          Long id = getLongValue(criterion.apply("Id"));
          Long groupId = getLongValue(criterion.apply("AdGroupId"));

          Criterion cr = criterions.get(new CriterionId(id, groupId));
          if (cr != null) {
            ((BidMetrics) cr.getMetrics())
                .setMaxBid(getDoubleValue(criterion.apply("CpcBid")) / 1_000_000);
          }
        });

    Map<Long, Map<Long, Double>> conversionsRate = getKeywordConversionsRate(accounts);

    File linearReportFile = fsUtils.getResultFile(userConfig, LINEAR);
    File cubicReportFile = fsUtils.getResultFile(userConfig, CUBIC);

    for (String interpolationType : userConfig.getGoogleLandscapeInterpolator().interpolationType) {
      switch (interpolationType) {
        case LINEAR:
          {
            try (ICsvListWriter writer =
                new CsvListWriter(
                    new FileWriter(linearReportFile), CsvPreference.STANDARD_PREFERENCE)) {
              writer.write(Arrays.asList(linear_report_fields));
            }
            break;
          }
        case CUBIC:
          {
            try (ICsvListWriter writer =
                new CsvListWriter(
                    new FileWriter(cubicReportFile), CsvPreference.STANDARD_PREFERENCE)) {
              writer.write(Arrays.asList(cubic_report_fields));
            }
          }
      }
    }

    for (LandscapeData ld : landscapeData) {
      Criterion cr = criterions.get(new CriterionId(ld.keywordId, ld.adGroupId));

      Double bid = ((BidMetrics) cr.getMetrics()).getMaxBid();

      Double increasedBidValue =
          bid * (100 + userConfig.getGoogleLandscapeInterpolator().bidFactor) / 100;
      Double decreasedBidValue =
          bid * (100 - userConfig.getGoogleLandscapeInterpolator().bidFactor) / 100;

      Map<Long, Double> conversions = conversionsRate.get(ld.adGroupId);

      for (String interpolationType :
          userConfig.getGoogleLandscapeInterpolator().interpolationType) {
        switch (interpolationType) {
          case LINEAR:
            writeLinearInterpolationBids(
                linearReportFile, bid, ld, increasedBidValue, decreasedBidValue, conversions);
            break;
          case CUBIC:
            writeCubicInterpolationBids(
                cubicReportFile, bid, ld, increasedBidValue, decreasedBidValue, conversions);
        }
      }
    }
    switch (userConfig.getGoogleLandscapeInterpolator().interpolationType.get(0)) {
      case LINEAR:
        {
          userConfig.setResultFile(linearReportFile.getAbsolutePath());
          break;
        }
      case CUBIC:
        {
          userConfig.setResultFile(cubicReportFile.getAbsolutePath());
        }
    }
  }
  private Map<Long, AdGroupPerformance> initializeBaseReport(
      IDsaTrackerUserConfiguration userConfig, final Map<String, Account> accounts)
      throws Exception {
    if (userConfig.getDsaTracker().reinitializeBaseReport) {
      File baseReportFile = fsUtils.getResultFile(userConfig, "DsaBaseReport");
      LOGGER.info("Reinitialize base report");

      // update adGroups bids
      if (!userConfig.getIdleMode()) {
        adGroupBidUploaderService.uploadBids(
            userConfig,
            accounts
                .values()
                .stream()
                .flatMap(accountData -> accountData.getCampaigns().values().stream())
                .flatMap(campaignData -> campaignData.getAdGroups().values().stream()));
      }

      if (!configuration.dsaTracker.singleRunMod) {
        LOGGER.info(
            "Wait for data to accumulate (waitTime = "
                + userConfig.getDsaTracker().waitTime
                + " sec)...");
        Thread.sleep(userConfig.getDsaTracker().waitTime * 1000);
      }

      LocalDateTime dateOne = now();
      LocalDateTime dateTwo = dateOne.minusDays(REPORT_DAYS);
      Map<Long, AdGroupPerformance> baseReport =
          performanceDataLoader.downloadPerformanceData(
              accounts, dateTwo.toLocalDate(), dateOne.toLocalDate());

      for (Long key : baseReport.keySet()) {
        if (baseReport.get(key).adGroup == null) baseReport.remove(key);
      }

      for (Account account : accounts.values()) {
        for (Campaign campaign : account.getCampaigns().values()) {
          for (AdGroup adGroup : campaign.getAdGroups().values()) {
            if (baseReport.get(adGroup.getAdGroupId()) != null) continue;
            AdGroupPerformance adGroupPerformance = new AdGroupPerformance();
            Long campaignId = campaign.getCampaignId();
            Long adGroupId = adGroup.getAdGroupId();
            adGroupPerformance.adGroup =
                new AdGroupImpl(
                    new CampaignImpl(new AccountImpl(account.getAccountId()), campaignId),
                    adGroupId);
            adGroupPerformance.adGroup.setMetrics(
                new DsaMetrics() {
                  {
                    setMaxBid(0.0);
                  }
                });
            adGroupPerformance.hasConversions = false;
            adGroupPerformance.averageCpc = 0;
            adGroupPerformance.averagePosition = 0;
            adGroupPerformance.clicks = 0;
            adGroupPerformance.cost = 0;
            adGroupPerformance.impressions = 0;

            baseReport.put(adGroupId, adGroupPerformance);
          }
        }
      }

      baseReport
          .values()
          .forEach(
              r -> {
                DsaMetrics metrics = (DsaMetrics) r.adGroup.getMetrics();
                r.maxBaseCpc =
                    metrics.getMaxBid() * (1 + userConfig.getDsaTracker().maxCpcBidChange);
                r.minBaseCpc =
                    metrics.getMaxBid() * (1 - userConfig.getDsaTracker().maxCpcBidChange);
              });

      dsaTrackerUtils.saveBaseReport(baseReportFile, baseReport);
      ((DsaTrackerUserConfiguration) userConfig).baseReportFilePath =
          baseReportFile.getAbsolutePath();

      return baseReport;
    } else {
      LOGGER.info("Load base report from previous launch");
      File baseReportFile = new File(((DsaTrackerUserConfiguration) userConfig).baseReportFilePath);
      if (!baseReportFile.exists()) {
        throw new FileNotFoundException("Basic landscape report file does not exist");
      }

      return dsaTrackerUtils.loadBaseReport(baseReportFile, accounts);
    }
  }