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

use Shopware\Components\Model\ModelManager;
use Shopware\CustomModels\ViisonShippingCommon\Shipment\Document;
use Shopware\Plugins\ViisonCommon\Components\FileStorage\FileStorage;
use Shopware\Plugins\ViisonShippingCommon\Util;

/**
 * Abstract class that provides shipping specific utility functions. Dispatch service provider plugins
 * have to provide a derived concrete class implementing the abstract functions.
 */
abstract class ShippingUtil
{
    /**
     * @deprecated Use Document instead
     * Document types
     */
    const DOCUMENT_TYPE_SHIPPING_LABEL = 'label';
    const DOCUMENT_TYPE_RETURN_LABEL = 'returnLabel';
    const DOCUMENT_TYPE_EXPORT_DOCUMENT = 'exportDocument';

    private $pluginInfo;
    private $util;

    public function __construct(Util $util)
    {
        $this->pluginInfo = $util->getPluginInfo();
        $this->util = $util;
    }

    /**
     * Sends a request to the dispatch service provider to download the document of the given type
     * with the given tracking code and save it to the given file path.
     *
     * @Note: This method should call the Communcaiton class method for retriving the disaptch provider.
     *
     * @param string $docType
     * @param string $trackingCode
     * @param string $path The file path where the document should be saved.
     * @return string|null A file containing the document.
     * @throws \Exception if the operation was not successful or is generally not supported by the dispatch service provider.
     */
    abstract protected function loadDocumentFromDispatchServiceProvider($docType, $trackingCode, $path);

    /**
     * @deprecated
     * Saves the given tracking code in the order corresponding to the given id.
     * That is, the new tracking code will be appended if some code already exists.
     * Furthermore is is saved as a new entry in the custom plugin order table.
     *
     * @param int $orderId The id of the order the tracking code should be added to.
     * @param string $identifier The identifier distinguishing normal shipping labels from return labels.
     * @param string $trackingCode The tracking code to add.
     * @param array $shippingDetails The receiver address or an empty array, if the shipping address of the order should be used.
     * @param int|null $productId
     * @param float|null $weight The shipment weight
     * @param string|null $pageSize The page size of the label PDF.
     * @return string A string containing the old tracking codes as well as the new one.
     */
    public function saveTrackingCode($orderId, $identifier, $trackingCode, $shippingDetails, $productId = null, $weight = null, $pageSize = null)
    {
        $sqlHelper = new \Shopware\Plugins\ViisonCommon\Classes\Installation\SQLHelper(Shopware()->Db());

        // only append if the label is no return label
        if ($identifier !== self::DOCUMENT_TYPE_RETURN_LABEL) {
            // Get existing tracking codes
            $oldTrackingCodes = Shopware()->Db()->fetchOne(
                'SELECT trackingcode
                FROM s_order
                WHERE id = ?',
                array(
                    $orderId
                )
            );

            // Save new tracking code
            $newTrackingCodes = (strlen($oldTrackingCodes) > 0) ? $oldTrackingCodes.','.$trackingCode : $trackingCode;
            Shopware()->Db()->query(
                'UPDATE s_order
                SET trackingcode = ?
                WHERE id = ?',
                array(
                    $newTrackingCodes,
                    $orderId
                )
            );
        }

        // If no specific address for the label was given, the shipping address of the order should get used
        if (empty($shippingDetails)) {
            $shippingDetails = $this->util->getOrder($orderId);
        }

        // Save the customer address
        $customerAddress = $this->util->buildCustomerAddress($shippingDetails);

        $pluginOrderTable = new \Zend_Db_Table($this->pluginInfo->getOrderTableName());

        // Backward Compatibility for the prior plugins
        if (!$sqlHelper->doesColumnExist($this->pluginInfo->getOrderTableName(), 'documentIdentifier')) {
            // Document identifier row is not supported use tracking code instead
            $params = array(
                'orderId' => $orderId,
                'trackingCode' => $trackingCode,
                'url' => $this->util->createDocumentURL($identifier . PluginInfo::LABEL_IDENTIFIER_SEPARATOR . $trackingCode),
                'returnShipment' => $identifier == self::DOCUMENT_TYPE_RETURN_LABEL,
                'customerAddress' => $customerAddress,
                'created' => new \Zend_Db_Expr('UTC_TIMESTAMP()'),
            );
        } else {
            // Document identifier row is supported
            $params = array(
                'orderId' => $orderId,
                'trackingCode' => $trackingCode,
                'url' => $this->util->createDocumentURL($identifier . PluginInfo::LABEL_IDENTIFIER_SEPARATOR . $trackingCode),
                'documentIdentifier' => $trackingCode,
                'returnShipment' => $identifier == self::DOCUMENT_TYPE_RETURN_LABEL,
                'customerAddress' => $customerAddress,
                'created' => new \Zend_Db_Expr('UTC_TIMESTAMP()'),
            );
        }

        // Only set productId if provided, this ensures backwards compatibility of the ShippingCommon API
        if (!is_null($productId)) {
            $params['productId'] = $productId;
        }
        // Only set weight if provided, this ensures backwards compatibility of the ShippingCommon API
        if (!is_null($weight)) {
            $params['weight'] = $weight;
        }
        // Only set pageSize if provided, this ensures backwards compatibility of the ShippingCommon API
        if ($pageSize !== null) {
            $params['pageSize'] = $pageSize;
        }
        $pluginOrderTable->insert($params);

        return $newTrackingCodes;
    }

