<?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 Shopware\CustomModels\ViisonSetArticles\SetArticle;
use Shopware\Models\Shop\Shop;
use Shopware\Plugins\ViisonCommon\Classes\Subscribers\Base;
use Shopware\Plugins\ViisonCommon\Classes\Util\Util as ViisonCommonUtil;
use Shopware\Plugins\ViisonSetArticles\Components\SetArticleOrderDetailAssociation\SetArticleOrderDetailAssociationDescription;
use Shopware\Plugins\ViisonSetArticles\Components\SetArticleOrderDetailAssociationService;
use Shopware\Plugins\ViisonSetArticles\Util;

/**
 * Subscriber to add and manipulate sub articles to an order that is being saved.
 */
class CoreOrderSubscriber extends Base
{
    /**
     * @inheritdoc
     */
    public static function getSubscribedEvents()
    {
        return [
            'sOrder::sSaveOrder::before' => 'onBeforeSaveOrder',
            'Shopware_Modules_Order_SaveOrder_ProcessDetails' => 'onSaveOrderProcessDetails',
        ];
    }

    /**
     * Unfolds set articles before the order is created.
     *
     * @param \Enlight_Hook_HookArgs $args
     */
    public function onBeforeSaveOrder(\Enlight_Hook_HookArgs $args)
    {
        $sOrder = $args->getSubject();
        $shop = $this->get('shop');
        if ($sOrder->sBasketData) {
            $sOrder->sBasketData = $this->unfoldSetArticles($sOrder->sBasketData, $shop);
        }
    }

    /**
     * Assigns sub articles to their related set articles after the order has been created.
     *
     * @param \Enlight_Event_EventArgs $args
     */
    public function onSaveOrderProcessDetails(\Enlight_Event_EventArgs $args)
    {
        // Fetch order entity by number, since order id parameter was added to the event in SW 5.2
        $sOrder = $args->getSubject();
        $order = $this->get('models')->getRepository('Shopware\\Models\\Order\\Order')->findOneBy([
            'number' => $sOrder->sOrderNumber,
        ]);
        $orderDetails = $args->get('details');

        $setArticlePositionRelations = array_map(function ($orderDetail) {
            return new SetArticleOrderDetailAssociationDescription(
                $orderDetail['orderDetailId'],
                $orderDetail['viisonSetArticleSetArticleOrderNumber']
            );
        }, $orderDetails);

        /** @var SetArticleOrderDetailAssociationService $setArticleOrderDetailAssociationService */
        $setArticleOrderDetailAssociationService = $this->get('viison_set_articles.set_article_order_detail_association_service');
        $setArticleOrderDetailAssociationService->associateSetArticleOrderDetails(
            $order,
            $setArticlePositionRelations
        );
    }

