<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

namespace Shopware\Plugins\ViisonSetArticles\Components\ConfiguratorGatewayDecorator;

use Doctrine\DBAL\Connection;
use Shopware\Bundle\StoreFrontBundle\Gateway\ConfiguratorGatewayInterface;
use Shopware\Bundle\StoreFrontBundle\Struct;
use Shopware\Components\Model\ModelManager;
use Shopware\CustomModels\ViisonSetArticles\Repository as SetArticleRepository;
use Shopware\Models\Article\Detail;

/**
 * @Shopware\noEncryption
 * Decorator service to modify the selectable options for variant set articles. This service is used by other services
 * of the StoreFrontBundle.
 */
class ConfiguratorGatewayDecoratorPHP5 implements ConfiguratorGatewayInterface
{
    /**
     * @var ConfiguratorGatewayInterface
     */
    private $decoratedInstance;

    /**
     * @var ModelManager
     */
    private $entityManager;

    /**
     * @param ConfiguratorGatewayInterface $decoratedInstance
     * @param ModelManager $entityManager
     */
    public function __construct(
        ConfiguratorGatewayInterface $decoratedInstance,
        ModelManager $entityManager
    ) {
        $this->decoratedInstance = $decoratedInstance;
        $this->entityManager = $entityManager;
    }

    /**
     * Set articles availability needs to be calculated here because by default the product combinations are based on
     * the article details availability.
     *
     * @inheritdoc
     */
    public function getProductCombinations(Struct\BaseProduct $product)
    {
        $article = $this->entityManager->find('Shopware\\Models\\Article\\Article', $product->getId());
        $attributes = $article->getMainDetail()->getAttribute();
        if (!$attributes) {
            return $this->decoratedInstance->getProductCombinations($product);
        }
        $isSetArticle = $attributes->getViisonSetArticleActive();
        if (!$isSetArticle) {
            return $this->decoratedInstance->getProductCombinations($product);
        }
        $articleDetails = $article->getDetails()->toArray();
        /** @var SetArticleRepository $setArticleRepository */
        $setArticleRepository = $this->entityManager->getRepository(
            'Shopware\\CustomModels\\ViisonSetArticles\\SetArticle'
        );
        $articleDetailIds = array_map(function (Detail $articleDetail) {
            return $articleDetail->getId();
        }, $articleDetails);
        $setAvailabilities = $setArticleRepository->getCombinedSetArticleDetailsBatchData($articleDetailIds);
        $availableArticleDetailIds = array_keys(array_filter($setAvailabilities, static function ($setAvailability) {
            return $setAvailability['available'];
        }));

        // Slightly modified version of the query shopware uses, so only options for available set articles are returned:
        // https://github.com/shopware/shopware/blob/45f9f30c385de36e13a71fdb0789a09ef3ccf9f5/engine/Shopware/Bundle/StoreFrontBundle/Gateway/DBAL/ConfiguratorGateway.php#L161-L194
        $dbalConnection = $this->entityManager->getConnection();
        $query = $dbalConnection->createQueryBuilder();

        $query->select([
            'relations.option_id',
            "GROUP_CONCAT(DISTINCT assignedRelations.option_id, '' SEPARATOR '|') as combinations",
        ]);

        $query->from('s_article_configurator_option_relations', 'relations')
            ->innerJoin(
                'relations',
                's_articles_details',
                'variant',
                'variant.id = relations.article_id AND variant.active = 1 AND variant.id IN (:articleDetailIds)'
            )
            ->leftJoin(
                'relations',
                's_article_configurator_option_relations',
                'assignedRelations',
                'assignedRelations.article_id = relations.article_id AND assignedRelations.option_id != relations.option_id'
            )
            ->groupBy('relations.option_id')
            ->setParameter(':articleDetailIds', $availableArticleDetailIds, Connection::PARAM_STR_ARRAY);

        /** @var \Doctrine\DBAL\Driver\ResultStatement $statement */
        $statement = $query->execute();

        $data = $statement->fetchAll(\PDO::FETCH_KEY_PAIR);

        foreach ($data as &$row) {
            $row = explode('|', $row);
        }

        return $data;
    }

    /**
     * @inheritdoc
     */
    public function get(Struct\BaseProduct $product, Struct\ShopContextInterface $context)
    {
        return $this->decoratedInstance->get($product, $context);
    }

    /**
     * @inheritdoc
     */
    public function getConfiguratorMedia(Struct\BaseProduct $product, Struct\ShopContextInterface $context)
    {
        return $this->decoratedInstance->getConfiguratorMedia($product, $context);
    }
}
