<?php

namespace KupShop\ContentBundle\Entity;

use KupShop\CatalogBundle\Entity\Variation;
use KupShop\CatalogBundle\Util\ProductAvailabilityUtil;
use KupShop\KupShopBundle\Util\Price\ProductPrice;
use KupShop\KupShopBundle\Wrapper\PriceWrapper;

class ProductUnified
{
    protected \Product $product;
    protected ?array $variations = null;

    protected array|Variation|null $variation = null;

    public function __construct(\Product $product, ?array $variations = null)
    {
        $this->product = $product;
        $this->variations = $variations['variations'] ?? $variations ?? null;
    }

    public function selectVariation(int $id): bool
    {
        if ($this->variations[$id] ?? false) {
            $this->variation = $this->variations[$id];

            return true;
        }

        return false;
    }

    public function getProduct(): \Product
    {
        return $this->product;
    }

    public function getId(): int
    {
        return (int) $this->product->id;
    }

    public function getVariationId(): ?int
    {
        if ($this->variation) {
            return (int) $this->variation['id'];
        }

        return null;
    }

    public function getTitle(): string
    {
        if ($this->variation) {
            return $this->variation['title'] ?? '';
        }

        return $this->product->title;
    }

    public function getCode(): ?string
    {
        if ($this->variation) {
            return $this->variation['code'] ?? null;
        }

        return $this->product->code;
    }

    public function getEan(): ?string
    {
        if ($this->variation) {
            return $this->variation['ean'];
        }

        return $this->product->ean;
    }

    public function getUnit(): ?array
    {
        if (!findModule(\Modules::PRODUCTS, \Modules::SUB_UNITS)) {
            return null;
        }

        return $this->product->unit;
    }

    public function getCharges(): array
    {
        return $this->product->fetchCharges();
    }

    public function getLabels(): array
    {
        return $this->product->fetchLabels();
    }

    public function getDeliveryTimeText(): string
    {
        if ($this->variation) {
            // in cart delivery_time contains numeric delivery time, but on product detail it contains string... :/
            // so try to use `delivery_time_text` and then fallback to `delivery_time` if `delivery_time_text` does not exists
            return $this->variation['delivery_time_text'] ?? $this->variation['delivery_time'];
        }

        $this->product->prepareDeliveryText();

        return $this->product->deliveryTimeText;
    }

    public function getSupplierForecastedDeliveryDate(): ?\DateTimeImmutable
    {
        $this->product->getInStoreSuppliers();

        if ($this->variation) {
            return $this->variation->getForecastedDeliveryDate();
        }

        return $this->product->forecasted_delivery_date;
    }

    public function getSupplierForecastedPieces(): int
    {
        $this->product->getInStoreSuppliers();

        if ($this->variation) {
            return $this->variation->getForecastedPieces() ?? 0;
        }

        return $this->product->forecasted_pieces ?? 0;
    }

    public function getDeliveryTime(): int
    {
        if ($this->variation) {
            return (int) $this->variation['delivery_time_index'];
        }

        $this->product->prepareDeliveryText();

        return (int) $this->product->deliveryTime;
    }

    public function getDeliveryTimeRaw(): int
    {
        if ($this->variation) {
            return (int) $this->variation['delivery_time_raw'];
        }

        return (int) $this->product->deliveryTimeRaw;
    }

    public function getInStore(): float
    {
        if ($this->variation) {
            return (float) $this->variation['in_store'];
        }

        return (float) $this->product->inStore;
    }

    public function getInStoreSuppliers(): float
    {
        if ($this->variation && isset($this->variation['in_store_suppliers'])) {
            return (float) $this->variation['in_store_suppliers'];
        }

        return (float) $this->product->getInStoreSuppliers($this->variation['id'] ?? null);
    }

    public function getCampaignCodes(): array
    {
        return $this->product->campaign_codes;
    }

    public function getBonusPoints(): ?\Decimal
    {
        if (!findModule(\Modules::BONUS_PROGRAM)) {
            return null;
        }

        if ($this->variation && isset($this->variation['bonus_points'])) {
            return toDecimal($this->variation['bonus_points']);
        }

        return $this->product->getBonusPoints($this->getVariationId());
    }