    /**
     * Unfolds all set articles by modifying the content of the shopping basket.
     *
     * @param array $basketData Current state of the shopping basket
     * @param Shop $shop current shop, default null
     * @return array $basketData updated BasketData
     */
    public function unfoldSetArticles(array $basketData, $shop = null)
    {
        if (!array_key_exists('content', $basketData)) {
            return $basketData;
        }

        $newBasketContent = [];
        foreach ($basketData['content'] as $articleDetail) {
            if (!Util::isSetArticleByDetailId($articleDetail['articleDetailId'])) {
                // Do nothing with articles that are not set articles
                $newBasketContent[] = $articleDetail;
                continue;
            }

            // Set attribute of main set article
            $articleDetail['viisonSetArticleSetArticleOrderNumber'] = $articleDetail['ordernumber'];
            $newBasketContent[] = $articleDetail;

            $subArticles = $this->get('models')->getRepository('Shopware\\CustomModels\\ViisonSetArticles\\SetArticle')->findBy([
                'setId' => $articleDetail['articleDetailId'],
            ]);

            /*
             * Remark: Do NOT use new services according to sBasket->getBasketAdditionalDetails to fetch article
             * information since it does not work with sub articles that cannot be display in shop frontend (e.g. have
             * no category). Load missing information or copy it from its set article before write it into
             * s_order_basket
             */

            $subArticleDetailIds = array_map(function (SetArticle $subArticle) {
                return $subArticle->getArticleDetail()->getId();
            }, $subArticles);
            $additionalTexts = ViisonCommonUtil::getVariantAdditionalTexts($subArticleDetailIds);
            foreach ($subArticles as $subArticle) {
                $newArticle = [];

                $newArticle['articlename'] = $subArticle->getSubArticleName($shop);
                $newArticle['articleID'] = $subArticle->getArticleDetail()->getArticle()->getId();
                $newArticle['articleDetailId'] = $subArticle->getArticleDetail()->getId();
                $newArticle['ordernumber'] = $subArticle->getArticleDetail()->getNumber();
                $newArticle['additional'] = $additionalTexts[$subArticle->getArticleDetail()->getId()];
                $newArticle['shippingtime'] = $subArticle->getArticleDetail()->getShippingTime();
                $newArticle['weight'] = $subArticle->getArticleDetail()->getWeight();
                if ($subArticle->getArticleDetail()->getReleaseDate()) {
                    $newArticle['releasedate'] = $subArticle->getArticleDetail()->getReleaseDate()->format('Y-m-d');
                } else {
                    $newArticle['releasedate'] = null;
                }
                $newArticle['quantity'] = $subArticle->getQuantity() * $articleDetail['quantity'];
                $newArticle['priceNumeric'] = '0.00';
                $newArticle['price'] = '0,00';
                $newArticle['shippingfree'] = $articleDetail['shippingfree'];
                $newArticle['esd'] = $articleDetail['esd'];
                $newArticle['ean'] = $subArticle->getArticleDetail()->getEAN();
                // Mark sub articles to ease later post processing
                $newArticle['viisonSetArticleSetArticleOrderNumber'] = $articleDetail['ordernumber'];
                // Copy tax rate from set article since a "simple" selection of the sub articles tax does not consider
                // any customer or country restrictions.
                $newArticle['tax_rate'] = $articleDetail['tax_rate'];
                $newArticle['taxID'] = $articleDetail['taxID'];

                // Remark: also write data to s_order_basket and add more additional information
                if (ViisonCommonUtil::assertMinimumShopwareVersion('5.2')) {
                    $newArticle['id'] = $this->writeOrderBasket($newArticle);
                }

                // Add additional_details
                $additionalDetails = [];
                $additionalDetails['articleID'] = $subArticle->getArticleDetail()->getArticle()->getId();
                $additionalDetails['articleDetailsID'] = $subArticle->getArticleDetail()->getId();
                $additionalDetails['ordernumber'] = $subArticle->getArticleDetail()->getNumber();
                $additionalDetails['articleName'] = $subArticle->getSubArticleName($shop);
                $additionalDetails['shippingtime'] = $subArticle->getArticleDetail()->getShippingTime();
                $additionalDetails['tax'] = $articleDetail['tax_rate'];
                $additionalDetails['taxID'] = $articleDetail['taxID'];

                $newArticle['additional_details'] = $additionalDetails;

                // Add the sub article after its set article
                $newBasketContent[] = $newArticle;
            }
        }

        // Overwrite the basket content with the new content (that includes sub articles)
        $basketData['content'] = $newBasketContent;

        return $basketData;
    }

    /**
     * (SW5.2) Writes additional basket data (added sub articles) into s_order_basket according to sBasket->sAddArticle
     *
     * @param $article article information
     * @return int id of row in s_order_basket
     */
    private function writeOrderBasket($article)
    {
        $sql = '
            INSERT INTO s_order_basket (id, sessionID, userID, articlename, articleID,
                ordernumber, shippingfree, quantity, price, netprice,
                datum, esdarticle, partnerID, config)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
        ';

        $params = [
            '',
            '', // ignore $sessionId,
            '', // ignore $this->session->get('sUserId'),
            $article['articlename'],
            $article['articleID'],
            (string)$article['ordernumber'],
            $article['shippingfree'],
            $article['quantity'],
            $article['price'],
            $article['price'], // net = gross, since price = 0.0
            date('Y-m-d H:i:s'),
            ($article['esd']) ? $article['esd'] : '',
            '', // ignore $this->session->get('sPartner'),
            '',
        ];

        $this->get('db')->query($sql, $params);

        return $this->get('db')->lastInsertId();
    }
}
