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

use Shopware\Components\Model\ModelManager;
use Shopware\Models\Order\Order;
use Shopware\Models\Shop\Shop;
use Shopware\Plugins\ViisonCommon\Classes\Util\Util as ViisonCommonUtil;
use Shopware\Plugins\ViisonDHL\Components\ShippingDocumentTypeFactoryService;
use Shopware\Plugins\ViisonPickwareMobile\Classes\ShippingProvider\ReturnLabel;
use Shopware\Plugins\ViisonPickwareMobile\Interfaces\ShippingProvider\ReturnLabelCreation;
use Shopware\Plugins\ViisonShippingCommon\Classes\ShippingDocument;
use Shopware\Plugins\ViisonShippingCommon\Classes\ShippingProvider as ShippingCommonProvider;
use Shopware\Plugins\ViisonShippingCommon\Components\ReturnLabelCreation\ReturnLabelCreatorFactoryService;
use Shopware\Plugins\ViisonDHL\Util;

// Make sure all classes are included if this file is invoked directly via the API
include_once(__DIR__ . '/../Bootstrap.php');

/**
 * @Extends from ShippingCommon
 * Class ShippingProvider
 * @package Shopware\Plugins\ViisonDHL\Classes
 */
class ShippingProvider extends ShippingCommonProvider implements ReturnLabelCreation
{
    /**
     * @var ModelManager
     */
    private $entityManager;

    /**
     * @var \Zend_Db_Adapter_Abstract
     */
    private $database;

    /**
     * @var ShippingDocumentTypeFactoryService
     */
    private $documentTypeFactory;

    /**
     * @var ReturnLabelCreatorFactoryService
     */
    private $returnLabelCreatorFactoryService;

    public function __construct()
    {
        $shippingLabelGenerator = new ShippingLabelGenerator();
        $shippingUtil = new ShippingUtil();
        parent::__construct(Util::instance(), $shippingLabelGenerator, $shippingUtil);

        $this->entityManager = Shopware()->Container()->get('models');
        $this->database = Shopware()->Container()->get('db');
        $this->documentTypeFactory = Shopware()->Container()->get('viison_dhl.shipping_document_type_factory');
        $this->returnLabelCreatorFactoryService = Shopware()->Container()->get('viison_shipping_common.return_label_creator_factory_service');
    }

    /**
     * @inheritdoc
     */
    public function addLabelToOrder(
        $orderId,
        $shippingProductIdentifier = null,
        $weight = null,
        $dimensions = null,
        $options = [],
        $exportDocumentItems = null,
        $address = null
    ) {
        // Replace "empty" package dimensions (i.e. all zero values) with `null`, since they are only sent in case the
        // Picking app does not present the respective fields. This works around a problem caused by a new validation
        // introduced to the DHL webservice on March 24th 2019.
        // Although the same fix was added in ViisonPickwareMobile v5.7.0 we apply it here again to work around the
        // problem for shops that cannot update to Pickware 5 (for whatever reasons).
        if (is_array($dimensions) && $dimensions['length'] === 0 && $dimensions['width'] === 0 && $dimensions['height'] === 0) {
            $dimensions = null;
        }

        return parent::addLabelToOrder(
            $orderId,
            $shippingProductIdentifier,
            $weight,
            $dimensions,
            $options,
            $exportDocumentItems,
            $address
        );
    }

    /**
     * @Override from ShippingCommon
     *
     * @param \string[] $trackingCodes
     * @return string
     */
    public function statusURLForTrackingCodes($trackingCodes)
    {
        $select = $this->database->select()
            ->from(['o' => $this->pluginInfo->getOrderTableName()])
            ->join(['p' => $this->pluginInfo->getProductTableName()], 'p.id = o.productId')
            ->where('o.trackingCode IN (?)', $trackingCodes);

        $product = $this->database->fetchRow($select);

        // Use for express shipments a different URL (if the product is empty than it is a Return label)
        if ($product === false) {
            $url = $this->pluginInfo->getLocalizedTrackingURL($this->util->getShopLanguage());
        } else {
            $url = ($product['code'] === 'EXP') ? $this->pluginInfo->getExpressTrackingURL($this->util->getShopLanguage() !== 'de') : $this->pluginInfo->getLocalizedTrackingURL($this->util->getShopLanguage());
        }

        // Return the status URL
        return $url . implode($this->pluginInfo->getTrackingNumberDelimiter(), $trackingCodes) . $this->pluginInfo->additionalParametersForTrackingUrl($trackingCodes);
    }

