<?php

declare(strict_types=1);

namespace KupShop\UnitsBundle\Utils;

use KupShop\CatalogBundle\ProductList\ProductCollection;
use KupShop\ContentBundle\Entity\ProductUnified;
use KupShop\ContentBundle\Entity\Wrapper\ProductUnifiedWrapper;
use KupShop\I18nBundle\Translations\ProductsUnitsTranslation;
use KupShop\I18nBundle\Util\PriceConverter;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Util\Price\Price;
use KupShop\KupShopBundle\Util\Price\ProductPrice;
use KupShop\KupShopBundle\Wrapper\PriceWrapper;
use Query\Operator;
use Query\Translation;
use Symfony\Contracts\Service\Attribute\Required;

class MeasureUnitUtil
{
    protected ?PriceConverter $priceConverter;
    protected CurrencyContext $currencyContext;

    public function getUnitPrice(ProductUnified|ProductUnifiedWrapper $product, bool $round = false): ?array
    {
        $productCollection = new ProductCollection([$product->getId() => $product]);
        $productCollection->fetchMeasureUnits();

        if ($product instanceof ProductUnifiedWrapper) {
            $product = $product->getObject();
        }

        if (empty($product->measureUnits)) {
            return null;
        }

        // Select first variation by default or get data for product
        $data = $product->measureUnits[array_key_first($product->measureUnits)] ?? null;
        if ($product->getVariationId()) {
            $data = $product->measureUnits[$product->getVariationId()] ?? null;
        }

        if (!$data) {
            return null;
        }

        return $this->calculatePriceUnit($product->getProductPrice(), $data);
    }

    public function calculatePriceUnit(ProductPrice $productPrice, array $data): ?array
    {
        $measureQuantity = toDecimal($data['measure_quantity'] ?? null);
        $measureUnit = toDecimal($data['recalculate_to'] ?? null);

        if (!$measureQuantity->isPositive()) {
            return null;
        }

        if (!$measureUnit->isPositive()) {
            return null;
        }

        $wrapped = PriceWrapper::wrap($productPrice);
        $price = $wrapped->field_value_without_vat_no_rounding();
        $priceWithoutDiscount = $wrapped->field_value_without_vat_without_discount();

        $pricePerUnit = $price->div($measureQuantity->div($measureUnit, 4), 4);
        $priceWithoutDiscountPerUnit = $priceWithoutDiscount->div($measureQuantity->div($measureUnit, 4), 4);

        $result = [
            'price' => new Price($pricePerUnit, $this->currencyContext->getActive(), $productPrice->getVat()),
            'recalculate_to' => $data['recalculate_to'],
            'unit_name' => $data['short_name'],
        ];

        if ($priceWithoutDiscountPerUnit->comp($pricePerUnit)) {
            $result['price_without_discount'] = new Price($priceWithoutDiscountPerUnit, $this->currencyContext->getActive(), $productPrice->getVat());
        }

        return $result;
    }

    public function getUnitPriceQueryBuilder(?array $ids = null): \Query\QueryBuilder
    {
        $qb = sqlQueryBuilder()->select('p.id id_product, pv.id id_variation, 
            COALESCE(pv.measure_quantity, p.measure_quantity) measure_quantity, pu.recalculate_to, pu.id as id')
            ->fromProducts()
            ->joinVariationsOnProducts()
            ->innerJoin('p', 'products_units', 'pu', 'pu.id = p.measure_unit')
            ->andWhere(Translation::coalesceTranslatedFields(ProductsUnitsTranslation::class, ['short_name', 'long_name']));

        if (findModule(\Modules::PRODUCTS, \Modules::SUB_UNITS_FLOAT)) {
            $qb->addSelect('pu.pieces_precision as pieces_precision');
        }

        if ($ids) {
            $qb->andWhere(Operator::inIntArray($ids, 'p.id'));
        }

        return $qb;
    }

    #[Required]
    public function setPriceConverter(?PriceConverter $priceConverter): void
    {
        $this->priceConverter = $priceConverter;
    }

    #[Required]
    public function setCurrencyContext(CurrencyContext $currencyContext): void
    {
        $this->currencyContext = $currencyContext;
    }
}
