<?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\Subscribers\Backend;

use Enlight_Hook_HookArgs;
use Shopware\Models\Article\Article;
use Shopware\Models\Article\Detail as ArticleDetail;
use Shopware\Models\Attribute\Article as ArticleAttribute;
use Shopware\Plugins\ViisonCommon\Classes\Subscribers\Base;
use Shopware\Plugins\ViisonCommon\Classes\Util\Util as ViisonCommonUtil;
use Shopware\Plugins\ViisonSetArticles\Util;

/**
 * Loads and saves Article Attributes for Backend->Article->Detail view to make it available outside the attribute
 * fields.
 */
class ArticleSubscriber extends Base
{
    /**
     * Remembers article data before to after saveAction events
     * @var array
     */
    private $rememberArticleData = [];

    /**
     * @inheritdoc
     */
    public static function getSubscribedEvents()
    {
        return [
            'Shopware_Controllers_Backend_Article::detailListAction::after' => 'onAfterDetailListAction',
            'Shopware_Controllers_Backend_Article::getDetailDataForVariantGeneration::after' => 'onAfterGetDetailDataForVariantGeneration',
            'Shopware_Controllers_Backend_Article::getArticleData::after' => 'onAfterGetArticleData',
            'Shopware_Controllers_Backend_Article::saveAction::after' => 'onAfterSaveAction',
            'Shopware_Controllers_Backend_Article::saveAction::before' => 'onBeforeBackendArticleSaveAction',
            'Shopware_Controllers_Backend_Article::duplicateArticleAction::after' => 'onAfterDuplicateArticle',
        ];
    }

    /**
     * Reacts to an article duplication: if it's a set article, copy all sub articles for this specific article detail.
     *
     * @param Enlight_Hook_HookArgs $args
     */
    public function onAfterDuplicateArticle(Enlight_Hook_HookArgs $args)
    {
        $newArticleId = $args->getSubject()->View()->articleId;
        $articleController = $args->getSubject();
        $articleId = (int)$articleController->Request()->getParam('articleId');

        if (!$articleId) {
            return;
        }

        $article = $this->get('models')->find('Shopware\\Models\\Article\\Article', $articleId);

        if (!$article || !$article->getMainDetail()) {
            return;
        }

        /**
         * Remark: only consider and copy main article detail since variants are not duplicated.
         */
        $mainDetail = $article->getMainDetail();
        $this->get('models')->refresh($mainDetail);
        if ($mainDetail->getAttribute() && $mainDetail->getAttribute()->getViisonSetArticleActive()) {
            // It's a set article. Fetch new detail directly since the MainDetail connection is not working yet
            $newArticleMainDetail = $this->get('models')->getRepository('Shopware\\Models\\Article\\Detail')->findOneBy([
                'articleId' => $newArticleId,
                'kind' => 1,
            ]);
            if (!$newArticleMainDetail) {
                return;
            }
            $this->get('models')->getRepository('Shopware\\CustomModels\\ViisonSetArticles\\SetArticle')
                ->copySubArticles($mainDetail->getId(), $newArticleMainDetail->getId());
        }
    }

    /**
     * Adds the custom article detail fields to the response data.
     *
     * @param Enlight_Hook_HookArgs $args
     */
    public function onAfterGetArticleData(Enlight_Hook_HookArgs $args)
    {
        $data = $args->getReturn();
        $this->addCustomArticleDetailData($data[0]['mainDetail']);
        $args->setReturn($data);
    }

    /**
     * Adds the custom article detail fields to the response data.
     *
     * @param Enlight_Hook_HookArgs $args
     */
    public function onAfterDetailListAction(Enlight_Hook_HookArgs $args)
    {
        $data = $args->getSubject()->View()->data;
        if (is_array($data)) {
            foreach ($data as &$detail) {
                $this->addCustomArticleDetailData($detail);
            }
        }
        $args->getSubject()->View()->data = $data;
    }

    /**
     * Saves the custom article detail fields, in case the main action succeeded,
     * and adds the updated custom fields to the response data.
     *
     * @param Enlight_Hook_HookArgs $args
     */
    public function onAfterSaveAction(Enlight_Hook_HookArgs $args)
    {
        $view = $args->getSubject()->View();
        if (!$view->success) {
            return;
        }

        // Save the custom fields to all variants
        $requestData = $args->getSubject()->Request()->getParams();
        $responseData = $view->data;
        $this->updateAttributeFlags($responseData[0]['id'], $requestData['mainDetail'][0]);

        // Make sure to send the updated custom data back to the view
        $this->addCustomArticleDetailData($responseData[0]['mainDetail']);
        $view->data = $responseData;
    }

    /**
     * @param Enlight_Hook_HookArgs $args
     */
    public function onBeforeBackendArticleSaveAction(Enlight_Hook_HookArgs $args)
    {
        $params = $args->getSubject()->Request()->getParams();
        $mainDetailId = $params['mainDetail'][0]['id'];
        if ($mainDetailId) {
            $detail = $this->get('models')->find('Shopware\\Models\\Article\\Detail', $mainDetailId);
        }
        if ($detail) {
            $this->rememberArticleData[$mainDetailId] = [
                'viisonSetArticleActive' => $detail->getAttribute() && $detail->getAttribute()->getViisonSetArticleActive(),
            ];
        }
    }

