<?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\ModelRepository;
use Shopware\Plugins\ViisonDHL\Classes\DhlRetoureApi\DhlRetoureApi;
use Shopware\Plugins\ViisonDHL\Classes\DhlRetoureApi\DhlRetoureException;
use Shopware\Plugins\ViisonDHL\Classes\DhlRetoureApi\DhlRetoureReturnOrder;
use Shopware\Plugins\ViisonDHL\Components\ViisonDHLExportDocument;
use Shopware\Plugins\ViisonDHL\Util;
use Shopware\Plugins\ViisonShippingCommon\Classes\Types\LabelFile;
use Shopware\Plugins\ViisonShippingCommon\Components\AddressFactory;
use Shopware\Plugins\ViisonShippingCommon\Components\LabelPersister;

/**
 * Class OnlineRetoureCommunication
 *
 * This class provides the communication with the DHL Retoure web service. Therefore
 * it has a method to create new return labels for an order.
 *
 * @package Shopware\Plugins\ViisonDHL\Classes
 */
class OnlineRetoureCommunication
{
    /**
     * Util class
     * @var Util
     */
    private $util;

    public function __construct()
    {
        $this->util = Util::instance();
    }

    public static function getRetoureCountries()
    {
        return [
            'de',
            'at',
            'ch',
            'be',
            'bg',
            'cy',
            'cz',
            'dk',
            'ee',
            'es',
            'fi',
            'fr',
            'gb',
            'gr',
            'hu',
            'hr',
            'ie',
            'it',
            'lt',
            'lu',
            'lv',
            'nl',
            'pl',
            'pt',
            'ro',
            'se',
            'si',
            'sk',
        ];
    }

