<?php

namespace Lc\CaracoleBundle\Solver\Product;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Lc\CaracoleBundle\Doctrine\Extension\ProductPropertyInterface;
use Lc\CaracoleBundle\Model\Product\ProductFamilyInterface;
use Lc\CaracoleBundle\Model\Product\ProductFamilyModel;
use Lc\CaracoleBundle\Model\Product\ProductFamilySectionPropertyInterface;
use Lc\CaracoleBundle\Model\Product\ProductInterface;
use Lc\CaracoleBundle\Model\Reduction\ReductionCatalogInterface;
use Lc\CaracoleBundle\Model\Section\SectionInterface;

class ProductFamilySolver
{
    protected ProductSolver $productSolver;
    protected ProductFamilySectionPropertySolver $productFamilySectionPropertySolver;
    protected ProductCategorySolver $productCategorySolver;

    public function __construct(ProductSolver $productSolver, ProductFamilySectionPropertySolver $productFamilySectionPropertySolver, ProductCategorySolver $productCategorySolver)
    {
        $this->productSolver = $productSolver;
        $this->productFamilySectionPropertySolver = $productFamilySectionPropertySolver;
        $this->productCategorySolver = $productCategorySolver;
    }

    public static function getBehaviorCountStockChoices(): array
    {
        return [
                ProductFamilyModel::BEHAVIOR_COUNT_STOCK_BY_MEASURE,
                ProductFamilyModel::BEHAVIOR_COUNT_STOCK_BY_PRODUCT,
                ProductFamilyModel::BEHAVIOR_COUNT_STOCK_BY_PRODUCT_FAMILY,
        ];
    }

    public static function getWaringMessageTypeChoices(): array
    {
        return [
                ProductFamilyModel::WARNING_MESSAGE_TYPE_ERROR,
                ProductFamilyModel::WARNING_MESSAGE_TYPE_INFO,
                ProductFamilyModel::WARNING_MESSAGE_TYPE_SUCCESS,
                ProductFamilyModel::WARNING_MESSAGE_TYPE_WARNING,
        ];
    }

    public static function getBehaviorAddToCartChoices(): array
    {
        return [
                ProductFamilyModel::BEHAVIOR_ADD_TO_CART_MULTIPLE,
                ProductFamilyModel::BEHAVIOR_ADD_TO_CART_SIMPLE,
        ];
    }

    public static function getBehaviorPriceChoices(): array
    {
        return [
                ProductFamilyModel::BEHAVIOR_PRICE_BY_PIECE,
                ProductFamilyModel::BEHAVIOR_PRICE_BY_REFERENCE_UNIT,
        ];
    }

    public static function getTypeExpirationDateChoices(): array
    {
        return [
                ProductFamilyModel::TYPE_EXPIRATION_DATE_DLC,
                ProductFamilyModel::TYPE_EXPIRATION_DATE_DDM,
                ProductFamilyModel::TYPE_EXPIRATION_DATE_DLUO,
        ];
    }


    public static function getBehaviorExpirationDateChoices(): array
    {
        return [
                ProductFamilyModel::BEHAVIOR_EXPIRATION_DATE_BY_PRODUCT_FAMILY,
                ProductFamilyModel::BEHAVIOR_EXPIRATION_DATE_BY_PRODUCT,
        ];
    }


    public static function getBehaviorDisplaySaleChoices(): array
    {
        return [
                ProductFamilyModel::BEHAVIOR_DISPLAY_SALE_BY_MEASURE,
                ProductFamilyModel::BEHAVIOR_DISPLAY_SALE_BY_QUANTITY,
        ];
    }


    public function countProductFamiliesOrganizedByParentCategory(array $categories): int
    {
        $count = 0;
        foreach ($categories as $category) {
            $count += count($category['products']);
        }
        return $count;
    }

