<?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\ViisonPickwareMobile\Components;

use Enlight_Event_EventManager;
use Enlight_Event_Handler_Default;
use Enlight_Hook_HookArgs;
use Shopware\Components\Model\ModelManager;
use Shopware\Models\Article\Detail as ArticleDetail;
use Shopware\Models\Order\Detail as OrderDetail;
use Shopware\Plugins\ViisonCommon\Classes\Util\Document as DocumentUtil;
use Shopware\Plugins\ViisonCommon\Classes\Util\OrderDetailUtil;
use Shopware\Plugins\ViisonPickwareERP\Components\OrderDetailQuantityCalculator\OrderDetailQuantityCalculator;

class DocumentComponentListenerService
{
    /**
     * @var Enlight_Event_EventManager
     */
    protected $eventManager;

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

    /**
     * @var PluginConfigService
     */
    protected $pluginConfigService;

    /**
     * @var OrderDetailQuantityCalculator
     */
    protected $orderDetailQuantityCalculator;

    /**
     * @param Enlight_Event_EventManager $eventManager
     * @param ModelManager $entityManager
     * @param PluginConfigService $pluginConfigService
     * @param OrderDetailQuantityCalculator $orderDetailQuantityCalculator
     */
    public function __construct(
        Enlight_Event_EventManager $eventManager,
        ModelManager $entityManager,
        PluginConfigService $pluginConfigService,
        OrderDetailQuantityCalculator $orderDetailQuantityCalculator
    ) {
        $this->eventManager = $eventManager;
        $this->entityManager = $entityManager;
        $this->pluginConfigService = $pluginConfigService;
        $this->orderDetailQuantityCalculator = $orderDetailQuantityCalculator;
    }

    /**
     * Registers a one-off (after) hook handler on {@link Shopware_Components_Document::assignValues()} that
     * manipulates the template variables of a delivery note to add the information about remaining positions
     * of an order if the delivery note is a partial delivery note.
     */
    public function addRemainingPositionsOnNextDocument()
    {
        $listener = null;
        $listener = new Enlight_Event_Handler_Default(
            'Shopware_Components_Document::assignValues::after',
            function (Enlight_Hook_HookArgs $args) use (&$listener) {
                $this->eventManager->removeListener($listener);

                /** @var $document \Shopware_Components_Document */
                $document = $args->getSubject();
                $documentTypeId = DocumentUtil::getDocumentTypeId($document);
                if ($documentTypeId !== 2) {
                    return;
                }

                /** @var PluginConfigService $pluginConfigService */
                $pluginConfigService = $this->pluginConfigService;
                if (!$pluginConfigService->getShowRemainingPositionsOnPartialDeliveryNote()) {
                    return;
                }

                $orderId = DocumentUtil::getOrderId($document);
                $remainingPositions = $this->getUnprocessedItemsOfOrder($orderId);
                if (count($remainingPositions) === 0) {
                    return;
                }

                $document->_view->assign('viisonHasRemainingPositions', true);
                $document->_view->assign('viisonRemainingPositions', $remainingPositions);
            }
        );
        $this->eventManager->registerListener($listener);
    }

    /**
     * Finds all order items, which are unprocessed (not picked or shipped) Picking app and thus should be part of the
     * additional information of a partial delivery note ('Remaining positions of the order').
     *
     * @param int $orderId
     * @return array
     */
    private function getUnprocessedItemsOfOrder($orderId)
    {
        /** @var ModelManager $entityManager */
        $entityManager = $this->entityManager;
        $builder = $entityManager->createQueryBuilder();
        $builder
            ->select('orderDetail')
            ->from(OrderDetail::class, 'orderDetail')
            ->leftJoin('orderDetail.attribute', 'orderDetailAttribute')
            ->leftJoin(ArticleDetail::class, 'articleDetail', 'WITH', 'articleDetail.number = orderDetail.articleNumber')
            ->leftJoin('articleDetail.attribute', 'articleDetailAttribute')
            ->where('orderDetail.orderId = :orderId')
            ->setParameter('orderId', $orderId)
            // Ignore articles that are not relevant for picking, since they are not available in the picking app anyway
            ->andWhere('articleDetailAttribute.pickwareNotRelevantForPicking = 0')
            // Remark: mode 0 or 1 filters all non-articles-positions (e.g. shipping costs or discounts). This is
            // similar to Shopware's handling in the delivery note template 'index_ls.tpl'.
            ->andWhere(
                $builder->expr()->orX(
                    'orderDetail.mode = :modeDefaultArticle',
                    'orderDetail.mode = :modePremiumArticle'
                )
            )
            ->setParameters([
                'modeDefaultArticle' => OrderDetailUtil::ORDER_DETAIL_MODE_DEFAULT_ARTICLE,
                'modePremiumArticle' => OrderDetailUtil::ORDER_DETAIL_MODE_PREMIUM_ARTICLE,
            ]);
        $result = $builder->getQuery()->getResult();

        /** @var OrderDetailQuantityCalculator $orderDetailQuantityCalculator */
        $orderDetailQuantityCalculator = $this->orderDetailQuantityCalculator;
        $allOrderItems = array_map(function (OrderDetail $orderDetail) use ($orderDetailQuantityCalculator) {
            return [
                'ordernumber' => $orderDetail->getArticleNumber(),
                'name' => $orderDetail->getArticleName(),
                'remainingQuantity' => $orderDetailQuantityCalculator->calculateRemainingQuantityToShip($orderDetail),
            ];
        }, $result);

        return array_values(array_filter($allOrderItems, function (array $orderItem) {
            return $orderItem['remainingQuantity'] > 0;
        }));
    }
}
