<?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 Shopware\Models\Document\Document as DocumentType;

/**
 * This subscriber hides specific documents in some document list. This ensures that these documents cannot be created
 * manually.
 */
class DocumentTypeHidingSubscriber extends AbstractBaseSubscriber
{
    /**
     * @var int[]
     */
    private $hiddenDocumentTypeIds;

    /**
     * @var string[]
     */
    private $hiddenDocumentTypeKeys;

    /**
     * @var string[]
     */
    private $hiddenDocumentTypeNames;

    /**
     * @var string[]
     */
    private $hiddenDocumentTypeNumberRangeNames;

    /**
     * Creates a subscriber to hide document type in the Shopware backend.
     *
     * The hidden document type can be defined by id, name, key and number range name. They are given the parameter
     * $hiddenDocumentTypes the document types to hide are defined. It must be in the following format:
     *
     * [
     *     'ids' => [
     *         // IDs of document types to hide
     *     ],
     *     'keys' => [
     *         // Keys of document types to hide
     *     ],
     *     'names' => [
     *         // Names of document types to hide
     *     ],
     *     'numberRangeNames' => [
     *         // Names of number ranges of document types types to hide
     *     ],
     * ]
     *
     * @param \Shopware_Components_Plugin_Bootstrap $pluginBootstrap
     * @param string[] $hiddenDocumentTypes An assoc array defining the DocumentTypes to hide
     */
    public function __construct(\Shopware_Components_Plugin_Bootstrap $pluginBootstrap, array $hiddenDocumentTypes)
    {
        parent::__construct($pluginBootstrap);

        $this->hiddenDocumentTypeIds = isset($hiddenDocumentTypes['ids']) ? $hiddenDocumentTypes['ids'] : [];
        $this->hiddenDocumentTypeKeys = isset($hiddenDocumentTypes['keys']) ? $hiddenDocumentTypes['keys'] : [];
        $this->hiddenDocumentTypeNames = isset($hiddenDocumentTypes['names']) ? $hiddenDocumentTypes['names'] : [];
        $this->hiddenDocumentTypeNumberRangeNames = isset($hiddenDocumentTypes['numberRangeNames']) ? $hiddenDocumentTypes['numberRangeNames'] : [];
    }

    /**
     * @return array
     */
    public static function getSubscribedEvents()
    {
        return [
            'Shopware_Controllers_Backend_Order::createDocumentAction::before' => 'onBeforeOrderCreateDocumentAction',
            'Shopware_Controllers_Backend_Order::getDocumentTypesAction::after' => 'onAfterGetDocumentTypesAction',
            'Shopware_Controllers_Backend_Order::loadStoresAction::after' => 'onAfterOrderLoadStoresAction',
        ];
    }

    /**
     * Blocks the creation of document of all types that are actually hidden.
     *
     * If this the create document action still gets called for one of the hidden document types, just throw. This
     * should never happen in practice because we prevent the document types from being selected for creation in the UI.
     *
     * @param \Enlight_Hook_HookArgs $args
     */
    public function onBeforeOrderCreateDocumentAction(\Enlight_Hook_HookArgs $args)
    {
        $documentTypeId = $args->getSubject()->Request()->getParam('documentType');
        if ($documentTypeId === null) {
            return;
        }
        /** @var DocumentType $documentType */
        $documentType = $this->get('models')->find('Shopware\\Models\\Document\\Document', $documentTypeId);

        if (!$documentType) {
            return;
        }

        if ($this->isHiddenDocumentType($documentType)) {
            throw new \InvalidArgumentException(
                sprintf('Manually generating documents of type "%s" is not allowed', $documentType->getName())
            );
        }
    }

    /**
     * Gets called when the DocumentTypes are retrieved via the Order sub application
     *
     * @param \Enlight_Hook_HookArgs $args
     */
    public function onAfterGetDocumentTypesAction(\Enlight_Hook_HookArgs $args)
    {
        /** @var \Enlight_View $view */
        $view = $args->getSubject()->View();

        if (!$view->success) {
            return;
        }

        $documentTypes = $view->data;
        $this->addHidePropertyToDocumentTypes($documentTypes);
        $view->assign('data', $documentTypes);
    }

    /**
     * Gets called when the Orders are retrieved via the Order sub application
     *
     * @param \Enlight_Hook_HookArgs $args
     */
    public function onAfterOrderLoadStoresAction(\Enlight_Hook_HookArgs $args)
    {
        /** @var \Enlight_View $view */
        $view = $args->getSubject()->View();

        if (!$view->success) {
            return;
        }

        $data = $view->data;
        $this->addHidePropertyToDocumentTypes($data['documentTypes']);
        $view->assign('data', $data);
    }

    /**
     * Adds the property viisonHidden to every document type in the array $documentTypes.
     *
     * @param array $documentTypes
     */
    private function addHidePropertyToDocumentTypes(array &$documentTypes)
    {
        foreach ($documentTypes as &$documentType) {
            if (!isset($documentType['viisonHidden'])) {
                $documentType['viisonHidden'] = false;
            }
            $documentType['viisonHidden'] = $documentType['viisonHidden'] || $this->isArrayRepresentationOfHiddenDocumentType($documentType);
        }
    }

    /**
     * @param DocumentType $documentType
     * @return bool
     */
    private function isHiddenDocumentType(DocumentType $documentType)
    {
        return in_array($documentType->getId(), $this->hiddenDocumentTypeIds)
            || (method_exists(DocumentType::class, 'getKey') && in_array($documentType->getKey(), $this->hiddenDocumentTypeKeys))
            || in_array($documentType->getName(), $this->hiddenDocumentTypeNames)
            || in_array($documentType->getNumbers(), $this->hiddenDocumentTypeNumberRangeNames);
    }

    /**
     * @param array $documentTypeArray
     * @return bool
     */
    private function isArrayRepresentationOfHiddenDocumentType(array $documentTypeArray)
    {
        return in_array($documentTypeArray['id'], $this->hiddenDocumentTypeIds)
            || in_array($documentTypeArray['key'], $this->hiddenDocumentTypeKeys)
            || in_array($documentTypeArray['name'], $this->hiddenDocumentTypeNames)
            || in_array($documentTypeArray['numbers'], $this->hiddenDocumentTypeNumberRangeNames);
    }
}
