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

use Doctrine\ORM\QueryBuilder;
use ReflectionObject;
use Shopware\CustomModels\ViisonPickwareERP\StockLedger\StockLedgerEntry;
use Shopware\Models\Attribute\Document as OrderDocumentAttribute;
use Shopware\Models\Order\Document\Document as OrderDocument;
use Shopware\Plugins\ViisonCommon\Classes\Subscribers\AbstractBaseSubscriber;
use Shopware_Components_Document;

class DocumentComponentSubscriber extends AbstractBaseSubscriber
{
    /**
     * @var mixed|null
     */
    private $pickwareDocumentConfigFromRequest;

    /**
     * @see \Shopware\Plugins\ViisonCommon\Classes\Subscribers\Base::getSubscribedEvents()
     */
    public static function getSubscribedEvents()
    {
        return [
            'Shopware_Controllers_Api_ViisonPickwareCommonOrders::postDocumentsAction::before' => 'onBeforePostDocumentsAction',
            'Shopware_Components_Document::assignValues::after' => 'onAfterAssignValues',
            // This subscriber must be added to the event manager to ensure that a hook proxy for
            // `Shopware_Components_Document::render()` is created. This allows us to use one-off hook listeners as
            // registered by `DocumentComponentListenerService`.
            'Shopware_Components_Document::render::after' => 'dummyHookMethod',
        ];
    }

    /**
     * When creating a new document save the pickwareDocumentConfig here, so we can access it later once the document is
     * created and persisted.
     *
     * @param \Enlight_Hook_HookArgs $args
     */
    public function onBeforePostDocumentsAction(\Enlight_Hook_HookArgs $args)
    {
        $this->pickwareDocumentConfigFromRequest = $args->getSubject()->Request()->getParam('pickwareDocumentConfig');
    }

    /**
     * @param \Enlight_Hook_HookArgs $args
     */
    public function onAfterAssignValues(\Enlight_Hook_HookArgs $args)
    {
        /** @var Shopware_Components_Document $document */
        $documentComponent = $args->getSubject();

        $this->addPickwareStockItemPropertyValuesToPositions($documentComponent);

        /** @var OrderDocument|null $document */
        $document = $this->getDocumentFromDocumentComponent($documentComponent);
        if (!$document) {
            return;
        }
        $this->updatePickwareDocumentConfigFromRequest($document);
        $this->addPickwareDocumentConfigToTemplate($documentComponent, $document);
    }

    /**
     * @param \Enlight_Hook_HookArgs $args
     */
    public function dummyHookMethod(\Enlight_Hook_HookArgs $args)
    {
        // Don't do anything here
    }

    /**
     * @param Shopware_Components_Document $document
     */
    private function addPickwareStockItemPropertyValuesToPositions(Shopware_Components_Document $document)
    {
        /** @var array $templateVariables */
        $templateVariables = $document->_view->getTemplateVars();

        if (!$templateVariables['Order'] || !$templateVariables['Order']['_positions']) {
            return;
        }

        $positionsFromOrder = $templateVariables['Order']['_positions'];
        /** @var QueryBuilder $builder */
        $builder = $this->get('models')->createQueryBuilder();
        $builder
            ->select(
                'stockLedgerEntry',
                'stockItems',
                'propertyValues',
                'itemProperty'
            )
            ->from(StockLedgerEntry::class, 'stockLedgerEntry')
            ->innerJoin('stockLedgerEntry.stockItems', 'stockItems')
            ->innerJoin('stockItems.propertyValues', 'propertyValues')
            ->innerJoin('propertyValues.itemProperty', 'itemProperty')
            ->where('stockLedgerEntry.orderDetailId IN (:orderDetailIds)')
            ->setParameter('orderDetailIds', array_column($positionsFromOrder, 'id'))
            ->orderBy('stockLedgerEntry.created');

        // Fetch results
        $allStockEntries = $builder->getQuery()->getArrayResult();

        $stockEntriesByOrderDetailId = [];
        foreach ($allStockEntries as $stockEntry) {
            if (!$stockEntriesByOrderDetailId[$stockEntry['orderDetailId']]) {
                $stockEntriesByOrderDetailId[$stockEntry['orderDetailId']] = [];
            }
            $stockEntriesByOrderDetailId[$stockEntry['orderDetailId']][] = $stockEntry;
        }

        $templateVariables['Order']['_positions'] = $this->applyPickwareStockItemPropertyValuesToTemplateVariablePositions(
            $positionsFromOrder,
            $stockEntriesByOrderDetailId
        );

        $templateVariables['Pages'] = array_map(function ($positionsOfPage) use ($stockEntriesByOrderDetailId) {
            return $this->applyPickwareStockItemPropertyValuesToTemplateVariablePositions(
                $positionsOfPage,
                $stockEntriesByOrderDetailId
            );
        }, $templateVariables['Pages']);

        $document->_view->assign($templateVariables);
    }