    /**
     * Saves the given tracking code in the order corresponding to the given id.
     * That is, the new tracking code will be appended if some code already exists.
     * Furthermore is is saved as a new entry in the custom plugin order table.
     *
     * NOTE: TODO REMOVE this after DHL refectoring
     *
     * @param int $orderId The id of the order the tracking code should be added to.
     * @param string $identifier The identifier distinguishing normal shipping labels from return labels.
     * @param string $trackingCode The tracking code to add.
     * @param array $shippingDetails The receiver address or an empty array, if the shipping address of the order should be used.
     * @param int|null $productId
     * @param float|null $weight The shipment weight
     * @param null|string $documentIdentifier The identifier used for saving the documents and creating the path. By default the DB id is used.
     * @param string|null $pageSize The page size of the label PDF.
     * @return string A string containing the old tracking codes as well as the new one.
     * @throws \Exception
     */
    public function saveShippingLabelDataWithOrderTrackingCodes($orderId, $identifier, $trackingCode, $shippingDetails, $productId = null, $weight = null, $documentIdentifier = null, $pageSize = null)
    {
        /** @var Shopware/Db */
        $database = Shopware()->Db();
        /** @var \Zend_Db_Table */
        $orderTable = new \Zend_Db_Table('s_order');

        // Save tracking codes only if order id is not null
        // Possible scenario: Free form labels
        if (isset($orderId)) {
            // Get existing tracking codes
            $oldTrackingCodes = $database->fetchOne(
                $orderTable->select()
                    ->from($orderTable, array('trackingcode'))
                    ->where('id = ?', $orderId)
            );

            // Save new tracking code and update the existing ones with the new one
            $newTrackingCodes = (
                strlen(
                    $oldTrackingCodes
                ) > 0) ? $oldTrackingCodes . ',' . $trackingCode : $trackingCode;
            $orderTableWhere = $orderTable->getAdapter()->quoteInto('id = ?', $orderId);
            $orderTableUpdate = array('trackingcode' => $newTrackingCodes);
            $orderTable->update($orderTableUpdate, $orderTableWhere);
        }

        // If no specific address for the label was given, the shipping address of the order should get used
        $shippingDetails = empty($shippingDetails) ? $this->util->getOrder($orderId) : $shippingDetails;

        // Save the customer address
        $customerAddress = $this->util->buildCustomerAddress($shippingDetails);
        $params = array(
            'orderId' => $orderId,
            'trackingCode' => $trackingCode,
            'url' => $this->util->createDocumentURL(
                $identifier . PluginInfo::LABEL_IDENTIFIER_SEPARATOR . $trackingCode
            ),
            'returnShipment' => $identifier == self::DOCUMENT_TYPE_RETURN_LABEL,
            'customerAddress' => $customerAddress,
            'created' => new \Zend_Db_Expr('UTC_TIMESTAMP()'),
        );

        // Only set productId if provided, this ensures backwards compatibility of the ShippingCommon API
        if (!is_null($productId)) {
            $params['productId'] = $productId;
        }
        // Only set weight if provided, this ensures backwards compatibility of the ShippingCommon API
        if (!is_null($weight)) {
            $params['weight'] = $weight;
        }
        // Only set pageSize if provided, this ensures backwards compatibility of the ShippingCommon API
        if ($pageSize !== null) {
            $params['pageSize'] = $pageSize;
        }

        // Insert new label inside Adapter's order table
        $documentId = $this->insertLabelDataInAdapterOrderTable($params, $identifier, $documentIdentifier);

        // Create file path for the document
        $documentPath = $this->util->createFilePath($documentId, $identifier);

        return array(
            'trackingCodes' => $newTrackingCodes,
            'documentIdentifier' => $documentId,
            'documentPath' => $documentPath
        );
    }

