/*
 * 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.supplychain.service;

import com.axelor.apps.account.db.AnalyticMoveLine;
import com.axelor.apps.account.db.repo.AnalyticMoveLineRepository;
import com.axelor.apps.base.db.Company;
import com.axelor.apps.base.db.Product;
import com.axelor.apps.base.db.Unit;
import com.axelor.apps.base.db.repo.ProductRepository;
import com.axelor.apps.base.service.PriceListService;
import com.axelor.apps.base.service.UnitConversionService;
import com.axelor.apps.base.service.app.AppBaseService;
import com.axelor.apps.base.service.tax.AccountManagementService;
import com.axelor.apps.purchase.db.PurchaseOrder;
import com.axelor.apps.purchase.db.PurchaseOrderLine;
import com.axelor.apps.purchase.db.repo.PurchaseOrderLineRepository;
import com.axelor.apps.purchase.db.repo.PurchaseOrderRepository;
import com.axelor.apps.purchase.service.PurchaseOrderLineService;
import com.axelor.apps.purchase.service.PurchaseOrderLineServiceImpl;
import com.axelor.apps.purchase.service.PurchaseOrderServiceImpl;
import com.axelor.apps.sale.db.SaleOrderLine;
import com.axelor.apps.stock.db.InternalTrackingNumber;
import com.axelor.apps.stock.db.StockLocation;
import com.axelor.apps.stock.db.StockLocationLine;
import com.axelor.apps.stock.db.StockMove;
import com.axelor.apps.stock.db.StockMoveLine;
import com.axelor.apps.stock.db.TrackingNumber;
import com.axelor.apps.stock.db.TrackingNumberConfiguration;
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.stock.service.StockLocationLineService;
import com.axelor.apps.stock.service.StockLocationLineServiceImpl;
import com.axelor.apps.stock.service.StockMoveLineServiceImpl;
import com.axelor.apps.stock.service.StockMoveToolService;
import com.axelor.apps.stock.service.TrackingNumberService;
import com.axelor.apps.stock.service.WeightedAveragePriceService;
import com.axelor.apps.stock.service.app.AppStockService;
import com.axelor.apps.supplychain.exception.IExceptionMessage;
import com.axelor.db.JPA;
import com.axelor.exception.AxelorException;
import com.axelor.exception.db.repo.TraceBackRepository;
import com.axelor.exception.service.TraceBackService;
import com.axelor.i18n.I18n;
import com.axelor.inject.Beans;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import com.google.inject.servlet.RequestScoped;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@RequestScoped
public class StockMoveLineServiceSupplychainImpl extends StockMoveLineServiceImpl
    implements StockMoveLineServiceSupplychain {

  protected AccountManagementService accountManagementService;
  protected PriceListService priceListService;
  protected PurchaseOrderLineRepository purchaseOrderLineRepo;
  protected PurchaseOrderLineService purchaseOrderLineService;
  protected List<StockMoveLine> stockMoveLines;

  @Inject
  public StockMoveLineServiceSupplychainImpl(
      TrackingNumberService trackingNumberService,
      AppBaseService appBaseService,
      AppStockService appStockService,
      StockMoveToolService stockMoveToolService,
      StockMoveLineRepository stockMoveLineRepository,
      StockLocationLineService stockLocationLineService,
      UnitConversionService unitConversionService,
      WeightedAveragePriceService weightedAveragePriceService,
      TrackingNumberRepository trackingNumberRepo,
      AccountManagementService accountManagementService,
      PurchaseOrderLineRepository purchaseOrderLineRepository,
      PurchaseOrderLineService purchaseOrderLineService,
      PriceListService priceListService) {
    super(
        trackingNumberService,
        appBaseService,
        appStockService,
        stockMoveToolService,
        stockMoveLineRepository,
        stockLocationLineService,
        unitConversionService,
        weightedAveragePriceService,
        trackingNumberRepo);
    this.accountManagementService = accountManagementService;
    this.priceListService = priceListService;
    this.stockMoveLines = new ArrayList<>();
  }

  @Override
  public StockMoveLine createStockMoveLine(
      Product product,
      String productName,
      String description,
      BigDecimal quantity,
      BigDecimal requestedReservedQty,
      BigDecimal unitPrice,
      BigDecimal companyUnitPriceUntaxed,
      Unit unit,
      StockMove stockMove,
      int type,
      boolean taxed,
      BigDecimal taxRate,
      SaleOrderLine saleOrderLine,
      PurchaseOrderLine purchaseOrderLine)
      throws AxelorException {
    if (product != null) {

      StockMoveLine stockMoveLine =
          generateStockMoveLineConvertingUnitPrice(
              product,
              productName,
              description,
              quantity,
              unitPrice,
              companyUnitPriceUntaxed,
              unit,
              stockMove,
              taxed,
              taxRate);
      stockMoveLine.setRequestedReservedQty(BigDecimal.ZERO);
      stockMoveLine.setSaleOrderLine(saleOrderLine);
      stockMoveLine.setPurchaseOrderLine(purchaseOrderLine);
      if (saleOrderLine != null) {
        stockMoveLine.setShp(saleOrderLine.getShp());
        stockMoveLine.setPpa(saleOrderLine.getPpa());
        stockMoveLine.setPvg(saleOrderLine.getPvg());
        stockMoveLine.setStklim(saleOrderLine.getStklim());
        stockMoveLine.setUg(saleOrderLine.getUg());
      }
      TrackingNumberConfiguration trackingNumberConfiguration =
          product.getTrackingNumberConfiguration();

      if(purchaseOrderLine != null){
        System.out.println("Stepper loading ::::::::::::::::");
        // this.inheretAccountAnalyticMoveLine(purchaseOrderLine,stockMoveLine);
      }

      return assignOrGenerateTrackingNumber(
          stockMoveLine, stockMove, product, trackingNumberConfiguration, type);
    } else {
      return this.createStockMoveLine(
          product,
          productName,
          description,
          quantity,
          BigDecimal.ZERO,
          BigDecimal.ZERO,
          companyUnitPriceUntaxed,
          unit,
          stockMove,
          null);
    }
  }

  @Transactional
  private void inheretAccountAnalyticMoveLine(PurchaseOrderLine purchaseOrderLine, StockMoveLine stockMoveLine) {
      List<AnalyticMoveLine> analyticMoveLines = purchaseOrderLine.getAnalyticMoveLineList();
      if(analyticMoveLines.size() != 0){
        for (AnalyticMoveLine analyticMoveLine : analyticMoveLines) {
          AnalyticMoveLine aml = Beans.get(AnalyticMoveLineRepository.class).copy(analyticMoveLine, false);
          aml.setStockMoveLine(stockMoveLine);
          stockMoveLine.addAnalyticMoveLineListItem(aml);
        }
      }
  }

  @Override
  public StockMoveLine compute(StockMoveLine stockMoveLine, StockMove stockMove)
      throws AxelorException {

    // the case when stockMove is null is made in super.
    if (stockMove == null) {
      return super.compute(stockMoveLine, null);
    }

    // if this is a pack do not compute price
    if (stockMoveLine.getProduct() == null
        || (stockMoveLine.getLineTypeSelect() != null
            && stockMoveLine.getLineTypeSelect() == StockMoveLineRepository.TYPE_PACK)) {
      return stockMoveLine;
    }

    if (stockMove.getOriginId() != null && stockMove.getOriginId() != 0L) {
      // the stock move comes from a sale or purchase order, we take the price from the order.
      stockMoveLine = computeFromOrder(stockMoveLine, stockMove);
    } else {
      stockMoveLine = super.compute(stockMoveLine, stockMove);
    }
    return stockMoveLine;
  }

  protected StockMoveLine computeFromOrder(StockMoveLine stockMoveLine, StockMove stockMove)
      throws AxelorException {
    BigDecimal unitPriceUntaxed = stockMoveLine.getUnitPriceUntaxed();
    BigDecimal unitPriceTaxed = stockMoveLine.getUnitPriceTaxed();
    Unit orderUnit = null;
    if (StockMoveRepository.ORIGIN_SALE_ORDER.equals(stockMove.getOriginTypeSelect())) {
      SaleOrderLine saleOrderLine = stockMoveLine.getSaleOrderLine();
      if (saleOrderLine == null) {
        // log the exception
        TraceBackService.trace(
            new AxelorException(
                TraceBackRepository.TYPE_TECHNICAL,
                IExceptionMessage.STOCK_MOVE_MISSING_SALE_ORDER,
                stockMove.getOriginId(),
                stockMove.getName()));
      } else {
        unitPriceUntaxed = saleOrderLine.getPriceDiscounted();
        unitPriceTaxed = saleOrderLine.getInTaxPrice();
        orderUnit = saleOrderLine.getUnit();
      }
    } else if (StockMoveRepository.ORIGIN_PURCHASE_ORDER.equals(stockMove.getOriginTypeSelect())) {
      PurchaseOrderLine purchaseOrderLine = stockMoveLine.getPurchaseOrderLine();
      if (purchaseOrderLine == null) {
        // log the exception
        TraceBackService.trace(
            new AxelorException(
                TraceBackRepository.TYPE_TECHNICAL,
                IExceptionMessage.STOCK_MOVE_MISSING_PURCHASE_ORDER,
                stockMove.getOriginId(),
                stockMove.getName()));
      } else {
        unitPriceUntaxed = purchaseOrderLine.getPrice();
        unitPriceTaxed = purchaseOrderLine.getInTaxPrice();
        orderUnit = purchaseOrderLine.getUnit();
      }
    }

    stockMoveLine.setUnitPriceUntaxed(unitPriceUntaxed);
    stockMoveLine.setUnitPriceTaxed(unitPriceTaxed);

    Unit stockUnit = getStockUnit(stockMoveLine);
    return convertUnitPrice(stockMoveLine, orderUnit, stockUnit);
  }

  protected StockMoveLine convertUnitPrice(StockMoveLine stockMoveLine, Unit fromUnit, Unit toUnit)
      throws AxelorException {
    // convert units
    if (toUnit != null && fromUnit != null) {
      BigDecimal unitPriceUntaxed =
          unitConversionService.convert(
              fromUnit,
              toUnit,
              stockMoveLine.getUnitPriceUntaxed(),
              appBaseService.getNbDecimalDigitForSalePrice(),
              null);
      BigDecimal unitPriceTaxed =
          unitConversionService.convert(
              fromUnit,
              toUnit,
              stockMoveLine.getUnitPriceTaxed(),
              appBaseService.getNbDecimalDigitForSalePrice(),
              null);
      stockMoveLine.setUnitPriceUntaxed(unitPriceUntaxed);
      stockMoveLine.setUnitPriceTaxed(unitPriceTaxed);
    }
    return stockMoveLine;
  }

  @Override
  public StockMoveLine splitStockMoveLine(
      StockMoveLine stockMoveLine, BigDecimal qty, TrackingNumber trackingNumber)
      throws AxelorException {

    StockMoveLine newStockMoveLine = super.splitStockMoveLine(stockMoveLine, qty, trackingNumber);

    BigDecimal reservedQtyAfterSplit =
        BigDecimal.ZERO.max(stockMoveLine.getRequestedReservedQty().subtract(qty));
    BigDecimal reservedQtyInNewLine = stockMoveLine.getRequestedReservedQty().min(qty);
    stockMoveLine.setRequestedReservedQty(reservedQtyAfterSplit);
    newStockMoveLine.setRequestedReservedQty(reservedQtyInNewLine);
    newStockMoveLine.setSaleOrderLine(stockMoveLine.getSaleOrderLine());
    newStockMoveLine.setPurchaseOrderLine(stockMoveLine.getPurchaseOrderLine());
    return newStockMoveLine;
  }

  @Override
  public void updateAvailableQty(StockMoveLine stockMoveLine, StockLocation stockLocation) {
    BigDecimal availableQty = BigDecimal.ZERO;
    BigDecimal availableQtyForProduct = BigDecimal.ZERO;

    if (stockMoveLine.getProduct() != null) {
      if (stockMoveLine.getProduct().getTrackingNumberConfiguration() != null) {

        if (stockMoveLine.getTrackingNumber() != null) {
          StockLocationLine stockLocationLine =
              stockLocationLineService.getDetailLocationLine(
                  stockLocation, stockMoveLine.getProduct(), stockMoveLine.getTrackingNumber());

          if (stockLocationLine != null) {
            availableQty =
                stockLocationLine
                    .getCurrentQty()
                    .add(stockMoveLine.getReservedQty())
                    .subtract(stockLocationLine.getReservedQty());
          }
        }

        if (availableQty.compareTo(stockMoveLine.getRealQty()) < 0) {
          StockLocationLine stockLocationLineForProduct =
              stockLocationLineService.getStockLocationLine(
                  stockLocation, stockMoveLine.getProduct());

          if (stockLocationLineForProduct != null) {
            availableQtyForProduct =
                stockLocationLineForProduct
                    .getCurrentQty()
                    .add(stockMoveLine.getReservedQty())
                    .subtract(stockLocationLineForProduct.getReservedQty());
          }
        }
      } else {
        StockLocationLine stockLocationLine =
            stockLocationLineService.getStockLocationLine(
                stockLocation, stockMoveLine.getProduct());

        if (stockLocationLine != null) {
          availableQty =
              stockLocationLine
                  .getCurrentQty()
                  .add(stockMoveLine.getReservedQty())
                  .subtract(stockLocationLine.getReservedQty());
        }
      }
    }
    stockMoveLine.setAvailableQty(availableQty);
    stockMoveLine.setAvailableQtyForProduct(availableQtyForProduct);
  }

  @Override
  public StockMoveLine getMergedStockMoveLine(List<StockMoveLine> stockMoveLineList)
      throws AxelorException {

    if (stockMoveLineList == null || stockMoveLineList.isEmpty()) {
      return null;
    }

    if (stockMoveLineList.size() == 1) {
      return stockMoveLineList.get(0);
    }

    SaleOrderLine saleOrderLine = stockMoveLineList.get(0).getSaleOrderLine();
    PurchaseOrderLine purchaseOrderLine = stockMoveLineList.get(0).getPurchaseOrderLine();

    Product product;
    String productName;
    String description;
    BigDecimal quantity = BigDecimal.ZERO;
    Unit unit;

    if (saleOrderLine != null) {
      product = saleOrderLine.getProduct();
      productName = saleOrderLine.getProductName();
      description = saleOrderLine.getDescription();
      unit = saleOrderLine.getUnit();

    } else if (purchaseOrderLine != null) {
      product = purchaseOrderLine.getProduct();
      productName = purchaseOrderLine.getProductName();
      description = purchaseOrderLine.getDescription();
      unit = purchaseOrderLine.getUnit();

    } else {
      return null; // shouldn't ever happen or you misused this function
    }

    for (StockMoveLine stockMoveLine : stockMoveLineList) {
      quantity = quantity.add(stockMoveLine.getRealQty());
    }

    StockMoveLine generatedStockMoveLine =
        createStockMoveLine(
            product,
            productName,
            description,
            quantity,
            BigDecimal.ZERO,
            BigDecimal.ZERO,
            BigDecimal.ZERO,
            unit,
            null,
            null);

    generatedStockMoveLine.setSaleOrderLine(saleOrderLine);
    generatedStockMoveLine.setPurchaseOrderLine(purchaseOrderLine);
    return generatedStockMoveLine;
  }

  /**
   * This implementation copy fields from sale order line and purchase order line if the link
   * exists.
   *
   * @param stockMove
   * @param stockMoveLine
   * @param company
   */
  @Override
  public void setProductInfo(StockMove stockMove, StockMoveLine stockMoveLine, Company company)
      throws AxelorException {
    Preconditions.checkNotNull(stockMoveLine);
    Preconditions.checkNotNull(company);

    Product product = stockMoveLine.getProduct();

    if (product == null) {
      return;
    }
    super.setProductInfo(stockMove, stockMoveLine, company);

    SaleOrderLine saleOrderLine = stockMoveLine.getSaleOrderLine();
    PurchaseOrderLine purchaseOrderLine = stockMoveLine.getPurchaseOrderLine();

    if (saleOrderLine != null) {
      setProductInfoFromSaleOrder(stockMoveLine, saleOrderLine);
    }
    if (purchaseOrderLine != null) {
      setProductInfoFromPurchaseOrder(stockMoveLine, purchaseOrderLine);
    }
  }

  protected void setProductInfoFromSaleOrder(
      StockMoveLine stockMoveLine, SaleOrderLine saleOrderLine) {

    stockMoveLine.setUnit(saleOrderLine.getUnit());
    stockMoveLine.setProductName(saleOrderLine.getProductName());
    if (Strings.isNullOrEmpty(stockMoveLine.getDescription())) {
      stockMoveLine.setDescription(saleOrderLine.getDescription());
    }
    stockMoveLine.setQty(saleOrderLine.getQty());
  }

  protected void setProductInfoFromPurchaseOrder(
      StockMoveLine stockMoveLine, PurchaseOrderLine purchaseOrderLine) {

    stockMoveLine.setUnit(purchaseOrderLine.getUnit());
    stockMoveLine.setProductName(purchaseOrderLine.getProductName());
    if (Strings.isNullOrEmpty(stockMoveLine.getDescription())) {
      stockMoveLine.setDescription(purchaseOrderLine.getDescription());
    }
    stockMoveLine.setQty(purchaseOrderLine.getQty());
  }

  @Override
  public boolean isAvailableProduct(StockMove stockMove, StockMoveLine stockMoveLine) {
    if (stockMoveLine.getProduct() == null
        || (stockMoveLine.getProduct() != null && !stockMoveLine.getProduct().getStockManaged())) {
      return true;
    }
    updateAvailableQty(stockMoveLine, stockMove.getFromStockLocation());
    BigDecimal availableQty = stockMoveLine.getAvailableQty();
    if (stockMoveLine.getProduct().getTrackingNumberConfiguration() != null
        && stockMoveLine.getTrackingNumber() == null) {
      availableQty = stockMoveLine.getAvailableQtyForProduct();
    }
    BigDecimal realQty = stockMoveLine.getRealQty();
    if (availableQty.compareTo(realQty) < 0) {
      return false;
    }
    return true;
  }

  @Override
  public void setInvoiceStatus(StockMoveLine stockMoveLine) {
    if (stockMoveLine.getQtyInvoiced().compareTo(BigDecimal.ZERO) == 0) {
      stockMoveLine.setAvailableStatus(I18n.get("Not invoiced"));
      stockMoveLine.setAvailableStatusSelect(3);
    } else if (stockMoveLine.getQtyInvoiced().compareTo(stockMoveLine.getRealQty()) == -1) {
      stockMoveLine.setAvailableStatus(I18n.get("Partially invoiced"));
      stockMoveLine.setAvailableStatusSelect(4);
    } else if (stockMoveLine.getQtyInvoiced().compareTo(stockMoveLine.getRealQty()) == 0) {
      stockMoveLine.setAvailableStatus(I18n.get("Invoiced"));
      stockMoveLine.setAvailableStatusSelect(1);
    }
  }

  @Transactional
  public void resetValorization(
      Long productId,
      Long stockMoveId,
      LocalDate toDateTime,
      Boolean excludeValotisationStocklocation) {
    StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveId);
    Product product = Beans.get(ProductRepository.class).find(productId);

    if (excludeValotisationStocklocation) {
      this.stockMoveLines = getStockMoveLines(product, toDateTime, stockMove, true);
    } else {
      this.stockMoveLines = getStockMoveLines(product, toDateTime, stockMove, false);
    }

    List<StockMoveLine> lines = new ArrayList<>();

    if (this.stockMoveLines.size() > 1) {
      lines = sliceStockMoveLines(this.stockMoveLines, stockMoveId, product);
    } else {
      lines.addAll(stockMoveLines);
    }

    for (StockMoveLine stockMoveLine : lines) {
      stockMoveLine.setIsWapUpdated(false);
      Beans.get(StockMoveLineRepository.class).save(stockMoveLine);
    }
  }

  public void fillStockMoveLines(
      Long productId,
      LocalDate toDateTime,
      Long stockMoveId,
      Boolean excludeValotisationStocklocation) {
    StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveId);
    Product product = Beans.get(ProductRepository.class).find(productId);
    this.stockMoveLines =
        getStockMoveLines(product, toDateTime, stockMove, excludeValotisationStocklocation);

    System.out.println("***********************stockMoveLines****************************");
    System.out.println(this.stockMoveLines);
    System.out.println("***********************stockMoveLines****************************");
  }

  @Transactional
  public void valorize(
      Long productId,
      Long stockMoveId,
      LocalDate toDateTime,
      BigDecimal price,
      Boolean updatePo,
      Boolean excludeValotisationStocklocation)
      throws AxelorException {
    StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveId);
    Product product = Beans.get(ProductRepository.class).find(productId);
    int scale = appBaseService.getNbDecimalDigitForUnitPrice();

    this.updateStockMoveLinePrices(product, price, stockMove, false, null);

    // if(updatePo){
    //   this.updatePurchaseOrderPrices(stockMove, product, price);
    // }

    List<StockMoveLine> lines = sliceStockMoveLines(this.stockMoveLines, stockMoveId, product);

    BigDecimal currentQty = this.getTotalQty(product, stockMove.getCompany(), false);
    BigDecimal oldWap = this.getPreviousWap(lines.get(0), false);

    if (excludeValotisationStocklocation) {
      currentQty = this.getTotalQty(product, stockMove.getCompany(), true);
      oldWap = this.getPreviousWap(lines.get(0), true);
    }

    for (StockMoveLine stockMoveLine : lines) {
      if (stockMoveLine.getStockMove().getFromStockLocation().getTypeSelect()
              == StockLocationRepository.TYPE_VIRTUAL
          && stockMoveLine.getStockMove().getToStockLocation().getId() != 6L) {
        currentQty = currentQty.subtract(stockMoveLine.getRealQty());
      } else if (stockMoveLine.getStockMove().getToStockLocation().getTypeSelect()
              == StockLocationRepository.TYPE_VIRTUAL
          || (stockMoveLine.getStockMove().getToStockLocation().getId() == 6L
              && stockMoveLine.getStockMove().getFromStockLocation().getTypeSelect()
                  != StockLocationRepository.TYPE_VIRTUAL)) {
        currentQty = currentQty.add(stockMoveLine.getRealQty());
      }
      System.out.println("..................>>>>>>>>>>>>>>>><<<<<" + currentQty.toString());
    }

    BigDecimal newQty = lines.get(0).getRealQty();
    BigDecimal newPrice = price;

    BigDecimal newWap =
        newQty
            .multiply(newPrice)
            .add(currentQty.multiply(oldWap))
            .divide(currentQty.add(newQty), scale, BigDecimal.ROUND_HALF_UP);

    this.updateStockMoveLinePrices(product, price, stockMove, true, newWap);

    int index = 1;
    List<StockMoveLine> lines2 = new ArrayList<>();
    if (lines.size() > 1) {
      lines2 = this.stockMoveLineStartingFromIndex(index, lines);
    }

    for (StockMoveLine stockMoveLine : lines2) {
      // stockMoveLine.getStockMove().getFromStockLocation().getTypeSelect() !=
      // StockLocationRepository.TYPE_VIRTUAL &&

      if (stockMoveLine.getStockMove().getPartner() == stockMove.getCompany().getPartner()
          || stockMoveLine.getStockMove().getPartner() == null) {
        stockMoveLine.setUnitPriceTaxed(newWap);
        stockMoveLine.setUnitPriceUntaxed(newWap);
        stockMoveLine.setCompanyUnitPriceUntaxed(newWap);
        stockMoveLine.setWapPrice(newWap);
        stockMoveLine.setIsWapUpdated(true);
        Beans.get(StockMoveLineRepository.class).save(stockMoveLine);
      } else if (stockMoveLine.getStockMove().getPartner().getId()
              != stockMove.getCompany().getPartner().getId()
          && stockMoveLine.getIsWapUpdated() == false) {
        index++;
        this.valorize(
            productId,
            stockMoveLine.getStockMove().getId(),
            toDateTime,
            stockMoveLine.getUnitPriceUntaxed(),
            updatePo,
            excludeValotisationStocklocation);
        break;
      }
    }
  }

  private void updatePurchaseOrderPrices(StockMove stockMove, Product product, BigDecimal price)
      throws AxelorException {
    if (StockMoveRepository.ORIGIN_PURCHASE_ORDER.equals(stockMove.getOriginTypeSelect())
        && stockMove.getOriginId() != null) {
      PurchaseOrder purchaseOrder =
          Beans.get(PurchaseOrderRepository.class).find(stockMove.getOriginId());
      purchaseOrder.setCurrency(purchaseOrder.getCompany().getCurrency());
      List<PurchaseOrderLine> purchaseOrderLines =
          purchaseOrder
              .getPurchaseOrderLineList()
              .stream()
              .filter(t -> t.getProduct().getId() == product.getId())
              .collect(Collectors.toList());

      // if(purchaseOrderLines == null || purchaseOrderLines.isEmpty()){
      //   return;
      // }
      for (PurchaseOrderLine purchaseOrderLine : purchaseOrderLines) {
        this.updatePrice(purchaseOrderLine, price);
      }
      Beans.get(PurchaseOrderRepository.class).save(purchaseOrder);
      Beans.get(PurchaseOrderServiceImpl.class)._computePurchaseOrder(purchaseOrder);
    }
  }

  public List<StockMoveLine> stockMoveLineStartingFromIndex(
      int index, List<StockMoveLine> stockMoveLines) {
    List<StockMoveLine> newStockMoveLines = new ArrayList<>();
    if (index > stockMoveLines.size()) {
      return newStockMoveLines;
    }
    for (int i = index; i < stockMoveLines.size(); i++) {
      newStockMoveLines.add(stockMoveLines.get(i));
    }
    return newStockMoveLines;
  }

  @Transactional
  private void updateStockMoveLinePrices(
      Product product,
      BigDecimal price,
      StockMove stockMove,
      boolean updatePmp,
      BigDecimal newWapPrice) {
    List<StockMoveLine> stockMoveLines =
        // stockMoveLineRepository
        //     .all()
        //     .filter(
        //         "self.stockMove.id = ?1 and self.product.id = ?2 and self.stockMove.statusSelect
        // = 3 and self.realQty > 0",
        //         stockMoveId,
        //         productId)
        //     .fetch();
        stockMove
            .getStockMoveLineList()
            .stream()
            .filter(
                t ->
                    (t.getProduct().getId() == product.getId()
                        && t.getRealQty().compareTo(BigDecimal.ZERO) > 0))
            .collect(Collectors.toList());
    if (!updatePmp) {
      for (StockMoveLine stockMoveLine : stockMoveLines) {
        stockMoveLine.setCompanyUnitPriceUntaxed(price);
        stockMoveLine.setUnitPriceTaxed(price);
        stockMoveLine.setUnitPriceUntaxed(price);
        Beans.get(StockMoveLineRepository.class).save(stockMoveLine);
      }
      stockMove.setExTaxTotal(stockMoveToolService.compute(stockMove));
      Beans.get(StockMoveRepository.class).save(stockMove);
    } else {
      // Product product = Beans.get(ProductRepository.class).find(productId);
      for (StockMoveLine stockMoveLine : stockMoveLines) {
        stockMoveLine.setWapPrice(newWapPrice);
        stockMoveLine.setIsWapUpdated(true);
        product.setAvgPrice(newWapPrice);
        List<StockLocationLine> locationLines =
            Beans.get(StockLocationLineServiceImpl.class).getStockLocationLines(product);
        for (StockLocationLine stockLocationLine : locationLines) {
          stockLocationLine.setAvgPrice(newWapPrice);
          Beans.get(StockLocationLineRepository.class).save(stockLocationLine);
        }
        Beans.get(StockMoveLineRepository.class).save(stockMoveLine);
        Beans.get(ProductRepository.class).save(product);
      }
    }
  }

  @Transactional
  public void updatePrice(PurchaseOrderLine purchaseOrderLine, BigDecimal price)
      throws AxelorException {
    System.out.println("updateOrderLinePrices......................");
    System.out.println(purchaseOrderLine.toString());
    purchaseOrderLine.setPrice(price);
    purchaseOrderLine.setPriceDiscounted(price);
    Beans.get(PurchaseOrderLineServiceImpl.class)
        .compute(purchaseOrderLine, purchaseOrderLine.getPurchaseOrder());
    Beans.get(PurchaseOrderLineRepository.class).save(purchaseOrderLine);
  }

  public List<StockMoveLine> getStockMoveLines(
      Product product,
      LocalDate toDate,
      StockMove stockMove,
      Boolean excludeValotisationStocklocation) {

    String query =
        "self.stockMove.statusSelect = ?1 AND self.stockMove.realDate <= ?2  AND self.product = ?3  AND self.stockMove.realDate >= ?4 AND self.realQty > 0 AND (self.archived is null OR self.archived is false)";

    if (excludeValotisationStocklocation) {
      query +=
          " AND (self.stockMove.toStockLocation.excludeValorisation is true or self.stockMove.fromStockLocation.excludeValorisation is true)";
    } else {
      query +=
          " AND ((self.stockMove.toStockLocation.excludeValorisation is false or self.stockMove.toStockLocation.excludeValorisation is null) AND (self.stockMove.fromStockLocation.excludeValorisation is false or self.stockMove.fromStockLocation.excludeValorisation is null))";
    }

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

    List<Object> params = Lists.newArrayList();
    params.add(StockMoveRepository.STATUS_REALIZED);
    params.add(toDate);
    params.add(product);
    params.add(stockMove.getRealDate());
    // params.add(stockMove.getRealDate());
    // params.add(stockMove.getId());
    List<StockMoveLine> lines =
        stockMoveLineRepository
            .all()
            .filter(query, params.toArray())
            .order("stockMove.realDate")
            // .order("id")
            .fetch();

    return lines;
  }

  public BigDecimal getPreviousWap(
      StockMoveLine stockMoveLine, Boolean excludeValotisationStocklocation) {
    String query =
        "self.stockMove.statusSelect = ?1 AND self.product = ?2 and self.stockMove.realDate < ?3 AND self.realQty > 0";

    if (excludeValotisationStocklocation) {
      query +=
          " AND (self.stockMove.toStockLocation.excludeValorisation is true or self.stockMove.fromStockLocation.excludeValorisation is true)";
    }

    StockMoveLine l = stockMoveLineRepository.find(stockMoveLine.getId());
    List<Object> params = Lists.newArrayList();
    params.add(StockMoveRepository.STATUS_REALIZED);
    params.add(l.getProduct());
    params.add(l.getStockMove().getRealDate());

    StockMoveLine line =
        stockMoveLineRepository
            .all()
            .order("-stockMove.realDate")
            .order("-id")
            .filter(query, params.toArray())
            .fetchOne();

    if (line == null || line.getWapPrice() == null) {
      return stockMoveLine.getUnitPriceUntaxed();
    }
    return line.getWapPrice();
  }

  @SuppressWarnings({"unchecked", "rawtypes"})
  public BigDecimal getTotalQty(
      Product product, Company company, Boolean excludeValotisationStocklocation) {
    Long productId = product.getId();
    String query =
        "SELECT new list(self.id, self.avgPrice, self.currentQty) FROM StockLocationLine as self "
            + "WHERE self.product.id = "
            + productId
            + "AND self.stockLocation.id not in (6)  AND self.stockLocation.typeSelect != "
            + StockLocationRepository.TYPE_VIRTUAL;

    if (excludeValotisationStocklocation) {
      query += " AND (self.stockLocation.excludeValorisation is true)";
    } else {
      query +=
          " AND (self.stockLocation.excludeValorisation is false or  self.stockLocation.excludeValorisation is null)";
    }

    if (company != null) {
      query += " AND self.stockLocation.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;
    }

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

    return qtyTot;
  }

  // lines
  public List<StockMoveLine> lines = new ArrayList<>();

  public void showLines(Long productId, Long stockMoveId, LocalDate toDateTime, BigDecimal price) {
    StockMove stockMove = Beans.get(StockMoveRepository.class).find(stockMoveId);
    Product product = Beans.get(ProductRepository.class).find(productId);
    List<StockMoveLine> lines = getStockMoveLines(product, toDateTime, stockMove, false);
    System.out.println("*****************************************************");
    for (StockMoveLine line : lines) {
      System.out.println(line.getStockMove().getStockMoveSeq());
    }
    System.out.println("*****************************************************");
    int index = 1;
    List<StockMoveLine> lines2 = this.stockMoveLineStartingFromIndex(index, lines);
    System.out.println("********************lines2*********************************");
    for (StockMoveLine stockMoveLine : lines2) {
      if (stockMoveLine.getStockMove().getPartner() == stockMove.getCompany().getPartner()
          || stockMoveLine.getStockMove().getPartner() == null) {
        System.out.println("Interne ***** " + stockMoveLine.getStockMove().getStockMoveSeq());
      } else if (stockMoveLine.getStockMove().getPartner().getId()
          != stockMove.getCompany().getPartner().getId()) {
        index++;
        System.out.println(
            "Externe ***** "
                + stockMoveLine.getStockMove().getStockMoveSeq()
                + " ,id : "
                + stockMoveLine.getStockMove().getId());
        this.showLines(productId, stockMoveLine.getStockMove().getId(), toDateTime, price);
        break;
      }
    }
    System.out.println("***********************lines2******************************");
  }

  public void valorizeAll(LocalDate fromDateTime, Long productCategoryId) throws AxelorException {
    // ,67
    String query =
        "self.stockMove.statusSelect = ?1 "
            + "and self.stockMove.realDate >= ?2  and self.realQty > 0 and "
            + "self.product.familleProduit.id in ("
            + String.valueOf(productCategoryId)
            + ") and self.stockMove.fromStockLocation.id = 12 and self.stockMove.partner.id != 853";
    List<Object> params = Lists.newArrayList();
    params.add(StockMoveRepository.STATUS_REALIZED);
    params.add(fromDateTime);

    List<StockMoveLine> lines =
        stockMoveLineRepository
            .all()
            .filter(query, params.toArray())
            .order("stockMove.realDate")
            .order("id")
            .fetch();

    List<StockMoveLine> distinctLines = new ArrayList<>();
    List<Product> products = new ArrayList<>();

    for (StockMoveLine line : lines) {
      if (products.indexOf(line.getProduct()) == -1) {
        distinctLines.add(line);
        products.add(line.getProduct());
      }
    }

    int index = lines.size();
    for (StockMoveLine line : distinctLines) {
      System.out.println("Starting ............... " + String.valueOf(index));
      System.out.println("Size ............... " + String.valueOf(distinctLines.size()));
      System.out.println(line.getStockMove().getStockMoveSeq());
      System.out.println(line.getProductName());
      System.out.println(line.getProduct().getId());
      this.resetValorization(
          line.getProduct().getId(), line.getStockMove().getId(), LocalDate.now(), false);
      this.fillStockMoveLines(
          line.getProduct().getId(), LocalDate.now(), line.getStockMove().getId(), false);
      this.valorize(
          line.getProduct().getId(),
          line.getStockMove().getId(),
          LocalDate.now(),
          line.getUnitPriceUntaxed(),
          false,
          false);
      System.out.println("End .....................");
      index--;
    }
  }

  public List<StockMoveLine> sliceStockMoveLines(
      List<StockMoveLine> lines, Long stockMoveId, Product product) {

    StockMoveLine emp =
        lines
            .stream()
            .filter(
                (t) -> {
                  return t.getStockMove().getId() == stockMoveId && t.getProduct() == product;
                })
            .findFirst()
            .orElse(null);

    int index = lines.indexOf(emp);

    List<StockMoveLine> slicedLines = new ArrayList<>();

    for (int i = index; i < lines.size(); i++) {
      slicedLines.add(lines.get(i));
    }
    return slicedLines;
  }
}
