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

use Doctrine\Common\Collections\ArrayCollection;

/**
 * Class DocumentBoxGuard
 *
 * This Subscriber helps protecting document templates in the backend. It prevents the user from using the
 * "copy over all document boxes from the current template to all other document templates"
 * button to overwrite (and probably mess up) specific custom templates.
 *
 * @package Shopware\Plugins\ViisonCommon\Classes\Subscribers
 */
class DocumentBoxGuard extends AbstractBaseSubscriber
{

    /**
     * Array of names of documents that should be protected.
     *
     * @var array
     */
    private $documentNames = [];

    /**
     * DocumentBoxGuard constructor.
     *
     * @param \Shopware_Components_Plugin_Bootstrap $pluginBootstrap
     * @param array $documentNames
     */
    public function __construct(
        \Shopware_Components_Plugin_Bootstrap $pluginBootstrap,
        $documentNames
    ) {
        $this->documentNames = $documentNames;
        parent::__construct($pluginBootstrap);
    }

    /**
     * @return array containing all subsciptions.
     */
    public static function getSubscribedEvents()
    {
        return [
            'Shopware_Controllers_Backend_Document::duplicatePropertiesAction::replace' => 'onReplaceBackendDocumentDuplicateProperties',
            'ViisonCommon_collectProtectedDocumentNames' => 'onCollectProtectedDocumentNames',
        ];
    }

    /**
     * Combines all document names from plugins to hide the copy-to-all-layouts button
     * in config document view.
     *
     * @param \Enlight_Event_EventArgs $args
     * @return ArrayCollection
     */
    public function onCollectProtectedDocumentNames(\Enlight_Event_EventArgs $args)
    {
        return new ArrayCollection($this->documentNames);
    }

    /**
     * Replaces the backend document controller's duplicateProperties action in
     * order to prevent overwriting the document boxes of the coupon document template
     * when the user triggers the "copy over all document boxes from the current template
     * to all other document templates" action. Furthermore it prevents the other direction
     * (copy all document boxes from the coupon template over to all other template) as well.
     *
     * Remark:
     * The implemented type of replacement has to be considered bad practice, since it
     * replaces the old action without calling the replaced action (at least for some cases).
     * Nevertheless this was the only option to implement the described behaviour, which is
     * a must have from the support teams point of view.
     * To make the code as easy to compare with the replaced one, must of the lines are
     * simply copied from the default action.
     *
     * Remark ViisonCommon_collectProtectedDocumentNames:
     * Since multiple replace hooks of plugins are executed after each other instead of only once
     * (SW up to 5.3) the "protection" of document types only works if each plugin has a full list
     * of protected documents. Therefore use the event to fetch all relevant documents.
     *
     * @param  \Enlight_Hook_HookArgs $args Hook arguments
     */
    public function onReplaceBackendDocumentDuplicateProperties(\Enlight_Hook_HookArgs $args)
    {
        // Fetch names of all documents (including other plugins) that need to be protected
        $protectedDocumentNames = $this->get('events')->collect(
            'ViisonCommon_collectProtectedDocumentNames',
            new ArrayCollection([])
        )->toArray();
        $protectedIds = $this->getDocumentIdArrayFromNames($protectedDocumentNames);

        $controller = $args->getSubject();
        $controller->View()->setTemplate();
        $id = intval($controller->Request()->id);

        /**
         * If given ID is not protected by this subscriber (id not in the array), use the regular overwrite logic.
         * Otherwise ignore the whole process and do nothing.
         */
        if (!in_array($id, $protectedIds)) {
            // Update statement
            $getDocumentTypes = $this->get('db')->fetchAll(
                'SELECT DISTINCT id FROM s_core_documents WHERE id NOT IN (' .
                $this->get('db')->quote(array_merge([$id], $protectedIds), \Zend_Db::INT_TYPE) . ')'
            );
            foreach ($getDocumentTypes as $targetID) {
                $this->get('db')->query(
                    'DELETE FROM s_core_documents_box WHERE documentID = ?',
                    [$targetID['id']]
                );
                $sqlDuplicate = 'INSERT IGNORE INTO s_core_documents_box
                    SELECT NULL AS id, ? AS documentID , name, style, value
                    FROM s_core_documents_box WHERE `documentID` = ?;
                ';
                $this->get('db')->query($sqlDuplicate, [$targetID['id'], $id]);
            }
        }
    }

    /**
     * Returns an array of document ids corresponding to the given list of document names.
     *
     * @param array $names
     * @return array int
     */
    private function getDocumentIdArrayFromNames($names)
    {
        $documentIds = [];
        foreach ($names as $name) {
            $document = Shopware()->Container()->get('models')->getRepository('Shopware\\Models\\Document\\Document')
                ->findOneBy([
                    'name' => $name,
                ]);
            if ($document) {
                $documentIds[] = $document->getId();
            }
        }

        return $documentIds;
    }
}