    /**
     * Insert shipping label data to DB and after that update the URL with the document identifier
     * TODO Remove after DHL refectoring
     * @param array $params The params to be saved in the DB table
     * @param $identifier Tells which label it is (return label, export ...)
     * @param null $documentIdentifier The optional Identifier used instead of the lastInsertId
     * @return string The document identifier used by the update operation
     * @throws \Exception
     */
    public function insertLabelDataInAdapterOrderTable(array $params, $identifier, $documentIdentifier = null)
    {
        try {
            // Insert new label inside Adapter's order table
            $pluginOrderTable = new \Zend_Db_Table($this->pluginInfo->getOrderTableName());
            $pluginOrderTable->insert($params);
            $lastInsertId = $pluginOrderTable->getAdapter()->lastInsertId();

            /**
             * NOTE: We are updating the column name after creation because we can only be 99% sure what the exact
             *       lastInsertId is after the inset has been done. The Alternative would be to use the table Schema
             *       to get the next autoIncrement value but that value can also be altered and not updated correctly.
             *
             * Also we are setting the documentIdentifier after the insert so we can use by default the last insert id.
             */
            $documentId = $documentIdentifier ?: $lastInsertId;
            $data = array(
                'url' => $this->util->createDocumentURL($identifier . PluginInfo::LABEL_IDENTIFIER_SEPARATOR . $documentId)
            );
            $where = $pluginOrderTable->getAdapter()->quoteInto('id = ?', $lastInsertId);
            $pluginOrderTable->update($data, $where);

            return $documentId;
        } catch (\Zend_Db_Exception $e) {
            throw new \Zend_Db_Exception($e->getMessage());
        } catch (\Zend_Db_Statement_Exception $e) {
            throw new \Zend_Db_Statement_Exception($e->getMessage());
        } catch (\Zend_Db_Table_Select_Exception $e) {
            throw new \Zend_Db_Table_Select_Exception($e->getMessage());
        } catch (\Zend_Exception $e) {
            throw new \Zend_Exception($e->getMessage());
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }
    }