    public function getGdprMailConfiguration(Shop $shop)
    {
        return $this->util->config(
            $shop->getId(),
            'gdprMailConfiguration'
        );
    }

    /**
     * @param Shop $shop
     * @return string
     */
    public function getGdprPhoneConfiguration(Shop $shop)
    {
        return $this->util->config(
            $shop->getId(),
            'gdprPhoneConfiguration'
        );
    }

    /**
     * @inheritdoc
     */
    public function getAvailableShippingDocumentTypes()
    {
        return $this->documentTypeFactory->getAvailableDocumentTypes();
    }

    /**
     * @inheritdoc
     */
    public function getAllDocumentDescriptions($orderId, $undispatchedOnly = false)
    {
        $orderTableName = $this->getPluginInfo()->getOrderTableName();
        $labels = $this->database->query(
            'SELECT `returnShipment`, `trackingCode`, `url`, `exportDocumentUrl`
                FROM `' . $orderTableName . '`
             WHERE orderId = :orderId',
            [
                'orderId' => $orderId,
            ]
        )->fetchAll();

        if ($undispatchedOnly) {
            $labels = $this->filterDispatchedLabels($orderId, $labels);
        }

        $documents = [];
        foreach ($labels as $label) {
            if ($label['returnShipment']) {
                $labelType = ShippingUtil::DOCUMENT_TYPE_RETURN_LABEL;
                $documentType = $this->documentTypeFactory->createReturnLabelDocumentType();
            } else {
                $labelType = ShippingUtil::DOCUMENT_TYPE_SHIPPING_LABEL;
                $documentType = $this->documentTypeFactory->createLabelDocumentType();
            }

            $documents[] = new ShippingDocument(
                $labelType,
                $label['trackingCode'],
                $documentType->getPageSizeName(),
                $documentType->getId()
            );

            if ($label['exportDocumentUrl']) {
                $exportDocumentType = $this->documentTypeFactory->createExportDocumentType();
                $documents[] = new ShippingDocument(
                    ShippingUtil::DOCUMENT_TYPE_EXPORT_DOCUMENT,
                    $label['trackingCode'],
                    $exportDocumentType->getPageSizeName(),
                    $exportDocumentType->getId()
                );
            }
        }

        return $documents;
    }

    /**
     * @inheritdoc
     */
    public function createReturnLabel(Order $order, $weightInKg)
    {
        $returnLabelCreator = $this->returnLabelCreatorFactoryService->createReturnLabelCreator(
            $this->pluginInfo,
            $this->util,
            $this->communication,
            $this->shippingLabelGenerator
        );
        $result = $returnLabelCreator->createReturnLabel(
            $order->getId(),
            $this->util->getOrder($order->getId()),
            $weightInKg
        );

        // Collect return label info
        $documentType = $this->documentTypeFactory->createReturnLabelDocumentType();
        $fileContents = $this->shippingUtil->getDocumentFileContents(
            ShippingUtil::DOCUMENT_TYPE_RETURN_LABEL,
            $result['documentIdentifier']
        );

        return new ReturnLabel($documentType->getId(), $documentType->getPageSizeName(), $fileContents);
    }

    /**
     * Removes all dispatched labels by filtering them out through their tracking codes.
     *
     * @param int $orderId
     * @param array $labels
     * @return array
     */
    private function filterDispatchedLabels($orderId, array $labels)
    {
        if (!ViisonCommonUtil::isPluginInstalledAndActive('Core', 'ViisonPickwareMobile')) {
            return $labels;
        }

        /** @var \Shopware\Models\Attribute\Order $orderAttribute */
        $orderAttribute = $this->entityManager->getRepository('Shopware\\Models\\Attribute\\Order')->findOneBy([
            'orderId' => $orderId,
        ]);

        $undispatchedTrackingCodes = $orderAttribute->getViisonUndispatchedTrackingCodes();
        if (empty($undispatchedTrackingCodes)) {
            return [];
        }

        $undispatchedCodes = explode(',', $undispatchedTrackingCodes);
        $undispatchedLabels = [];
        foreach ($labels as $label) {
            if (in_array($label['trackingCode'], $undispatchedCodes)) {
                $undispatchedLabels[] = $label;
            }
        }

        return $undispatchedLabels;
    }
}
