<?php

namespace domain\Product\Product;

use common\helpers\GlobalParam;
use common\helpers\Price;
use domain\Config\Unit\UnitDefinition;
use domain\Config\Unit\UnitSolver;
use domain\PointSale\PointSale\PointSale;
use domain\PointSale\PointSale\PointSaleSolver;
use domain\Product\ProductPrice\Service\ProductPriceSolver;
use domain\_\AbstractService;
use domain\_\SolverInterface;

class ProductSolver extends AbstractService implements SolverInterface
{
    protected UnitSolver $unitSolver;
    protected ProductPriceSolver $productPriceSolver;
    protected PointSaleSolver $pointSaleSolver;

    public function loadDependencies(): void
    {
        $this->unitSolver = $this->loadService(UnitSolver::class);
        $this->productPriceSolver = $this->loadService(ProductPriceSolver::class);
        $this->pointSaleSolver = $this->loadService(PointSaleSolver::class);
    }

    public function getWeightOrNumberOfPieces(float $quantity, Product $product): float
    {
        // Poids total
        if($product->unit != 'piece') {
            $quantity = ($quantity * $product->weight) / $this->getUnitCoefficient($product);
        }

        return $quantity;
    }

    public function getNumberOfPieces(float $quantity, Product $product): int
    {
        if($product->unit != 'piece') {
            $quantity = ($quantity * $this->getUnitCoefficient($product)) / $product->weight;
        }

        return $quantity;
    }

    public function getUnitCoefficient(Product $product): int
    {
        return $this->unitSolver->getUnitCoefficient($product->unit);
    }

    public function getUnitReference(Product $product): string
    {
        return $this->unitSolver->getUnitReference($product->unit);
    }

    public function strUnit(Product $product, string $format = UnitDefinition::WORDING_SHORT, bool $unitReference = false): string
    {
        return $this->unitSolver->strUnit($product->unit, $format, $unitReference);
    }

    public function getWeightUnitReference(Product $product): float
    {
        if($product->weight) {
            return $product->weight / 1000;
        }

        return 0;
    }

    public function getWeightAsString(Product $product): string
    {
        if ($product->weight) {
            return $product->weight . ' g';
        }
        return '';
    }

    public function getPrice(Product $product, array $params = []): ?float
    {
        $specificPriceArray = $product->productPrice;

        $user = (isset($params['user']) && $params['user']) ? $params['user'] : null;
        $pointSale = (isset($params['point_sale']) && $params['point_sale']) ? $params['point_sale'] : null;
        $quantity = (isset($params['quantity']) && $params['quantity']) ? $params['quantity'] : 1;
        //$userProducer = (isset($params['user_producer']) && $params['user_producer']) ? $params['user_producer'] : null;

        if ($specificPriceArray && ($user || $pointSale)) {
            $specificPriceArray = $this->productPriceSolver->filterByPriorityMatch(
                $specificPriceArray,
                $user,
                $pointSale
            );
            $bestPrice = 9999;
            foreach ($specificPriceArray as $specificPrice) {
                $fromQuantity = $specificPrice->from_quantity;
                if ((($fromQuantity && $fromQuantity <= $quantity) || !$fromQuantity)
                    && $specificPrice->price < $bestPrice) {
                    $bestPrice = $specificPrice->price;
                }
            }

            if ($bestPrice != 9999) {
                return $bestPrice;
            }
        }

        /*if ($userProducer && $userProducer->product_price_percent) {
            return $product->price * (1 + $userProducer->product_price_percent / 100);
        }*/

        /*if ($pointSale && $pointSale->product_price_percent) {
            return $product->price * (1 + $pointSale->product_price_percent / 100);
        }*/

        return $product->price;
    }

    public function getPriceUnitReference(Product $product): ?float
    {
        $productPrice = $product->price;
        if($product->unit == 'piece') {
            if($product->weight) {
                $price = (1000 * $productPrice) / $product->weight;
            }
            else {
                return null;
            }
        }
        else {
            $price = $product->price;
        }

        return $price;
    }

    public function getPriceUnitReferenceWithTax(Product $product)
    {
        $taxRateValue = $product->taxRate ? $product->taxRate->value : 0;
        return Price::getPriceWithTax($this->getPriceUnitReference($product), $taxRateValue);
    }

    /**
     * Retourne le prix du produit avec taxe
     */
    public function getPriceWithTax(Product $product, $params = [])
    {
        $taxRateValue = $product->taxRate ? $product->taxRate->value : 0;
        return Price::getPriceWithTax($this->getPrice($product, $params), $taxRateValue);
    }