    /**
     * @deprecated
     * Deletes the given tracking code from the order it is contained in,
     * as well as from the custom plugin order table.
     * NOTE: Because of backward compatiblity the name trackingCode has not been changed
     *
     * @param string $trackingCode The tracking code to delete.
     * @param string $documentIdentifier The document to delete.
     * @return string A string containing all remaining tracking codes or FALSE, if no order is found.
     */
    public function deleteTrackingCode($trackingCode, $documentIdentifier)
    {
        $pluginOrderTable = new \Zend_Db_Table($this->pluginInfo->getOrderTableName());

        // Search for the document via the url with the documentIdentifier
        $pluginOrderTable->delete($pluginOrderTable->getAdapter()->quoteInto('id = ?', $documentIdentifier));

        // Find the order attributes containing the given $trackingCode as a return label tracking code and remove it
        $orderAttributesTable = new \Zend_Db_Table('s_order_attributes');
        $orderAttribute = $orderAttributesTable->fetchAll(
            $orderAttributesTable->select()
                ->from($orderAttributesTable, array('id', 'viison_return_label_tracking_id'))
                ->where('viison_return_label_tracking_id LIKE ?', '%' . $trackingCode . '%')
        )->toArray();

        if ($orderAttribute) {
            $orderAttributeId = $orderAttribute[0]['id'];
            $returnLabelTrackingCodes = $orderAttribute[0]['viison_return_label_tracking_id'];

            // Get all tracking codes and remove the one given
            $newReturnLabelTrackingCodes = explode(',', $returnLabelTrackingCodes);
            $index = array_search($trackingCode, $newReturnLabelTrackingCodes);
            unset($newReturnLabelTrackingCodes[$index]);
            $newReturnLabelTrackingCodes = implode(',', $newReturnLabelTrackingCodes);

            // Update the attributes order with the new tracking codes
            /** @var \Zend_Db_Table */
            $orderAttributesTable = new \Zend_Db_Table('s_order_attributes');
            $orderAttributesTableWhere = $orderAttributesTable->getAdapter()->quoteInto(
                'id = ?',
                $orderAttributeId
            );
            $orderAttributesUpdate = array('viison_return_label_tracking_id' => $newReturnLabelTrackingCodes);
            $orderAttributesTable->update($orderAttributesUpdate, $orderAttributesTableWhere);
        }

        // Find the order containing the given $trackingCode and delete it from the trackingCode field
        $orderTable = new \Zend_Db_Table('s_order');
        $order = $orderTable->fetchAll(
            $orderTable->select()
                ->from($orderTable, array('id', 'trackingcode'))
                ->where('trackingcode LIKE ?', '%' . $trackingCode . '%')
        )->toArray();
        if (empty($order)) {
            return false;
        }
        $orderId = $order[0]['id'];
        $trackingCodes = $order[0]['trackingcode'];

        // Get all tracking codes and remove the one given
        $newTrackingCodes = explode(',', $trackingCodes);
        $index = array_search($trackingCode, $newTrackingCodes);
        unset($newTrackingCodes[$index]);
        $newTrackingCodes = implode(',', $newTrackingCodes);

        // Update the order with the new tracking codes
        /** @var \Zend_Db_Table */
        $orderTable = new \Zend_Db_Table('s_order');
        $orderTableWhere = $orderTable->getAdapter()->quoteInto('id = ?', $orderId);
        $orderTableUpdate = array('trackingcode' => $newTrackingCodes);
        $orderTable->update($orderTableUpdate, $orderTableWhere);

        // Check if Pickware is installed, if yes, also remove the tracking code from the undispatched tracking codes
        $attribute = new \Shopware\Models\Attribute\Order;
        if (method_exists($attribute, 'getViisonUndispatchedTrackingCodes')) {
            // todo issue for pickware mobile to fire a event
            $orderAttributesTable = new \Zend_Db_Table('s_order_attributes');
            $orderAttribute = $orderAttributesTable->fetchAll(
                $orderAttributesTable->select()
                ->from($orderAttributesTable, array('id', 'viison_undispatched_tracking_codes'))
                ->where('viison_undispatched_tracking_codes LIKE ?', '%' . $trackingCode . '%')
            )->toArray();
            if (empty($orderAttribute)) {
                return false;
            }

            $orderAttributeId = $orderAttribute[0]['id'];
            $undispatchedTrackingCodes = $orderAttribute[0]['viison_undispatched_tracking_codes'];

            // Get all tracking codes and remove the one given
            $newUndispatchedTrackingCodes = explode(',', $undispatchedTrackingCodes);
            $index = array_search($trackingCode, $newUndispatchedTrackingCodes);
            unset($newUndispatchedTrackingCodes[$index]);
            $newUndispatchedTrackingCodes = implode(',', $newUndispatchedTrackingCodes);

            // Update the attributes order with the new tracking codes
            /** @var \Zend_Db_Table */
            $orderAttributesTable = new \Zend_Db_Table('s_order_attributes');
            $orderAttributesTableWhere = $orderTable->getAdapter()->quoteInto('id = ?', $orderAttributeId);
            $orderAttributesUpdate = array('viison_undispatched_tracking_codes' => $newUndispatchedTrackingCodes);
            $orderAttributesTable->update($orderAttributesUpdate, $orderAttributesTableWhere);

        }

        return $newTrackingCodes;
    }

    /**
     * First parses the given identifier to select the right document type and tracking code.
     * Then tries to find the requested document in the Shopware documents directory
     * and just return it. If it was not found, it uses the document type and tracking code
     * to request the document URL and finally load the file, which is then saved on disk and returned.
     *
     * @param string $identifier A string identifying the document, which shall be returned.
     * @return string A file containing the document.
     * @throws \Exception, if an unknown identifier was passed to this method or a request failed.
     */
    public function getDocument($identifier)
    {
        /**
         * Process the document identifier to determine the document type
         *
         * @var int $splitPos Get the position of LABEL_IDENTIFIER_SEPARATOR
         * @var string $docType Can be Label or Return Label
         * @var string $labelIdentifier The identifier used for Document Creation (Tracking Code, Vaoucher ID ...)
         */
        $splitPos = $this->util->getIdentifierPosition($identifier);
        if ($splitPos !== false) {
            /**
             * @deprecated block, backwards compatibility
             */
            $docType = substr($identifier, 0, $splitPos);
            $labelIdentifier = substr($identifier, ($splitPos + 1));

            if (!in_array($docType, array(self::DOCUMENT_TYPE_SHIPPING_LABEL, self::DOCUMENT_TYPE_EXPORT_DOCUMENT)) || strlen($labelIdentifier) == 0) {
                // Unknown document type / invalid tracking code
                $this->util->log('Unknown identifier "' . $identifier . '".');
                throw new \Exception('Unknown identifier "' . $identifier . '".');
            }

            return $this->getDocumentFileContents($docType, $labelIdentifier);
        } else {
            /** @var ModelManager $modelManager */
            $modelManager = Shopware()->Container()->get('models');
            $shippingDocument = $modelManager->getRepository($this->pluginInfo->getShipmentDocumentModelName())->findOneBy(array(
                'guid' => $identifier
            ));

            if (!$shippingDocument) {
                throw new \Exception('Unknown identifier "' . $identifier . '".');
            }

            return $shippingDocument->getDocumentAsPDF();
        }
    }