    public function getAvailableQuantityInherited(ProductFamilyInterface $productFamily)
    {
        $availableQuantity = 0;

        switch ($productFamily->getBehaviorCountStock()) {
            case ProductFamilyModel::BEHAVIOR_COUNT_STOCK_BY_MEASURE :
            case ProductFamilyModel::BEHAVIOR_COUNT_STOCK_BY_PRODUCT_FAMILY :

                $availableQuantity = $productFamily->getAvailableQuantity();
                break;

            case ProductFamilyModel::BEHAVIOR_COUNT_STOCK_BY_PRODUCT :

                foreach ($this->getProductsOnline($productFamily) as $product) {
                    $availableQuantity += $this->productSolver->getAvailableQuantityInherited($product);
                }
                break;
        }

        return $availableQuantity;
    }

    public function getAvailableQuantitySupplierInherited(ProductFamilyInterface $productFamily)
    {
        $availableQuantity = 0;

        switch ($productFamily->getBehaviorCountStock()) {
            case ProductFamilyModel::BEHAVIOR_COUNT_STOCK_BY_MEASURE :
            case ProductFamilyModel::BEHAVIOR_COUNT_STOCK_BY_PRODUCT_FAMILY :

                $availableQuantity = $productFamily->getAvailableQuantitySupplier();
                break;

            case ProductFamilyModel::BEHAVIOR_COUNT_STOCK_BY_PRODUCT :

                foreach ($this->getProductsOnline($productFamily) as $product) {
                    $availableQuantity += $this->productSolver->getAvailableQuantitySupplierInherited($product);
                }
                break;
        }

        return $availableQuantity;
    }

    public function getTaxRateInherited(ProductPropertyInterface $productFamily)
    {
        if ($productFamily instanceof ProductInterface) {
            $productFamily = $productFamily->getProductFamily();
        }

        if ($productFamily->getTaxRate()) {
            return $productFamily->getTaxRate();
        } else {
            return $productFamily->getProductFamilySectionProperties()[0]->getSection()->getMerchant()->getTaxRate();
        }
    }

    public function getProductsOnline(ProductFamilyInterface $productFamily): Collection
    {
        $products = $productFamily->getProducts();
        $productsOnlineArray = new ArrayCollection();

        foreach ($products as $product) {
            if ($product->getStatus() == 1 && $product->getOriginProduct() != true) {
                $productsOnlineArray[] = $product;
            }
        }

        return $productsOnlineArray;
    }

    public function getReductionCatalogInherited(ProductFamilyInterface $productFamily): ?ReductionCatalogInterface
    {
        return $productFamily->getReductionCatalog();
    }

    public function getProductCategoryParent(ProductFamilyInterface $productFamily, SectionInterface $section)
    {
        $productCategories = $productFamily->getProductCategories();

        if (count($productCategories) > 0) {

            foreach ($productCategories as $productCategory) {
                if($productCategory->getSection()->getId() == $section->getId()
                        && $productCategory->getParent() !== null) {

                    return $productCategory->getParent();
                }
            }
        }

        return false;
    }


    public function getProductCategoryChild(ProductFamilyInterface $productFamily)
    {
        $productCategories = $productFamily->getProductCategories();

        foreach ($productCategories as $productCategory) {
            if ($productCategory->getParent()) {
                return $productCategory;
            }
        }

        return false;
    }


    public function isPropertyNoveltyOnline(ProductFamilyInterface $productFamily): ?bool
    {
        if ($productFamily->getPropertyNoveltyExpirationDate()) {
            $now = new \DateTime();
            if ($now <= $productFamily->getPropertyNoveltyExpirationDate()) {
                return true;
            }
        }

        return false;
    }


    public function countProperties(ProductFamilyInterface $productFamily): bool
    {
        $count = 0;

        $count += (int)strlen($productFamily->getPropertyOrganicLabel()) > 0;
        $count += (int)strlen($productFamily->getPropertyWeight()) > 0;
        $count += (int)strlen($productFamily->getPropertyFragrances()) > 0;
        $count += (int)strlen($productFamily->getPropertyComposition()) > 0;
        $count += (int)strlen($productFamily->getPropertyAllergens()) > 0;
        $count += (int)strlen($productFamily->getPropertyAlcoholLevel()) > 0;
        $count += (int)strlen($productFamily->getPropertyCharacteristics()) > 0;
        $count += (int)strlen($productFamily->getPropertyFeature()) > 0;
        $count += (int)strlen($productFamily->getPropertyPackaging()) > 0;
        $count += (int)strlen($productFamily->getPropertyQuantity()) > 0;
        $count += (int)strlen($productFamily->getPropertyVariety()) > 0;
        $count += (int)($productFamily->getPropertyExpirationDate() != null);

        return $count;
    }