    /**
     * @param array $positions
     * @param array $stockEntriesByOrderDetailId
     * @return array
     */
    private function applyPickwareStockItemPropertyValuesToTemplateVariablePositions($positions, $stockEntriesByOrderDetailId)
    {
        foreach ($positions as &$position) {
            $stockEntries = $stockEntriesByOrderDetailId[$position['id']];
            $position['pickwareStockItemPropertyValues'] = [];
            if (!$stockEntries) {
                continue;
            }
            foreach ($stockEntries as $stockEntry) {
                foreach ($stockEntry['stockItems'] as $stockItem) {
                    $stockItemValues = [];
                    foreach ($stockItem['propertyValues'] as $propertyValue) {
                        $stockItemValues[$propertyValue['itemProperty']['name']] = $propertyValue['value'];
                    }
                    $position['pickwareStockItemPropertyValues'][] = $stockItemValues;
                }
            }
        }

        return $positions;
    }

    private function getDocumentFromDocumentComponent(Shopware_Components_Document $documentComponent)
    {
        $reflectionObject = new ReflectionObject($documentComponent);
        $documentRowIdProperty = $reflectionObject->getProperty('_documentRowID');
        $documentRowIdProperty->setAccessible(true);
        $documentId = $documentRowIdProperty->getValue($documentComponent);
        if (!$documentId) {
            return null;
        }

        return $this->get('models')->find(OrderDocument::class, $documentId);
    }

    private function updatePickwareDocumentConfigFromRequest(OrderDocument $document)
    {
        if (!$this->pickwareDocumentConfigFromRequest) {
            return;
        }

        $documentAttribute = $document->getAttribute();
        if (!$documentAttribute) {
            $documentAttribute = new OrderDocumentAttribute();
            $document->setAttribute($documentAttribute);
            $this->get('models')->persist($documentAttribute);
        }

        $pickwareDocumentConfig = $this->getPickwareDocumentConfig($documentAttribute);
        $mergedPickwareDocumentConfig = array_replace_recursive(
            $pickwareDocumentConfig ?: [],
            $this->pickwareDocumentConfigFromRequest
        );
        $documentAttribute->setPickwareDocumentConfig(json_encode($mergedPickwareDocumentConfig));
        $this->get('models')->flush($documentAttribute);
    }

    private function getPickwareDocumentConfig(OrderDocumentAttribute $documentAttribute)
    {
        $pickwareDocumentConfigString = $documentAttribute->getPickwareDocumentConfig();
        if (!$pickwareDocumentConfigString) {
            return null;
        }

        return json_decode($pickwareDocumentConfigString, true);
    }

    private function addPickwareDocumentConfigToTemplate(
        Shopware_Components_Document $documentComponent,
        OrderDocument $document
    ) {
        $documentAttribute = $document->getAttribute();
        if (!$documentAttribute) {
            return;
        }
        $pickwareDocumentConfig = $this->getPickwareDocumentConfig($documentAttribute);
        if (!$pickwareDocumentConfig) {
            return;
        }

        /** @var array $templateVariables */
        $templateVariables = $documentComponent->_view->getTemplateVars();
        $templateVariables['pickwareDocumentConfig'] = $pickwareDocumentConfig;
        $documentComponent->_view->assign($templateVariables);
    }
}