    /**
     * Sets the custom attribute 'viisonSetarticleActive' for newly generated article variants
     * to ensure, that the value of this attribute is always the same for all variants of an article.
     *
     * @param Enlight_Hook_HookArgs $args The arguments passed by the method triggering the hook.
     */
    public function onAfterGetDetailDataForVariantGeneration(Enlight_Hook_HookArgs $args)
    {
        $data = $args->getReturn();
        $isActiveSetArticle = Util::isSetArticleByArticleId($data['articleId']);
        $data['attribute']['viisonSetarticleActive'] = $isActiveSetArticle;

        // Disable Pickware stock management for set articles
        if ($isActiveSetArticle) {
            // Pickware 5 and newer
            $data['attribute']['pickwareStockManagementDisabled'] = true;
            // Legacy (< 5.0.0) Pickware
            $data['attribute']['viisonNotRelevantForStockManager'] = true;
        }

        $args->setReturn($data);
    }

    /**
     * Sets the custom attribute 'viisonSetarticleActive' of all variants of a given article to a given value. If the
     * ArticleDetail is now a set article, some fields (e.g. lastStock) are reset to a default value.
     *
     * @param int $articleId ID of the article, whose variants should be updated
     * @param array $mainDetail main article detail data of the saved article
     */
    protected function updateAttributeFlags($articleId, $mainDetail)
    {
        $setArticleActive = $mainDetail['viisonSetarticleActive'];

        // It is not possible to fetch the details through the article at this point which is why we load the details and get the article through the details.
        /** @var ArticleDetail[] $articleDetails */
        $articleDetails = $this->get('models')->getRepository('Shopware\\Models\\Article\\Detail')->findBy([
            'articleId' => $articleId,
        ]);
        if (count($articleDetails) > 0) {
            /** @var Article $article */
            $article = $articleDetails[0]->getArticle();
        }
        if (!$article) {
            return;
        }
        foreach ($articleDetails as $articleDetail) {
            /** @var ArticleDetail $articleDetail */
            if (!$articleDetail->getAttribute()) {
                // Create new attributes for the article detail
                $attribute = new ArticleAttribute();
                $attribute->setArticleDetail($articleDetail);
                $articleDetail->setAttribute($attribute);
                $this->get('models')->persist($attribute);
            }
            $articleDetail->getAttribute()->setViisonSetarticleActive($setArticleActive);
            // Set some fields of set articles ArticleDetails to default values
            if ($setArticleActive) {
                // Set laststock according to the shopware version
                if (ViisonCommonUtil::assertMinimumShopwareVersion('5.4.0')) {
                    $articleDetail->setLastStock(0);
                }
            }

            // Update the Pickware "stock management disabled" flag if necessary
            if ($setArticleActive) {
                // Article is a set article - disable stock management for this article detail
                $this->setPickwareStockManagementDisabledForArticleDetail(true, $articleDetail);
            } elseif ($this->rememberArticleData[$mainDetail['id']] && $this->rememberArticleData[$mainDetail['id']]['viisonSetArticleActive']) {
                // Article has just stopped being a set article - re-enable stock management for this article detail
                $this->setPickwareStockManagementDisabledForArticleDetail(false, $articleDetail);
            } // Otherwise: was no set article, is no set article, do nothing further

            $this->get('models')->flush($articleDetail);
            $this->get('models')->flush($articleDetail->getAttribute());
        }
        // Reset the laststock flag for the main Article once
        if ($setArticleActive) {
            $supportsDetailLastStock = ViisonCommonUtil::assertMinimumShopwareVersion('5.4.0');
            if ($supportsDetailLastStock) {
                $article->getMainDetail()->setLastStock(0);
                $this->get('models')->flush($article->getMainDetail());
            } else {
                $article->setLastStock(0);
                $this->get('models')->flush($article);
            }
        }
    }

    /**
     * @param &array $articleDetailData
     */
    private function addCustomArticleDetailData(&$articleDetailData)
    {
        // Find the article detail and its attributes
        $articleDetail = $this->get('models')->find('Shopware\\Models\\Article\\Detail', $articleDetailData['id']);
        $attribute = $articleDetail->getAttribute();
        if ($attribute) {
            $articleDetailData['viisonSetarticleActive'] = $attribute->getViisonSetarticleActive();
        }
    }

    /**
     * If Pickware is installed, sets the "stock management disabled" flag on the given article detail. Does nothing if
     * Pickware is not installed.
     *
     * @param bool $pickwareStockManagementDisabled Whether stock management should be disabled for $articleDetail
     * @param ArticleDetail $articleDetail The article detail to update the stock management flag for
     */
    private function setPickwareStockManagementDisabledForArticleDetail(
        $pickwareStockManagementDisabled,
        ArticleDetail $articleDetail
    ) {
        $articleAttribute = $articleDetail->getAttribute();
        if (method_exists($articleAttribute, 'setPickwareStockManagementDisabled')) {
            $articleAttribute->setPickwareStockManagementDisabled($pickwareStockManagementDisabled);
        } elseif (method_exists($articleAttribute, 'setViisonNotRelevantForStockManager')) {
            // Legacy (< 5.0.0) Pickware
            $articleAttribute->setViisonNotRelevantForStockManager($pickwareStockManagementDisabled);
        }
    }
}
