<?php

namespace common\logic\Distribution\Distribution\Export;

use common\helpers\CSV;
use common\logic\AbstractGenerator;
use common\logic\Config\Unit\Service\UnitSolver;
use common\logic\Distribution\Distribution\Model\Distribution;
use common\logic\Order\Order\Repository\OrderRepository;
use common\logic\Order\Order\Service\OrderBuilder;
use common\logic\Order\Order\Service\OrderSolver;
use common\logic\PointSale\PointSale\Repository\PointSaleRepository;
use common\logic\Producer\Producer\Repository\ProducerRepository;
use common\logic\Producer\Producer\Service\ProducerSolver;
use common\logic\Product\Product\Model\Product;
use common\logic\Product\Product\Repository\ProductRepository;
use common\logic\Product\Product\Service\ProductSolver;

class DistributionReportCsvGenerator extends AbstractGenerator implements DistributionExportGeneratorInterface
{
    protected UnitSolver $unitSolver;
    protected ProducerRepository $producerRepository;
    protected ProductRepository $productRepository;
    protected ProductSolver $productSolver;
    protected OrderRepository $orderRepository;
    protected OrderSolver $orderSolver;
    protected PointSaleRepository $pointSaleRepository;
    protected OrderBuilder $orderBuilder;
    protected ProducerSolver $producerSolver;

    public function loadDependencies(): void
    {
        $this->unitSolver = $this->loadService(UnitSolver::class);
        $this->producerRepository = $this->loadService(ProducerRepository::class);
        $this->productRepository = $this->loadService(ProductRepository::class);
        $this->productSolver = $this->loadService(ProductSolver::class);
        $this->orderRepository = $this->loadService(OrderRepository::class);
        $this->orderSolver = $this->loadService(OrderSolver::class);
        $this->pointSaleRepository = $this->loadService(PointSaleRepository::class);
        $this->orderBuilder = $this->loadService(OrderBuilder::class);
        $this->producerSolver = $this->loadService(ProducerSolver::class);
    }

    public function generate(Distribution $distribution, bool $save = false)
    {
        $datas = [];
        $ordersArray = $this->orderRepository->findOrdersByDistribution($distribution);
        $productsArray = $this->productRepository->findProductsByDistribution($distribution);
        $optionCsvExportAllProducts = $this->producerSolver->getConfig('option_csv_export_all_products');
        $optionCsvExportByPiece = $this->producerSolver->getConfig('option_csv_export_by_piece');
        $pointsSaleArray = $this->pointSaleRepository->findPointSales();
        foreach ($pointsSaleArray as $pointSale) {
            $this->orderBuilder->initPointSaleOrders($pointSale, $ordersArray);
        }

        // produits en colonne
        $productsNameArray = ['', 'Commentaire'];
        $productsIndexArray = [];
        $productsHasQuantity = [];
        $cpt = 2;
        foreach ($productsArray as $product) {
            $productsHasQuantity[$product->id] = 0;
            foreach (Product::$unitsArray as $unit => $dataUnit) {
                $quantity = $this->orderSolver->getProductQuantity($product, $ordersArray, true, $unit);
                if ($quantity) {
                    $productsHasQuantity[$product->id] += $quantity;
                }
            }
            if ($productsHasQuantity[$product->id] > 0 || $optionCsvExportAllProducts) {
                $productName = $this->productSolver->getNameExport($product);

                if ($optionCsvExportByPiece) {
                    $productUnit = 'piece';
                } else {
                    $productUnit = $product->unit;
                }

                $productName .= ' (' . $this->unitSolver->strUnit($productUnit, 'wording_short', true) . ')';

                $productsNameArray[] = $productName;
                $productsIndexArray[$product->id] = $cpt++;
            }
        }
        $datas[] = $productsNameArray;

        // points de vente
        foreach ($pointsSaleArray as $pointSale) {
            if (count($pointSale->orders)) {
                // listing commandes
                $datas[] = ['> ' . $pointSale->name];
                foreach ($pointSale->orders as $order) {
                    $orderLine = [$this->orderSolver->getOrderUsername($order), $this->orderSolver->getCommentReport($order)];

                    if ($optionCsvExportByPiece) {
                        foreach ($order->productOrder as $productOrder) {
                            $orderLine[$productsIndexArray[$productOrder->id_product]] = $this->orderSolver->getProductQuantityPiecesByOrders(
                                $productOrder->product,
                                [$order]
                            );
                        }
                    } else {
                        foreach ($productsIndexArray as $idProduct => $indexProduct) {
                            $orderLine[$indexProduct] = '';
                        }

                        foreach ($order->productOrder as $productOrder) {
                            if (strlen($orderLine[$productsIndexArray[$productOrder->id_product]])) {
                                $orderLine[$productsIndexArray[$productOrder->id_product]] .= ' + ';
                            }
                            $orderLine[$productsIndexArray[$productOrder->id_product]] .= $productOrder->quantity;
                            if ($productOrder->product->unit != $productOrder->unit) {
                                $orderLine[$productsIndexArray[$productOrder->id_product]] .= $this->productSolver->strUnit(
                                    $productOrder->product,
                                    'wording_short',
                                    true
                                );
                            }
                        }
                    }

                    $datas[] = $this->lineOrderReport($orderLine, $cpt);
                }

                // total point de vente
                if ($optionCsvExportByPiece) {
                    $totalsPointSaleArray = $this->totalReportPieces(
                        'Total',
                        $pointSale->orders,
                        $productsArray,
                        $productsIndexArray
                    );
                } else {
                    $totalsPointSaleArray = $this->totalReport(
                        'Total',
                        $pointSale->orders,
                        $productsArray,
                        $productsIndexArray
                    );
                }

                $datas[] = $this->lineOrderReport($totalsPointSaleArray, $cpt);

                $datas[] = [];
            }
        }

        // global
        if ($optionCsvExportByPiece) {
            $totalsGlobalArray = $this->totalReportPieces(
                '> Totaux',
                $ordersArray,
                $productsArray,
                $productsIndexArray
            );
        } else {
            $totalsGlobalArray = $this->totalReport(
                '> Totaux',
                $ordersArray,
                $productsArray,
                $productsIndexArray
            );
        }

        $datas[] = $this->lineOrderReport($totalsGlobalArray, $cpt);

        CSV::send('Commandes_' . $distribution->date . '.csv', $datas);
    }