    public function isAvailableOnPointSale(Product $product, PointSale $pointSale): bool
    {
        // disponible par défaut
        if ($product->available_on_points_sale) {
            foreach ($product->productPointSale as $productPointSale) {
                if (isset($pointSale->id)
                    && $productPointSale->id_point_sale
                    && $pointSale->id == $productPointSale->id_point_sale
                    && !$productPointSale->available) {
                    return false;
                }
            }

            return true;
        } // indisponible par défaut
        else {
            foreach ($product->productPointSale as $productPointSale) {
                if (isset($pointSale->id) && $pointSale->id == $productPointSale->id_point_sale
                    && $productPointSale->available) {
                    return true;
                }
            }

            return false;
        }
    }

    /**
     * Retourne la description du produit.
     */
    public function getDescription(Product $product): string
    {
        $description = $product->description;
        if (isset($product->weight) && is_numeric($product->weight) && $product->weight > 0) {
            if ($product->weight >= 1000) {
                $description .= ' (' . ($product->weight / 1000) . 'kg)';
            } else {
                $description .= ' (' . $product->weight . 'g)';
            }
        }

        return $description;
    }

    /**
     * Retourne le libellé (admin) du produit.
     */
    public function getStrWordingAdmin(Product $product): string
    {
        return $product->name;
    }

    public function hasPriceWithQuantityZero(array $priceArray): bool
    {
        foreach ($priceArray as $price) {
            if ($price['from_quantity'] == 0) {
                return true;
            }
        }

        return false;
    }

    public function getTheTaxRate(Product $product)
    {
        if ($product->id_tax_rate) {
            return $product->id_tax_rate;
        } else {
            return GlobalParam::getCurrentProducer()->taxRate->id;
        }
    }

    public function getNameExport(Product $product)
    {
        $producer = $this->getProducerContext();
        if ($producer->option_export_display_product_reference
            && $product->reference
            && strlen($product->reference) > 0) {
            return $product->reference;
        }

        $productName = $product->name;
        // le caractère "<" crée un problème dans le récapitulatif PDF des commandes
        $productName = str_replace('<', '', $productName);

        return $productName;
    }

    public function isProductActiveByDay(Product $product, string $day): bool
    {
        return $product->status && $product->$day;
    }

    public function getProductFieldNameQuantityMax(string $day): string
    {
        return 'quantity_max_' . $day;
    }

    public function isProductQuantityMaxDefinedByDay(Product $product, string $day): bool
    {
        $fieldQuantityMax = $this->getProductFieldNameQuantityMax($day);
        return isset($product->$fieldQuantityMax) && $product->$fieldQuantityMax > 0;
    }

    public function getProductQuantityMaxByDay(Product $product, string $day): ?float
    {
        $quantityMax = ($product->quantity_max && $product->quantity_max > 0) ? $product->quantity_max : null;
        if ($this->isProductQuantityMaxDefinedByDay($product, $day)) {
            $fieldQuantityMax = $this->getProductFieldNameQuantityMax($day);
            $quantityMax = $product->$fieldQuantityMax;
        }

        return $quantityMax;
    }

    public function isPublic(Product $product): bool
    {
        if ($product->available_on_points_sale) {
            return true;
        } else {
            foreach ($product->productPointSale as $productPointSale) {
                if ($productPointSale->pointSale && $this->pointSaleSolver->isPublic($productPointSale->pointSale)) {
                    return true;
                }
            }
        }

        return false;
    }

    public function filterProductsByPointSale(array $productArray, PointSale $pointSale = null): array
    {
        $productArrayFilter = [];

        if ($pointSale) {
            foreach ($productArray as $product) {
                if ($this->isAvailableOnPointSale($product, $pointSale)) {
                    $productArrayFilter[] = $product;
                }
            }
        }

        return $productArrayFilter;
    }

    public function getProductDistributionPotentialRevenues(array $productsArray, bool $withTax = false)
    {
        $potentialRevenues = 0;

        foreach ($productsArray as $product) {
            if (isset($product['productDistribution'][0]) && $product['productDistribution'][0]['active'] && $product['productDistribution'][0]['quantity_max']) {
                $price = $withTax ? $product['price_with_tax'] : $product['price'];
                $potentialRevenues += $product['productDistribution'][0]['quantity_max'] * $price;
            }
        }

        return $potentialRevenues;
    }

    public function getProductDistributionPotentialWeight(array $productsArray)
    {
        $potentialWeight = 0;

        foreach ($productsArray as $product) {
            if (isset($product['productDistribution'][0]) && $product['productDistribution'][0]['active'] && $product['productDistribution'][0]['quantity_max']) {
                $potentialWeight += $product['productDistribution'][0]['quantity_max'] * $product['weight'] / 1000;
            }
        }

        return $potentialWeight;
    }

    public function isStatusOnlineOrOffline(Product $product): bool
    {
        return $product->status >= Product::STATUS_OFFLINE;
    }
}