/*
 * Axelor Business Solutions
 *
 * Copyright (C) 2019 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.axelor.apps.stock.service;

import com.axelor.app.AppSettings;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.repo.CompanyRepository;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.UnitConversionService;
import com.axelor.apps.stock.db.StockHistoryLine;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.apps.stock.db.StockLocationLine;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.stock.db.TrackingNumber;
import com.axelor.apps.stock.db.repo.StockLocationLineRepository;
import com.axelor.apps.stock.db.repo.StockLocationRepository;
import com.axelor.apps.stock.db.repo.StockMoveLineRepository;
import com.axelor.apps.stock.db.repo.StockMoveRepository;
import com.axelor.apps.stock.db.repo.TrackingNumberRepository;
import com.axelor.apps.tool.file.CsvTool;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.axelor.meta.MetaFiles;
import com.axelor.meta.db.MetaFile;
import com.google.common.io.Files;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.persistence.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StockHistoryServiceImpl implements StockHistoryService {

  protected StockMoveLineRepository stockMoveLineRepository;
  protected UnitConversionService unitConversionService;

  private final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

  @Inject
  public StockHistoryServiceImpl(
      StockMoveLineRepository stockMoveLineRepository,
      UnitConversionService unitConversionService) {
    this.stockMoveLineRepository = stockMoveLineRepository;
    this.unitConversionService = unitConversionService;
  }

  public List<StockHistoryLine> computeStockHistoryLineList(
      Long productId, Long companyId, Long stockLocationId, LocalDate beginDate, LocalDate endDate)
      throws AxelorException {
    List<StockHistoryLine> stockHistoryLineList = new ArrayList<>();

    // one line per month
    for (LocalDate periodBeginDate = beginDate.withDayOfMonth(1);
        periodBeginDate.isBefore(endDate);
        periodBeginDate = periodBeginDate.plusMonths(1)) {
      LocalDate periodEndDate = periodBeginDate.plusMonths(1);
      StockHistoryLine stockHistoryLine = new StockHistoryLine();
      stockHistoryLine.setLabel(periodBeginDate.toString());
      fetchAndFillResultForStockHistoryQuery(
          stockHistoryLine,
          productId,
          companyId,
          stockLocationId,
          periodBeginDate,
          periodEndDate,
          true,
          false,
          null);
      fetchAndFillResultForStockHistoryQuery(
          stockHistoryLine,
          productId,
          companyId,
          stockLocationId,
          periodBeginDate,
          periodEndDate,
          false,
          false,
          null);
      stockHistoryLineList.add(stockHistoryLine);
    }
    StockHistoryLine totalStockHistoryLine = createStockHistoryTotalLine(stockHistoryLineList);
    StockHistoryLine avgStockHistoryLine =
        createStockHistoryAvgLine(stockHistoryLineList, totalStockHistoryLine);
    stockHistoryLineList.add(totalStockHistoryLine);
    stockHistoryLineList.add(avgStockHistoryLine);

    // result lines
    return stockHistoryLineList;
  }

  @Transactional
  protected void fetchAndFillResultForStockHistoryQuery(
      StockHistoryLine stockHistoryLine,
      Long productId,
      Long companyId,
      Long stockLocationId,
      LocalDate periodBeginDate,
      LocalDate periodEndDate,
      boolean incoming,
      boolean allLocations,
      TrackingNumber trackingNumber)
      throws AxelorException {
    Long trackingNumberId = trackingNumber.getId();
    String filter =
        "self.product.id = :productId "
            + "AND self.stockMove.statusSelect = :realized "
            + "AND self.stockMove.company.id = :companyId "
            + "AND self.stockMove.realDate >= :beginDate "
            + "AND self.stockMove.realDate < :endDate ";

    if (incoming) {
      if (allLocations == true) {
        filter += "AND self.stockMove.fromStockLocation.typeSelect = :typeSelect ";
      } else {
        filter += "AND self.stockMove.toStockLocation.id = :stockLocationId ";
      }
    } else {
      if (allLocations == true) {
        filter += "AND self.stockMove.toStockLocation.typeSelect = :typeSelect ";
      } else {
        filter += "AND self.stockMove.fromStockLocation.id = :stockLocationId ";
      }
    }

    if (trackingNumberId != null) {
      filter += " AND self.trackingNumber.id = :trackingNumberId";
    }

    System.out.println("*************query filter************************");
    System.out.println(filter);
    System.out.println("*************query************************");

    List<StockMoveLine> stockMoveLineList =
        stockMoveLineRepository
            .all()
            .filter(filter)
            .bind("productId", productId)
            .bind("companyId", companyId)
            .bind("stockLocationId", stockLocationId)
            .bind("realized", StockMoveRepository.STATUS_REALIZED)
            .bind("beginDate", periodBeginDate)
            .bind("endDate", periodEndDate)
            .bind("typeSelect", StockLocationRepository.TYPE_VIRTUAL)
            .bind("trackingNumberId", trackingNumberId)
            .fetch();

    System.out.println("stockMoveLineList size******************************************");
    System.out.println(stockMoveLineList.size());
    System.out.println("stockMoveLineList size******************************************");

    if (incoming) {
      fillIncomingStockHistoryLineFields(stockHistoryLine, stockMoveLineList);
    } else {
      fillOutgoingStockHistoryLineFields(stockHistoryLine, stockMoveLineList);
    }
    JPA.clear();
  }

  protected void fillIncomingStockHistoryLineFields(
      StockHistoryLine stockHistoryLine, List<StockMoveLine> stockMoveLineList)
      throws AxelorException {

    stockHistoryLine.setCountIncMvtStockPeriod(
        Math.toIntExact(
            stockMoveLineList.stream().map(StockMoveLine::getStockMove).distinct().count()));
    BigDecimal sumIncQtyPeriod = BigDecimal.ZERO;
    for (StockMoveLine stockMoveLine : stockMoveLineList) {
      // quantity in product unit
      BigDecimal qtyConverted =
          unitConversionService.convert(
              stockMoveLine.getUnit(),
              stockMoveLine.getProduct().getUnit(),
              stockMoveLine.getRealQty(),
              stockMoveLine.getRealQty().scale(),
              stockMoveLine.getProduct());
      sumIncQtyPeriod = sumIncQtyPeriod.add(qtyConverted);
    }
    stockHistoryLine.setSumIncQtyPeriod(sumIncQtyPeriod);
    stockHistoryLine.setPriceIncStockMovePeriod(
        stockMoveLineList
            .stream()
            .map(StockMoveLine::getCompanyUnitPriceUntaxed)
            .reduce(BigDecimal::add)
            .orElse(BigDecimal.ZERO));
  }

  protected void fillOutgoingStockHistoryLineFields(
      StockHistoryLine stockHistoryLine, List<StockMoveLine> stockMoveLineList)
      throws AxelorException {

    stockHistoryLine.setCountOutMvtStockPeriod(
        Math.toIntExact(
            stockMoveLineList.stream().map(StockMoveLine::getStockMove).distinct().count()));
    BigDecimal sumOutQtyPeriod = BigDecimal.ZERO;
    for (StockMoveLine stockMoveLine : stockMoveLineList) {
      // quantity in product unit
      BigDecimal qtyConverted =
          unitConversionService.convert(
              stockMoveLine.getUnit(),
              stockMoveLine.getProduct().getUnit(),
              stockMoveLine.getRealQty(),
              stockMoveLine.getRealQty().scale(),
              stockMoveLine.getProduct());
      sumOutQtyPeriod = sumOutQtyPeriod.add(qtyConverted);
    }
    stockHistoryLine.setSumOutQtyPeriod(sumOutQtyPeriod);
    stockHistoryLine.setPriceOutStockMovePeriod(
        stockMoveLineList
            .stream()
            .map(StockMoveLine::getCompanyUnitPriceUntaxed)
            .reduce(BigDecimal::add)
            .orElse(BigDecimal.ZERO));
  }

  /**
   * Create a line labelled "Total", summing each field in the table.
   *
   * @param stockHistoryLineList
   * @return the created line.
   */
  protected StockHistoryLine createStockHistoryTotalLine(
      List<StockHistoryLine> stockHistoryLineList) {
    StockHistoryLine stockHistoryLine = new StockHistoryLine();
    stockHistoryLine.setLabel(I18n.get("Total"));

    Integer countIncMvtStock =
        stockHistoryLineList
            .stream()
            .map(StockHistoryLine::getCountIncMvtStockPeriod)
            .filter(Objects::nonNull)
            .mapToInt(Integer::intValue)
            .sum();
    stockHistoryLine.setCountIncMvtStockPeriod(countIncMvtStock);

    BigDecimal sumIncQtyPeriod =
        stockHistoryLineList
            .stream()
            .map(StockHistoryLine::getSumIncQtyPeriod)
            .filter(Objects::nonNull)
            .reduce(BigDecimal::add)
            .orElse(BigDecimal.ZERO);
    stockHistoryLine.setSumIncQtyPeriod(sumIncQtyPeriod);

    BigDecimal priceIncStockMove =
        stockHistoryLineList
            .stream()
            .map(StockHistoryLine::getPriceIncStockMovePeriod)
            .filter(Objects::nonNull)
            .reduce(BigDecimal::add)
            .orElse(BigDecimal.ZERO);
    stockHistoryLine.setPriceIncStockMovePeriod(priceIncStockMove);

    Integer countOutMvtStock =
        stockHistoryLineList
            .stream()
            .map(StockHistoryLine::getCountOutMvtStockPeriod)
            .filter(Objects::nonNull)
            .mapToInt(Integer::intValue)
            .sum();
    stockHistoryLine.setCountOutMvtStockPeriod(countOutMvtStock);

    BigDecimal sumOutQtyPeriod =
        stockHistoryLineList
            .stream()
            .map(StockHistoryLine::getSumOutQtyPeriod)
            .filter(Objects::nonNull)
            .reduce(BigDecimal::add)
            .orElse(BigDecimal.ZERO);
    stockHistoryLine.setSumOutQtyPeriod(sumOutQtyPeriod);

    BigDecimal priceOutStockMove =
        stockHistoryLineList
            .stream()
            .map(StockHistoryLine::getPriceOutStockMovePeriod)
            .filter(Objects::nonNull)
            .reduce(BigDecimal::add)
            .orElse(BigDecimal.ZERO);
    stockHistoryLine.setPriceOutStockMovePeriod(priceOutStockMove);

    return stockHistoryLine;
  }

  /**
   * Create a line labelled "Average", using a list of stockHistory and a line containing totals.
   *
   * @param stockHistoryLineList
   * @param totalStockHistoryLine
   * @return the created line.
   */
  protected StockHistoryLine createStockHistoryAvgLine(
      List<StockHistoryLine> stockHistoryLineList, StockHistoryLine totalStockHistoryLine) {
    StockHistoryLine stockHistoryLine = new StockHistoryLine();
    stockHistoryLine.setLabel(I18n.get("Average"));

    int sizeOfList = stockHistoryLineList.size();
    if (sizeOfList == 0) {
      return stockHistoryLine;
    }

    stockHistoryLine.setCountIncMvtStockPeriod(
        totalStockHistoryLine.getCountIncMvtStockPeriod() / sizeOfList);

    stockHistoryLine.setSumIncQtyPeriod(
        totalStockHistoryLine
            .getSumIncQtyPeriod()
            .divide(BigDecimal.valueOf(sizeOfList), 2, RoundingMode.HALF_EVEN));

    stockHistoryLine.setPriceIncStockMovePeriod(
        totalStockHistoryLine
            .getPriceIncStockMovePeriod()
            .divide(BigDecimal.valueOf(sizeOfList), 2, RoundingMode.HALF_EVEN));

    stockHistoryLine.setCountOutMvtStockPeriod(
        totalStockHistoryLine.getCountOutMvtStockPeriod() / sizeOfList);

    stockHistoryLine.setSumOutQtyPeriod(
        totalStockHistoryLine
            .getSumOutQtyPeriod()
            .divide(BigDecimal.valueOf(sizeOfList), 2, RoundingMode.HALF_EVEN));

    stockHistoryLine.setPriceOutStockMovePeriod(
        totalStockHistoryLine
            .getPriceOutStockMovePeriod()
            .divide(BigDecimal.valueOf(sizeOfList), 2, RoundingMode.HALF_EVEN));

    return stockHistoryLine;
  }

  @Transactional
  public List<StockHistoryLine> compuHistoryLinesPerDate(
      Long productId,
      Long companyId,
      Long stockLocationId,
      LocalDate beginDate,
      LocalDate endDate,
      Long categoryId,
      Long trackingNumberId,
      Integer searchTypeSelect)
      throws AxelorException {
    List<StockHistoryLine> stockHistoryLineList = new ArrayList<>();
    Company company = Beans.get(CompanyRepository.class).find(companyId);

    // ALL
    if (searchTypeSelect == 1) {

      List<Product> products =
          Beans.get(StockLocationLineRepository.class)
              .all()
              .fetchStream()
              .map(StockLocationLine::getProduct)
              .distinct()
              .collect(Collectors.toList());
      for (Product product : products) {
        StockHistoryLine stockHistoryLine = new StockHistoryLine();
        stockHistoryLine.setLabel(product.getFullName().toString());

        fetchAndFillResultForStockHistoryQuery(
            stockHistoryLine,
            product.getId(),
            companyId,
            stockLocationId,
            beginDate,
            endDate,
            true,
            true,
            null);

        fetchAndFillResultForStockHistoryQuery(
            stockHistoryLine,
            product.getId(),
            companyId,
            stockLocationId,
            beginDate,
            endDate,
            false,
            true,
            null);

        BigDecimal totalQty = this.getTotalQty(product, company, stockLocationId, true, null);
        BigDecimal realQty =
            totalQty
                .add(stockHistoryLine.getSumOutQtyPeriod())
                .subtract(stockHistoryLine.getSumIncQtyPeriod());
        stockHistoryLine.setRealQty(realQty);
        stockHistoryLineList.add(stockHistoryLine);
      }

    } else if (searchTypeSelect == 3) {
      List<Product> products =
          Beans.get(ProductRepository.class)
              .all()
              .filter("self.familleProduit.id in (" + String.valueOf(categoryId) + ")")
              .fetch();
      for (Product product : products) {
        StockHistoryLine stockHistoryLine = new StockHistoryLine();
        stockHistoryLine.setLabel(product.getFullName().toString());

        fetchAndFillResultForStockHistoryQuery(
            stockHistoryLine,
            product.getId(),
            companyId,
            stockLocationId,
            beginDate,
            endDate,
            true,
            true,
            null);

        fetchAndFillResultForStockHistoryQuery(
            stockHistoryLine,
            product.getId(),
            companyId,
            stockLocationId,
            beginDate,
            endDate,
            false,
            true,
            null);

        BigDecimal totalQty = this.getTotalQty(product, company, stockLocationId, true, null);
        BigDecimal realQty =
            totalQty
                .add(stockHistoryLine.getSumOutQtyPeriod())
                .subtract(stockHistoryLine.getSumIncQtyPeriod());
        stockHistoryLine.setRealQty(realQty);
        stockHistoryLineList.add(stockHistoryLine);
      }

    } else if (searchTypeSelect == 2) {
      StockHistoryLine stockHistoryLine = new StockHistoryLine();
      stockHistoryLine.setLabel(beginDate.toString());

      fetchAndFillResultForStockHistoryQuery(
          stockHistoryLine,
          productId,
          companyId,
          stockLocationId,
          beginDate,
          endDate,
          true,
          false,
          null);

      fetchAndFillResultForStockHistoryQuery(
          stockHistoryLine,
          productId,
          companyId,
          stockLocationId,
          beginDate,
          endDate,
          false,
          false,
          null);

      Product product = Beans.get(ProductRepository.class).find(productId);
      BigDecimal totalQty = this.getTotalQty(product, company, stockLocationId, false, null);
      BigDecimal realQty =
          totalQty
              .add(stockHistoryLine.getSumOutQtyPeriod())
              .subtract(stockHistoryLine.getSumIncQtyPeriod());
      stockHistoryLine.setRealQty(realQty);
      stockHistoryLineList.add(stockHistoryLine);
    } else if (searchTypeSelect == 4) {
      StockHistoryLine stockHistoryLine = new StockHistoryLine();
      TrackingNumber trackingNumber =
          Beans.get(TrackingNumberRepository.class).find(trackingNumberId);
      Product product = Beans.get(ProductRepository.class).find(productId);

      stockHistoryLine.setLabel(
          beginDate.toString()
              + " "
              + product.getName()
              + " "
              + trackingNumber.getTrackingNumberSeq());

      fetchAndFillResultForStockHistoryQuery(
          stockHistoryLine,
          productId,
          companyId,
          stockLocationId,
          beginDate,
          endDate,
          true,
          true,
          trackingNumber);

      fetchAndFillResultForStockHistoryQuery(
          stockHistoryLine,
          productId,
          companyId,
          stockLocationId,
          beginDate,
          endDate,
          false,
          true,
          trackingNumber);

      BigDecimal totalQty =
          this.getTotalQty(product, company, stockLocationId, true, trackingNumberId);
      System.out.println("***************totalQty*******************");
      System.out.println(trackingNumberId);
      System.out.println(totalQty);
      System.out.println("***************totalQty*******************");
      BigDecimal realQty =
          totalQty
              .add(stockHistoryLine.getSumOutQtyPeriod())
              .subtract(stockHistoryLine.getSumIncQtyPeriod());
      stockHistoryLine.setRealQty(realQty);
      stockHistoryLineList.add(stockHistoryLine);
    } else if (searchTypeSelect == 5) {
      List<StockLocationLine> stockLocationLines =
          Beans.get(StockLocationLineRepository.class)
              .all()
              .filter(
                  "self.detailsStockLocation.typeSelect != "
                      + StockLocationRepository.TYPE_VIRTUAL
                      + " AND self.detailsStockLocation != null AND self.product.familleProduit.id in ("
                      + String.valueOf(categoryId)
                      + ")")
              .fetch();

      log.debug("stockLocationLines : {}", stockLocationLines);
      log.debug("stockLocationLines size : {}", stockLocationLines.size());
      log.debug("categoryId  : {}", categoryId);
      log.debug("categoryId  : {}", categoryId);

      int sizeWithoutTraccking =
          stockLocationLines
              .stream()
              .filter(t -> t.getTrackingNumber() == null)
              .collect(Collectors.toList())
              .size();

      log.debug("sizeWithoutTraccking  : {}", sizeWithoutTraccking);

      List<TrackingNumber> trackingNumbers =
          Beans.get(TrackingNumberRepository.class).all().fetch();
      List<Product> products = Beans.get(ProductRepository.class).all().fetch();

      this.sumGroupByStocklocation(null, null, beginDate, null);

      for (StockLocationLine line : stockLocationLines) {

        TrackingNumber trackingNumber =
            trackingNumbers
                .stream()
                .filter(t -> t.getId() == line.getTrackingNumber().getId())
                .findFirst()
                .get();
        Product product =
            products.stream().filter(t -> t.getId() == line.getProduct().getId()).findFirst().get();

        Long trackingNumberId2 = trackingNumber.getId();

        if (trackingNumber != null) {
          StockLocation location = line.getDetailsStockLocation();
          log.debug("trackingNumber >>>>>> : {}", trackingNumber);
          log.debug("product >>>>>> : {}", product);

          StockHistoryLine stockHistoryLine = new StockHistoryLine();
          stockHistoryLine.setLabel(
              product.getName()
                  + "*"
                  + trackingNumber.getTrackingNumberSeq()
                  + "*"
                  + location.getName());

          fetchAndFillResultForStockHistoryQuery(
              stockHistoryLine,
              product.getId(),
              companyId,
              location.getId(),
              beginDate,
              endDate,
              true,
              false,
              trackingNumber);

          fetchAndFillResultForStockHistoryQuery(
              stockHistoryLine,
              product.getId(),
              companyId,
              location.getId(),
              beginDate,
              endDate,
              false,
              false,
              trackingNumber);

          BigDecimal totalQty =
              this.getTotalQty(product, company, location.getId(), false, trackingNumberId2);
          BigDecimal realQty =
              totalQty
                  .add(stockHistoryLine.getSumOutQtyPeriod())
                  .subtract(stockHistoryLine.getSumIncQtyPeriod());

          log.debug(
              "*** trackingNumber || totalQty || realQty : {} {} {}",
              trackingNumber,
              totalQty,
              realQty);

          stockHistoryLine.setRealQty(realQty);
          stockHistoryLineList.add(stockHistoryLine);
        }
      }
    }

    return stockHistoryLineList;
  }

  @SuppressWarnings({"unchecked", "rawtypes"})
  public BigDecimal getTotalQty(
      Product product,
      Company company,
      Long StockLocationId,
      boolean allLocations,
      Long trackingNumberId) {
    Long productId = product.getId();
    String query = "";

    query =
        "SELECT new list(self.id, self.avgPrice, self.currentQty) FROM StockLocationLine as self "
            + "WHERE self.product.id = "
            + productId;

    if (trackingNumberId == null) {
      query += " AND self.stockLocation.typeSelect != " + StockLocationRepository.TYPE_VIRTUAL;
    } else {
      query +=
          " AND self.detailsStockLocation.typeSelect != "
              + StockLocationRepository.TYPE_VIRTUAL
              + " AND self.trackingNumber.id = "
              + trackingNumberId;
    }

    if (!allLocations) {
      if (trackingNumberId == null) {
        query += " AND self.stockLocation.id in (" + StockLocationId + ")";
      } else {
        query += " AND self.detailsStockLocation.id in (" + StockLocationId + ")";
      }
    }

    if (company != null) {
      if (trackingNumberId == null) {
        query += " AND self.stockLocation.company = " + company.getId();
      } else {
        query += " AND self.detailsStockLocation.company = " + company.getId();
      }
    }

    BigDecimal qtyTot = BigDecimal.ZERO;
    List<List<Object>> results = JPA.em().createQuery(query).getResultList();
    if (results.isEmpty()) {
      return BigDecimal.ZERO;
    }
    for (List<Object> result : results) {
      BigDecimal qty = (BigDecimal) result.get(2);
      qtyTot = qtyTot.add(qty);
    }
    if (qtyTot.compareTo(BigDecimal.ZERO) == 0) {
      return BigDecimal.ZERO;
    }
    return qtyTot;
  }

  public MetaFile exportToCSV(List<HashMap<String, Object>> historyLines)
      throws AxelorException, IOException {
    List<String[]> allMoveLineData = new ArrayList<>();

    System.out.println("***************************************");
    System.out.println(historyLines.size());
    System.out.println("***************************************");

    for (HashMap<String, Object> historyLine : historyLines) {
      String[] items = new String[6];
      int inc =
          historyLine.get("countIncMvtStockPeriod") != null
              ? Integer.parseInt(historyLine.get("countIncMvtStockPeriod").toString())
              : 0;
      int out =
          historyLine.get("countOutMvtStockPeriod") != null
              ? Integer.parseInt(historyLine.get("countOutMvtStockPeriod").toString())
              : 0;
      String label = historyLine.get("label") != null ? historyLine.get("label").toString() : "";
      BigDecimal sumInc =
          historyLine.get("sumIncQtyPeriod") != null
              ? new BigDecimal(historyLine.get("sumIncQtyPeriod").toString())
              : BigDecimal.ZERO;
      BigDecimal sumOut =
          historyLine.get("sumOutQtyPeriod") != null
              ? new BigDecimal(historyLine.get("sumOutQtyPeriod").toString())
              : BigDecimal.ZERO;
      BigDecimal realQty =
          historyLine.get("realQty") != null
              ? new BigDecimal(historyLine.get("realQty").toString())
              : BigDecimal.ZERO;

      items[0] = label;
      items[1] = String.valueOf(inc);
      items[2] = String.valueOf(sumInc);
      items[3] = String.valueOf(out);
      items[4] = String.valueOf(sumOut);
      items[5] = String.valueOf(realQty);
      allMoveLineData.add(items);
    }

    String filePath = Files.createTempDir().getAbsolutePath();

    if (filePath == null) {
      filePath = AppSettings.get().get("file.upload.dir");
    } else {
      new File(filePath).mkdirs();
    }

    String fileName = I18n.get("StockPerDate") + ".csv";
    Path path = Paths.get(filePath, fileName);
    File file = path.toFile();

    String[] headers = {
      I18n.get("Product Name"),
      I18n.get("Inc"),
      I18n.get("SumInc"),
      I18n.get("Out"),
      I18n.get("SumOut"),
      I18n.get("Real Quantity"),
    };

    CsvTool.csvWriter(filePath, fileName, ';', headers, allMoveLineData);

    try (InputStream is = new FileInputStream(file)) {
      return Beans.get(MetaFiles.class).upload(is, fileName);
    }
  }

  public void sumGroupByStocklocation(
      Long detailsStockLocationId, Long productId, LocalDate date, Long trackingNumberId) {
    Query q =
        JPA.em()
            .createNativeQuery(
                "select coalesce(m2.sum,0) inn,coalesce(m1.sum,0) outt from"
                    + " (SELECT product,line.tracking_number,from_stock_location,sum(real_qty)"
                    + " FROM STOCK_STOCK_MOVE_LINE LINE left join stock_stock_move move on move.id = line.stock_move"
                    + " where (line.archived = false OR line.archived is null) and move.estimated_date >= ?1 "
                    + " group by product,line.tracking_number,from_stock_location) m1"
                    + " full outer join"
                    + " (SELECT product,line.tracking_number,to_stock_location,sum(real_qty)"
                    + " FROM STOCK_STOCK_MOVE_LINE LINE left join stock_stock_move move on move.id = line.stock_move"
                    + " where (line.archived = false OR line.archived is null) and move.estimated_date >= ?2 "
                    + " group by product,line.tracking_number,to_stock_location) m2 "
                    + " on m1.product = m2.product and ((m1.tracking_number  is null and m2.tracking_number  is null) or m1.tracking_number = m2.tracking_number) and m1.from_stock_location = m2.to_stock_location"
                //  " where"+
                //  " coalesce(m1.product, m2.product) =  ?3 and "+
                //  " coalesce(m1.tracking_number,m2.tracking_number) = ?4"+
                //  " and coalesce(m1.from_stock_location,m2.to_stock_location) = ?5"
                );

    q.setParameter(1, date);
    q.setParameter(2, date);
    // q.setParameter(3, productId);
    // q.setParameter(4, trackingNumberId);
    // q.setParameter(5, detailsStockLocationId);

    List<Object[]> resultList = q.getResultList();
    System.out.println("*************resultList***************");
    System.out.println(resultList.toString());
    System.out.println("*************resultList***************");

    for (Object[] result : resultList) {
      System.out.println("*************result***************");
      System.out.println(result.toString());
      System.out.println("*************result***************");
    }
  }
}