    private function totalReport($label, $ordersArray, $productsArray, $productsIndexArray)
    {
        $totalsPointSaleArray = [$label];
        foreach ($productsArray as $product) {
            foreach (Product::$unitsArray as $unit => $dataUnit) {
                $quantity = $this->orderSolver->getProductQuantity($product, $ordersArray, false, $unit);
                if ($quantity) {
                    $index = $productsIndexArray[$product->id];
                    if (!isset($totalsPointSaleArray[$index])) {
                        $totalsPointSaleArray[$index] = '';
                    }

                    if (strlen($totalsPointSaleArray[$index])) {
                        $totalsPointSaleArray[$index] .= ' + ';
                    }

                    $totalsPointSaleArray[$index] .= $quantity;

                    if ($product->unit != $unit) {
                        $totalsPointSaleArray[$index] .= '' . $this->productSolver->strUnit($unit, 'wording_short', true);
                    }
                }
            }
        }
        return $totalsPointSaleArray;
    }

    private function totalReportPieces($label, $ordersArray, $productsArray, $productsIndexArray)
    {
        $totalsPointSaleArray = [$label];
        foreach ($productsArray as $product) {
            $quantity = 0;
            foreach (Product::$unitsArray as $unit => $dataUnit) {
                $quantityProduct = $this->orderSolver->getProductQuantity($product, $ordersArray, false, $unit);

                if ($unit == 'piece') {
                    $quantity += $quantityProduct;
                } else {
                    if ($product->weight > 0) {
                        $quantity += ($quantityProduct * $dataUnit['coefficient']) / $product->weight;
                    }
                }
            }

            if ($quantity) {
                $index = $productsIndexArray[$product->id];
                $totalsPointSaleArray[$index] = $quantity;
            }
        }

        return $totalsPointSaleArray;
    }

    private function lineOrderReport($orderLine, $cptMax, $showTotal = false)
    {
        $line = [];
        $cptTotal = 0;

        for ($i = 0; $i <= $cptMax; $i++) {
            if (isset($orderLine[$i]) && $orderLine[$i]) {
                $line[] = $orderLine[$i];
                if (is_numeric($orderLine[$i])) {
                    $cptTotal += $orderLine[$i];
                }
            } else {
                $line[] = '';
            }
        }

        if ($cptTotal > 0 && $showTotal) {
            $line[] = $cptTotal;
        }

        return $line;
    }
}