    /**
     * @param string $documentType
     * @param string $documentIdentifier
     * @return string
     */
    public function getDocumentFileContents($documentType, $documentIdentifier)
    {
        /** @var FileStorage $fileStorageService */
        $fileStorageService = Shopware()->Container()->get('viison_common.document_file_storage_service');

        // Try to read the document from the file system
        $fileName = $this->util->getDocumentFileName($documentIdentifier, $documentType);
        if ($fileStorageService->doesFileExist($fileName)) {
            return $fileStorageService->readFileContents($fileName);
        }

        // PDF not found locally, load it from the dispatch service provider if possible. Please note that this fallback
        // is "deprecated" because every provider will be downloading the documents in the future. This method was used
        // for the batch creation (where the document is already created) and for DHL to download the document after
        // the creation.
        return $this->loadDocumentFromDispatchServiceProvider($documentType, $documentIdentifier, $fileName);
    }

    /**
     * @deprecated
     * Tries to download all documents that belong to the given identifier
     *
     * @param string $labelIdentifier The identifier used in the process of the label creation
     */
    public function downloadDocuments($labelIdentifier)
    {
        /** @var \Zend_Db_Table $orderTable Order table of the specific Plugin */
        $orderTable = new \Zend_Db_Table($this->pluginInfo->getOrderTableName());
        /** @var array $order Info for the specific order */
        $order = $orderTable->fetchRow(
            $orderTable->select()
                ->from($orderTable, array('exportDocumentUrl', 'returnShipment'))
                ->where('url LIKE ?', '%' . $labelIdentifier)
                // This is a workaround to prevent the mysql automatic conversion from string to int.
                // The issue is explained here:
                // https://github.com/VIISON/ShopwareShippingCommon/issues/85#issuecomment-347192768
                // This whole logic needs to be refactored but because it is legacy,
                // it will be done with the new ShippingCommon
                ->orWhere(new \Zend_Db_Expr('CONCAT(\'A\', id) = ?'), 'A' . $labelIdentifier)
        );

        $this->getDocumentsFromOrder($order, $labelIdentifier);
    }

    /**
     * @deprecated
     * Tries to download all documents that belong to the given $order
     *
     * @param array $order
     * @param null $identifierForLabel
     * @throws \Exception
     */
    private function getDocumentsFromOrder($order, $identifierForLabel = null)
    {
        $identifiers = array();
        $labelIdentifier = $identifierForLabel ?: $this->util->getIdentifierFromLabelUrl($order->url);

        if ($order->returnShipment) {
            $identifiers[] = static::DOCUMENT_TYPE_RETURN_LABEL . PluginInfo::LABEL_IDENTIFIER_SEPARATOR . $labelIdentifier;
        } else {
            $identifiers[] = static::DOCUMENT_TYPE_SHIPPING_LABEL.PluginInfo::LABEL_IDENTIFIER_SEPARATOR.$labelIdentifier;
        }
        if (!empty($order->exportDocumentUrl)) {
            $identifiers[] = static::DOCUMENT_TYPE_EXPORT_DOCUMENT.PluginInfo::LABEL_IDENTIFIER_SEPARATOR.$labelIdentifier;
        }

        /**
         * Try to download all created documents to cache them on disk
         */
        foreach ($identifiers as $identifier) {
            try {
                $this->getDocument($identifier);
            } catch (\Exception $e) {
                // Ignore errors in this case, because although the archiving of these PDFs is desired,
                // there is also a fallback mechanism to download the files when needed
            }
        }
    }