    public function hasProductsWithVariousWeight(ProductFamilyInterface $productFamily)
    {
        if ($productFamily->getActiveProducts()) {
            $arrayCountProducts = [];
            $products = $this->getProductsOnline($productFamily);

            foreach ($products as $product) {
                $titleProduct = $this->productSolver->getTitleInherited($product);
                if (!isset($arrayCountProducts[$titleProduct])) {
                    $arrayCountProducts[$titleProduct] = [];
                }

                if (!in_array(
                        $this->productSolver->getQuantityLabelInherited($product),
                        $arrayCountProducts[$titleProduct]
                )) {
                    $arrayCountProducts[$titleProduct][] = $this->productSolver->getQuantityLabelInherited($product);
                }

                if (count($arrayCountProducts[$titleProduct]) > 1) {
                    return true;
                }
            }
        }

        return false;
    }


    public function getProductsGroupByTitle(ProductFamilyInterface $productFamily): array
    {
        $arrayProductsGroupByTitle = [];
        $products = $this->getProductsOnline($productFamily);

        foreach ($products as $product) {
            if ($product->getStatus() == 1) {
                $titleProduct = $this->productSolver->getTitleInherited($product);
                if (!isset($arrayProductsGroupByTitle[$titleProduct])) {
                    $arrayProductsGroupByTitle[$titleProduct] = [];
                }
                $arrayProductsGroupByTitle[$titleProduct][] = $product;
            }
        }

        return $arrayProductsGroupByTitle;
    }

    public function getQuantityTitle(ProductFamilyInterface $productFamily, ProductInterface $product)
    {
        $title = $this->productSolver->getQuantityLabelInherited($product);

        if ($this->hasProductsWithVariousWeight($productFamily)) {
            $title .= ', ' . $this->productSolver->getTitleInherited($product);
        }

        return $title;
    }

    public function getOriginProduct(ProductFamilyInterface $productFamily): ?ProductInterface
    {
        $products = $productFamily->getProducts();

        foreach ($products as $product) {
            if ($product->getOriginProduct()) {
                return $product;
            }
        }

        return null;
    }

    public function getOriginProductOnline(ProductFamilyInterface $productFamily): ?ProductInterface
    {
        $originProduct = $this->getOriginProduct($productFamily);

        if ($originProduct->getStatus() == 1) {
            return $originProduct;
        } else {
            return null;
        }
    }

    public function hasOneProductOnline(ProductFamilyInterface $productFamily)
    {
        if (($productFamily->getActiveProducts() && count($this->getProductsOnline($productFamily)) > 0)
                || (!$productFamily->getActiveProducts() && $this->getOriginProduct($productFamily))) {
            return true;
        }

        return false;
    }

    public function getFieldBuyingPrice(ProductFamilyInterface $productFamily): string
    {
        if ($productFamily->getBehaviorPrice() === ProductFamilyModel::BEHAVIOR_PRICE_BY_PIECE) {
            return 'buyingPrice';
        } elseif ($productFamily->getBehaviorPrice() === ProductFamilyModel::BEHAVIOR_PRICE_BY_REFERENCE_UNIT) {
            return 'buyingPriceByRefUnit';
        }
    }

    public function getFieldPrice(ProductFamilyInterface $productFamily): string
    {
        if ($productFamily->getBehaviorPrice() === ProductFamilyModel::BEHAVIOR_PRICE_BY_PIECE) {
            return 'price';
        } elseif ($productFamily->getBehaviorPrice() === ProductFamilyModel::BEHAVIOR_PRICE_BY_REFERENCE_UNIT) {
            return 'priceByRefUnit';
        }
    }

    public function getBehaviorPriceInherited(ProductFamilyInterface $productFamily): string
    {
        return $productFamily->getBehaviorPrice();
    }

