<?php

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Twig\Components\Collection;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Context\GroupContext;
use CpCreation\VitiCore\Product\Model\Product;
use CpCreation\VitiCore\Product\Model\ProductCollection as ProductCollectionModel;
use CpCreation\VitiCore\Product\Model\ProductCollectionItem;
use CpCreation\VitiCore\Product\Model\ProductPriceGroup;
use CpCreation\VitiCore\Product\Model\ProductVariant;
use CpCreation\VitiCore\Product\Repository\ProductCollectionRepository;
use CpCreation\VitiCore\Product\Repository\ProductRepository;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;

#[AsTwigComponent('collection:ProductCollection', template: '@components/collection/ProductCollection.html.twig')]
final class ProductCollection
{
    public string $collection = '';

    public ?string $title = null;

    public ?int $limit = null;

    private ?ProductCollectionModel $productCollection = null;

    private bool $productCollectionLoaded = false;

    public function __construct(
        private readonly ProductCollectionRepository $collectionRepository,
        private readonly ProductRepository $productRepository,
    ) {}

    public function getProductCollection(): ?ProductCollectionModel
    {
        if ($this->productCollectionLoaded) {
            return $this->productCollection;
        }

        $this->productCollectionLoaded = true;

        if ('' === mb_trim($this->collection)) {
            return null;
        }

        $collection = $this->collectionRepository->findOneByCode($this->collection);
        if (!$collection instanceof ProductCollectionModel || !$collection->isCurrentlyPublished()) {
            return null;
        }

        return $this->productCollection = $collection;
    }

    /**
     * @return list<ProductCollectionItem>
     */
    public function getItems(): array
    {
        $collection = $this->getProductCollection();

        if (!$collection instanceof ProductCollectionModel || !$collection->isManual()) {
            return [];
        }

        $items = array_values(array_filter(
            $collection->getItems()->toArray(),
            fn(ProductCollectionItem $item): bool => $this->isPublishedProduct($item->getProduct())
        ));

        if (null !== $this->limit && $this->limit > 0) {
            return \array_slice($items, 0, $this->limit);
        }

        return $items;
    }

    /**
     * @return list<Product>
     */
    public function getProducts(): array
    {
        $collection = $this->getProductCollection();

        if (!$collection instanceof ProductCollectionModel) {
            return [];
        }

        if ($collection->isAutomatic()) {
            return $this->productRepository->findPublishedForProductCollection(
                $collection,
                group: GroupContext::$groupId,
                limit: $this->resolveLimit($collection)
            );
        }

        return array_values(array_filter(array_map(
            static fn(ProductCollectionItem $item): ?Product => $item->getProduct(),
            $this->getItems(),
        )));
    }

    private function resolveLimit(ProductCollectionModel $collection): ?int
    {
        if (null !== $this->limit && $this->limit > 0) {
            return $this->limit;
        }

        return $collection->getAutomaticLimit();
    }

    private function isPublishedProduct(?Product $product): bool
    {
        if (!$product instanceof Product || $product->isDisabled() || $product->isDeleted()) {
            return false;
        }

        $activeGroupId = GroupContext::$groupId;
        if (\is_string($activeGroupId) && '' !== $activeGroupId) {
            foreach ($product->getGroups() as $group) {
                if ($group instanceof ProductPriceGroup && $group->getId()->toString() === $activeGroupId) {
                    return $this->hasPublishedVariant($product);
                }
            }

            return false;
        }

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

    private function hasPublishedVariant(Product $product): bool
    {
        foreach ($product->getVariants() as $variant) {
            if ($variant instanceof ProductVariant && !$variant->isUnPublished() && null === $variant->getDeletedAt()) {
                return true;
            }
        }

        return false;
    }
}