    /**
     * First checks if the shipment weight calculation is activated in the configuration of the implementing plugin
     * and returns the configured default weight if not. Otherwise calculates the weight of a shipment
     * containing all items of the order with the given id. That is, the weight of all items multiplied
     * with their quantities is summed up and multiplied with the factor for filling material.
     * Finally the fixed surcharge for other packaging material is added and the total weight
     * in kilograms is returned.
     *
     * @param int $orderId The id of the order whose package weight should be calculated.
     * @param array $orderDetails An optional array containing the ids and quantities of all order detail items,
     *        whose weight multiplied with the quantity shall be included in the calculated weight.
     * @return float The calculated package weight or 0, if the order doesn't contain any items whose weight is set.
     */
    public function determineShipmentWeight($orderId, $orderDetails = null)
    {
        $shopId = $this->util->originatingShop($orderId);

        // Determine the default weight based on the product of the order and the configuration
        $countryId = Shopware()->Db()->fetchOne(
            'SELECT countryID
             FROM s_order_shippingaddress
             WHERE orderID = ?',
            array(
                $orderId
            )
        );

        // Get the default weight or use a fallback value of 1 kilogram, which won't be used for label creation,
        // because the product will be checked again later and thus cause error.
        $defaultWeight = 1.0;
        if ($countryId !== false) {
            // Build configuration key for extracting the right default weight
            $product = $this->util->getProduct($orderId);
            if ($product !== null) {
                $defaultWeight = floatval($this->util->getDefaultShipmentWeight($shopId, $product));
            } else {
                $defaultWeight = floatval($this->util->getDefaultShipmentWeightWithoutProduct($shopId));
            }
        }

        // Check if the calculation is activated
        $weightShouldBeCalculated = $this->util->config($shopId, 'calculateWeight');
        if (!$weightShouldBeCalculated) {
            return $defaultWeight;
        }

        $dispatchOrderPositions = Shopware()->Container()->get('viison_shipping_common.dispatch_data')->getDispatchOrderPositions($orderId, $this->pluginInfo);

        // Calculate the total weight
        $weight = 0.0;
        foreach ($dispatchOrderPositions as $row) {
            $detailId = $row['id'];
            if ($orderDetails !== null && $orderDetails[$detailId] === null) {
                // Current item shall not be contained in the result
                continue;
            }

            // Add the weight of the current item
            $quantity = ($orderDetails !== null) ? $orderDetails[$detailId] : $row['quantity'];
            $itemUnitWeight = floatval($row['weight']);
            if ($itemUnitWeight == 0) {
                // If no weight is assigned, assume a weight of 0.1kg
                $itemUnitWeight = 0.1;
            }

            $weight += $itemUnitWeight * floatval($quantity);
        }

        // Calculate total package weight rounded up to 3 decimal places
        $weight *= 1.0 + (floatval($this->util->config($shopId, 'fillerSurcharge')) / 100.0);
        $weight += floatval($this->util->config($shopId, 'packagingSurcharge'));
        $weight = round($weight, 3);

        $weight = $this->applyMinimumWeight($weight);

        return $weight;
    }

    /**
     * Applies a minimum weight
     * Moved into own function so all adapters can overwrite it with their custom minimum weight
     *
     * @param float $weight
     * @return float
     */
    protected function applyMinimumWeight($weight)
    {
        // Weight must be at least 0.1kg for backwards compatibility (used for export documents)
        return max($weight, 0.1);
    }

    /**
     * Determines whether in order to create shipping labels for the order with the given id
     * the package dimensions (length, width and height) are required.
     *
     * @param int $orderId The id of the order, for which the requirements shall be checked.
     * @return boolean True, if the package dimensions are required. Otherwise false.
     */
    public function packageDimensionsRequired($orderId)
    {
        $product = $this->util->getProduct($orderId);

        return ($product !== null && $product['packageDimensionsRequired']);
    }

    /**
     * @deprecated Can't be removed as long the old Intraship Plugin is in use.
     *
     * Determines all options available for the shipping product with the given ID and
     * returns them in an array.
     *
     * Note: By default this returns the option 'createExportDocument' to get the same
     * behaviour as before making the product options configurable. That is, previously
     * 'getAvailableOptions()' of \Shopware\Plugins\ViisonShippingCommon\Classes\ShippingProduct always
     * returned an array containing 'createExportDocument' (see
     * https://github.com/VIISON/ShopwareShippingCommon/blob/2d4757758a9a9f2ff1144589d4f5f37b6801a73b/Classes/ShippingProduct.php#L43-L47).
     * This default must be removed as soon as all other shipping plugins using ShippingCommon
     * override this method and return their own options.
     *
     * @param int $productId
     * @return array
     */
    public function getProductOptions($productId)
    {
        return array(
            'createExportDocument'
        );
    }
}