    public function getBuyingPriceByRefUnitInherited(ProductFamilyInterface $productFamily): ?float
    {
        return $productFamily->getBuyingPriceByRefUnit();
    }

    public function getBuyingPriceInherited(ProductFamilyInterface $productFamily): ?float
    {
        return $productFamily->getBuyingPrice();
    }

    public function getPriceByRefUnitInherited(ProductFamilyInterface $productFamily): ?float
    {
        return $productFamily->getPriceByRefUnit();
    }

    public function getQuantityInherited(ProductFamilyInterface $productFamily): ?float
    {
        return $productFamily->getQuantity();
    }

    public function getUnitInherited(ProductFamilyInterface $productFamily)
    {
        return $productFamily->getUnit();
    }

    public function getPriceInherited(ProductFamilyInterface $productFamily)
    {
        return $productFamily->getPrice();
    }

    public function getSummaryQualityLabels(ProductFamilyInterface $productFamily): string
    {
        $strLabels = '';
        $qualityLabelArray = $productFamily->getQualityLabels()->toArray();

        foreach($qualityLabelArray as $index => $qualityLabel) {
            $strLabels .= $qualityLabel->getTitle();

            if ($index !== array_key_last($qualityLabelArray)) {
                $strLabels .= ', ';
            }
        }

        return $strLabels;
    }

    public function hasQualityLabel(ProductFamilyInterface $productFamily): bool
    {
        $qualityLabels = $productFamily->getQualityLabels();
        return $qualityLabels && count($qualityLabels) > 0;
    }

    public function getOrganicLabel(ProductFamilyInterface $productFamily)
    {
        return $this->getQualityLabelByArray($productFamily, ProductFamilyModel::$organicLabels);
    }

    public function getGeographicLabel(ProductFamilyInterface $productFamily)
    {
        return $this->getQualityLabelByArray($productFamily, ProductFamilyModel::$geographicLabels);
    }

    public function getQualityLabelByArray(ProductFamilyInterface $productFamily, array $labelArray)
    {
        $qualityLabelArray = $productFamily->getQualityLabels();

        foreach ($qualityLabelArray as $qualityLabel) {
            if (in_array($qualityLabel->getDevAlias(), $labelArray)) {
                return $qualityLabel;
            }
        }

        return false;
    }

    public function hasOrganicLabel(ProductFamilyInterface $productFamily)
    {
        return $this->hasQualityLabelByArray($productFamily, ProductFamilyModel::$organicLabels);
    }

    public function hasGeographicLabel(ProductFamilyInterface $productFamily)
    {
        return $this->hasQualityLabelByArray($productFamily, ProductFamilyModel::$geographicLabels);
    }

    public function hasQualityLabelByArray(ProductFamilyInterface $productFamily, array $labelArray)
    {
        $qualityLabelArray = $productFamily->getQualityLabels();

        foreach ($qualityLabelArray as $qualityLabel) {
            if (in_array($qualityLabel->getDevAlias(), $labelArray)) {
                return true;
            }
        }

        return false;
    }

    public function getSection(ProductFamilyInterface $productFamily): ?SectionInterface
    {
        foreach ($productFamily->getProductFamilySectionProperties() as $productFamilySectionProperty) {
            if ($productFamilySectionProperty->getStatus()) {
                return $productFamilySectionProperty->getSection();
            }
        }

        return null;
    }

    public function isInSection(ProductFamilyInterface $productFamily, SectionInterface $section): bool
    {
        foreach ($productFamily->getProductFamilySectionProperties() as $productFamilySectionProperty) {
            if ($productFamilySectionProperty->getSection()->getId() == $section->getId(
                    ) && $productFamilySectionProperty->getStatus()) {
                return true;
            }
        }

        return false;
    }


    public function isCategoriesOnlineInSection(ProductFamilyInterface $productFamily, SectionInterface $section):bool
    {
        $isCategoriesOnlineInSection =false;
        foreach ($productFamily->getProductCategories() as $productCatgory){
            if($productCatgory->getSection() === $section && $this->productCategorySolver->isOnline($productCatgory)){
                $isCategoriesOnlineInSection = true;
            }
        }
        return $isCategoriesOnlineInSection;
    }
}