    public function getDiscount(): \Decimal
    {
        if ($this->variation) {
            $variationDiscount = toDecimal($this->variation['discount'] ?? 0);
            if ($variationDiscount->isPositive()) {
                return $variationDiscount;
            }
        }

        return $this->product->discount;
    }

    public function getProductPrice(): ProductPrice
    {
        if ($this->variation) {
            return PriceWrapper::unwrap($this->variation['productPrice']);
        }

        return $this->product->getProductPrice();
    }

    public function getPriceCommon(): ?ProductPrice
    {
        if ($this->variation) {
            $priceCommon = !empty($this->variation['priceCommon']) ? $this->variation['priceCommon'] : null;

            return PriceWrapper::unwrap($priceCommon);
        }

        return $this->product->priceCommon ? $this->product->priceCommon : null;
    }

    public function getPriceForDiscount(): ?ProductPrice
    {
        if ($this->variation) {
            $priceForDiscount = !empty($this->variation['price_for_discount']) ? $this->variation['price_for_discount'] : null;

            return PriceWrapper::unwrap($priceForDiscount);
        }

        return $this->product->priceForDiscount ? $this->product->priceForDiscount : null;
    }

    public function getData(): array
    {
        if ($this->variation) {
            return $this->variation['data'] ?? [];
        }

        return $this->product->getData();
    }

    public function getSuppliers()
    {
        return $this->product->getSuppliers($this->variation['id'] ?? null);
    }

    public function getConfigurations(): array
    {
        $this->product->fetchParameters();

        return $this->product->configurations ?? [];
    }

    public function getPhotos()
    {
        if ($this->variation && !empty($this->variation['photos'])) {
            return $this->variation['photos'];
        }

        if (!$this->product->photos) {
            $this->product->fetchImages(3, 4);
        }

        return $this->product->photos;
    }

    public function getMainPhoto(): ?array
    {
        if (empty($this->product->image)) {
            $this->product->fetchImages(3);
        }

        return $this->product->image ?: null;
    }

    public function isVirtual(): bool
    {
        return (bool) $this->product->isVirtual();
    }

    public function canBuy(): bool
    {
        if ($this->variation) {
            if (isset($this->variation['canBuy'])) {
                return $this->variation['canBuy'];
            } else {
                ProductAvailabilityUtil::canBuy($this->variation['delivery_time_index'], false, $canWatch, $canBuy);
                $this->variation['canWatch'] = $canWatch;

                return $this->variation['canBuy'] = $canBuy;
            }
        }

        return $this->product->canBuy();
    }

    public function canWatch(): ?bool
    {
        if ($this->variation) {
            if (!isset($this->variation['canWatch'])) {
                $this->canBuy();
            }

            return $this->variation['canWatch'];
        }

        return $this->product->canWatch();
    }

    public function getPricelists(): array
    {
        return $this->product->pricelists ?? [];
    }

    /**
     * @return array<Variation|array>
     */
    public function getVariations(): array
    {
        return $this->variations ?? [];
    }

    public function getTotalAvailablePieces(): float
    {
        if ($this->variation) {
            if (isset($this->variation[ProductAvailabilityUtil::IN_STORE_MAX_FIELD])) {
                return min(
                    $this->variation[ProductAvailabilityUtil::IN_STORE_MAX_FIELD],
                    $this->variation->getTotalAvailablePieces(),
                );
            }

            return $this->variation->getTotalAvailablePieces();
        }

        if (isset($this->product[ProductAvailabilityUtil::IN_STORE_MAX_FIELD])) {
            return min(
                $this->product[ProductAvailabilityUtil::IN_STORE_MAX_FIELD],
                $this->product->inStore + $this->product->getInStoreSuppliers(),
            );
        }

        return $this->product->inStore + $this->product->getInStoreSuppliers();
    }

    public function getSections(): array
    {
        return $this->product->sections ?? [];
    }

    public function getProducer(): ?array
    {
        if (!findModule(\Modules::PRODUCERS)) {
            return null;
        }

        return $this->product->producer;
    }

    public function getOnTheWay(): ?array
    {
        if (!findModule(\Modules::STOCK_IN)) {
            return null;
        }

        if ($this->variation && isset($this->variation['onTheWay'])) {
            return $this->variation['onTheWay'] ?? null;
        }

        return $this->product['onTheWay'] ?? null;
    }
}