    /**
     * Creates a new DHL return label for a given order or shippingDetails (free form label). Export document items are
     * included automatically when required. Request logging can be enabled and will log the full request and response.
     *
     * @param int $orderId The id of the order, for which a new return label shall be created.
     * @param float|null $weight
     * @param array|null $shippingDetails
     * @return array An array containing the success status and additional information based on the operation's success.
     * @throws DHLException
     * @throws DhlRetoureException
     */
    public function createLabel($orderId, $weight = null, $shippingDetails = null)
    {
        $sender = $shippingDetails ? $shippingDetails : $this->util->getOrder($orderId);

        if ($orderId === null && $sender['countryiso'] === 'CH') {
            throw DhlRetoureException::freeFormLabelNotSupportedForSwissCustomers();
        }

        $country = Shopware()->Models()->getRepository('Shopware\\Models\\Country\\Country')->findOneBy(['iso' => $sender['countryiso']]);
        $shopId = $this->util->originatingShop($orderId);

        try {
            $portalName = $this->util->config($shopId, 'onlineRetourePortalName' . mb_strtoupper($country->getIso()));
        } catch (\InvalidArgumentException $e) {
            throw DhlRetoureException::unsupportedCountry($country->getName());
        }
        if (empty($portalName)) {
            throw DhlRetoureException::receiverIdForCountryNotConfigured($country->getName());
        }

        $orderNumber = Shopware()->Db()->fetchOne(
            'SELECT ordernumber
            FROM s_order
            WHERE id = ?',
            [
                $orderId,
            ]
        );
        $phoneNumber = $this->util->config($shopId, 'phoneNumber');
        $email = $this->util->config($shopId, 'email');

        /** @var AddressFactory $addressFactory */
        $addressFactory = Shopware()->Container()->get('viison_shipping_common.address_factory');
        $senderAddress = $addressFactory->getAddressFromAddressArray($sender);

        $dhlRetoureReturnOrder = new DhlRetoureReturnOrder(
            $senderAddress,
            $portalName
        );
        if ($email) {
            $dhlRetoureReturnOrder->setEmail($email);
        }
        if ($phoneNumber) {
            $dhlRetoureReturnOrder->setTelephoneNumber($phoneNumber);
        }
        if ($orderNumber) {
            $dhlRetoureReturnOrder->setCustomerReference($orderNumber);
        }
        if ($weight) {
            $dhlRetoureReturnOrder->setShipmentWeightInKg($weight);
        }

        // Fetch the order positions to be included in the customs declaration
        $exportDocumentItems = [];
        if ($orderId) {
            /** @var ViisonDHLExportDocument $dhlExportDocumentService */
            $dhlExportDocumentService = Shopware()->Container()->get('viison_dhl.export_document');
            $exportDocumentItems = $dhlExportDocumentService->getExportDocumentItems($orderId);

            // The DHL Retoure API only supports EUR, GBP and CHF as currency, convert other currencies.
            $exportDocumentItems = $dhlExportDocumentService->convertExportDocumentItemPrices(
                $exportDocumentItems,
                [
                    'EUR',
                    'GBP',
                    'CHF',
                ],
                'EUR'
            );

            $exportDocumentItems = $dhlExportDocumentService->condenseExportDocumentItems(
                $exportDocumentItems,
                DhlRetoureReturnOrder::MAX_EXPORT_POSITIONS
            );

            // The DHL Retoure API requires the ISO-3 code for the country of origin.
            /** @var ModelRepository $countryRepository */
            $countryRepository = Shopware()->Models()->getRepository('Shopware\\Models\\Country\\Country');
            $exportDocumentItems = array_map(function ($exportDocumentItem) use ($countryRepository, $senderAddress) {
                // Convert 2-character ISO to 3-character ISO as DHL supports only the 3-character ISO
                $countryOfOriginIso2 = null;
                if ($exportDocumentItem['countryOfOriginCode']) {
                    $countryOfOriginIso2 = $exportDocumentItem['countryOfOriginCode'];
                } else {
                    // If no country of origin is set, fall back to the country of the sender address
                    $senderCountry = $senderAddress->getCountry();
                    if ($senderCountry) {
                        $countryOfOriginIso2 = mb_strtolower($senderCountry->getIso());
                    }
                }
                if ($countryOfOriginIso2 === null) {
                    $exportDocumentItem['countryOfOriginCode3'] = null;

                    return $exportDocumentItem;
                }

                $countryOfOrigin = $countryRepository->findOneBy(['iso' => $countryOfOriginIso2]);
                $exportDocumentItem['countryOfOriginCode3'] = $countryOfOrigin->getIso3();

                return $exportDocumentItem;
            }, $exportDocumentItems);
        }

        if (count($exportDocumentItems) > 0) {
            $dhlRetoureReturnOrder->setExportDocumentItems($exportDocumentItems);
        }

        $invoice = Shopware()->Db()->fetchRow(
            'SELECT date, docId AS number
            FROM s_order_documents
            WHERE orderID = ?
            AND type = 1',
            [
                $orderId
            ]
        );

        if (!empty($invoice)) {
            $dhlRetoureReturnOrder->setInvoice($invoice);
        }

        $username = $this->util->config($shopId, 'username');
        $password = $this->util->config($shopId, 'password');
        if (!$username || !$password) {
            throw DHLException::gkpCredentialsMissing();
        }

        // Create a configured dhl retoure api client
        $useTestingWebservice = $this->util->config($shopId, 'useTestingWebservice');
        $httpLogger = Shopware()->Container()->get('viison_dhl.http_logger');
        if ($useTestingWebservice) {
            $dhlRetoureApi = DhlRetoureApi::createForTestEndpoint($username, $password, $httpLogger);
        } else {
            $dhlRetoureApi = DhlRetoureApi::createForProductionEndpoint($username, $password, $httpLogger);
        }

        $dhlRetoureReturnOrderResponse = $dhlRetoureApi->createReturnOrder($dhlRetoureReturnOrder);

        // Save the label PDF
        $trackingCode = $dhlRetoureReturnOrderResponse['shipmentNumber'];
        $documentFileName = $this->util->getDocumentFileName($trackingCode, ShippingUtil::DOCUMENT_TYPE_RETURN_LABEL);
        $pdf = base64_decode($dhlRetoureReturnOrderResponse['labelData']);

        $dhlLabelFile = new LabelFile($pdf, $documentFileName, $trackingCode);

        /** @var LabelPersister $dhlLabelPersister */
        $dhlLabelPersister = Shopware()->Container()->get('viison_dhl.label_persister');
        $dhlLabelPersister->persistLabelAndArchiveOldLabel($dhlLabelFile);

        $shippingUtil = new ShippingUtil();
        $shippingUtil->saveTrackingCode($orderId, ShippingUtil::DOCUMENT_TYPE_RETURN_LABEL, $trackingCode, $shippingDetails, null, $weight);

        return $trackingCode;
    }

    /**
     * Returns if it possible to generate a return label from the given country.
     *
     * @param $orderId
     * @return bool True, if it is possible to generate a return label from the given country and a portal name is given in the configuration.
     */
    public function canCreateLabel($orderId)
    {
        $shopId = $this->util->originatingShop($orderId);
        $order = $this->util->getOrder($orderId);

        // First check if the general return configuration exists
        $username = $this->util->config($shopId, 'username');
        $password = $this->util->config($shopId, 'password');
        if (!$username || !$password) {
            return false;
        }

        // Check if a portal name for the country exists
        try {
            $portalName = $this->util->config($shopId, 'onlineRetourePortalName' . mb_strtoupper($order['countryiso']));
        } catch (\InvalidArgumentException $e) {
            // Country not supported at all
            return false;
        }
        if (empty($portalName)) {
            // Portal name not set in configuration
            return false;
        }

        return true;
    }
}
