<?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\DhlRetoureApi;

use Shopware\Plugins\ViisonShippingCommon\Classes\Types\Address;
use Shopware\Plugins\ViisonShippingCommon\Classes\Types\Weight;

class DhlRetoureReturnOrder
{
    const ADDRESS_NAME_MAX_LENGTH = 35;
    const ADDRESS_STREET_MAX_LENGTH = 35;
    const ADDRESS_HOUSE_NUMBER_MAX_LENGTH = 10;
    const ADDRESS_ZIP_MAX_LENGTH = 10;
    const ADDRESS_CITY_MAX_LENGTH = 35;
    const CUSTOMER_REFERENCE_MAX_LENGTH = 30;
    const EMAIL_MAX_LENGTH = 70;
    const TELEPHONE_NUMBER_MAX_LENGTH = 35;
    const EXPORT_ITEM_DESCRIPTION_MAX_LENGTH = 50;

    const MAX_EXPORT_POSITIONS = 5;

    const COUNTRIES_WITH_EXPORT_DOCUMENTS = [
        'CH',
        'GB',
    ];

    /**
     * @var array
     */
    private $senderAddress;

    /**
     * @var string
     */
    private $receiverId;

    /**
     * @var array
     */
    private $exportDocumentItems = [];

    /**
     * @var string|null
     */
    private $email = null;

    /**
     * @var string|null
     */
    private $telephoneNumber = null;

    /**
     * @var string|null
     */
    private $customerReference = null;

    /**
     * @var float|null
     */
    private $shipmentWeightInKg = null;

    /**
     * @var array|null
     */
    private $invoice = null;

    /**
     * @param Address $senderAddress
     * @param string $receiverId The receiverId corresponds to an address stored in the DHL Retoure Portal
     */
    public function __construct(
        Address $senderAddress,
        $receiverId
    ) {
        $this->senderAddress = $senderAddress;
        $this->receiverId = $receiverId;
    }

    /**
     * @return array
     */
    public function getRequestBody()
    {
        $body = [
            'receiverId' => $this->receiverId,
            'senderAddress' => $this->getSenderAddress(),
            'returnDocumentType' => 'SHIPMENT_LABEL',
        ];

        if ($this->email) {
            $body['email'] = mb_substr($this->email, 0, self::EMAIL_MAX_LENGTH, 'utf-8');
        }
        if ($this->telephoneNumber) {
            $body['telephoneNumber'] = mb_substr($this->telephoneNumber, 0, self::TELEPHONE_NUMBER_MAX_LENGTH, 'utf-8');
        }
        if ($this->customerReference) {
            $body['customerReference'] = mb_substr($this->customerReference, 0, self::CUSTOMER_REFERENCE_MAX_LENGTH, 'utf-8');
        }

        return array_merge($body, $this->getShipmentExportInfoArray());
    }

    /**
     * @return array
     */
    private function getShipmentExportInfoArray()
    {
        $shipmentValue = round(array_sum(array_map(function ($exportDocumentItem) {
            return $exportDocumentItem['price'] * $exportDocumentItem['quantity'];
        }, $this->exportDocumentItems)), 2);

        $shipmentWeight = new Weight($this->shipmentWeightInKg ?: 0, 'kg');
        $exportInfo = [
            'value' => $shipmentValue,
            'weightInGrams' => (int) max($shipmentWeight->convertTo('g'), 1),
        ];

        // Only add export positions for non eu countries
        if (count($this->exportDocumentItems) > 0
            && in_array($this->senderAddress->getCountry()->getIso(), self::COUNTRIES_WITH_EXPORT_DOCUMENTS, true)) {
            $exportInfo['customsDocument'] = [
                'currency' => $this->exportDocumentItems[0]['currency'] ?: 'EUR',
                'positions' => $this->getExportPositions(),
            ];

            if ($this->customerReference) {
                $exportInfo['customsDocument']['originalShipmentNumber'] = $this->customerReference;
            }

            if ($this->invoice) {
                $exportInfo['customsDocument']['originalInvoiceNumber'] = $this->invoice['number'];
                $exportInfo['customsDocument']['originalInvoiceDate'] = $this->invoice['date'];
            }
        }

        return $exportInfo;
    }

    /**
     * @return array
     */
    private function getExportPositions()
    {
        return array_map(function ($exportDocumentItem) {
            return $this->getExportPosition($exportDocumentItem);
        }, $this->exportDocumentItems);
    }

    /**
     * @param array $exportDocumentItem
     * @return array
     */
    private function getExportPosition(array $exportDocumentItem)
    {
        $weightSumInKg = $exportDocumentItem['weight'] * $exportDocumentItem['quantity'];
        $weight = new Weight($weightSumInKg, 'kg');

        return [
            'positionDescription' => mb_substr($exportDocumentItem['name'], 0, self::EXPORT_ITEM_DESCRIPTION_MAX_LENGTH, 'utf-8'),
            'count' => (int) $exportDocumentItem['quantity'],
            'weightInGrams' => (int) max($weight->convertTo('g'), 1),
            'values' => round($exportDocumentItem['price'] * $exportDocumentItem['quantity'], 2),
            'originCountry' => $exportDocumentItem['countryOfOriginCode3'],
            'tarifNumber' => $exportDocumentItem['customsTariffNumber'] ?: '',
            'articleReference' => $exportDocumentItem['articleordernumber'],
        ];
    }

    /**
     * @return array
     */
    private function getSenderAddress()
    {
        $name = $this->senderAddress->getFirstName() . ' ' . $this->senderAddress->getLastName();
        if (mb_strlen($name) > self::ADDRESS_NAME_MAX_LENGTH) {
            // Use only the last name
            $name = mb_substr($this->senderAddress->getLastName(), 0, self::ADDRESS_NAME_MAX_LENGTH, 'utf-8');
        }

        $additionalInfo = implode(', ', array_filter([
            $this->senderAddress->getCompany(),
            $this->senderAddress->getDepartment(),
            $this->senderAddress->getAdditionOne(),
        ]));

        $additionalInfo = mb_substr($additionalInfo, 0, self::ADDRESS_NAME_MAX_LENGTH, 'utf-8');
        $secondName = $additionalInfo;

        if (!empty($this->senderAddress->getCompany())) {
            // Use the company as the main name
            $secondName = $name;
            $name = $additionalInfo;
        }

        return [
            'name1' => $name,
            'name2' => $secondName,
            'streetName' => mb_substr($this->senderAddress->getStreetName(), 0, self::ADDRESS_STREET_MAX_LENGTH, 'utf-8'),
            'houseNumber' => mb_substr(
                trim($this->senderAddress->getHouseNumber()),
                0,
                self::ADDRESS_HOUSE_NUMBER_MAX_LENGTH,
                'utf-8'
            ),
            'postCode' => mb_substr($this->senderAddress->getZipCode(), 0, self::ADDRESS_ZIP_MAX_LENGTH, 'utf-8'),
            'city' => mb_substr($this->senderAddress->getCity(), 0, self::ADDRESS_CITY_MAX_LENGTH, 'utf-8'),
            'country' => [
                'countryISOCode' => mb_strtolower($this->senderAddress->getCountry()->getIso3()),
                'state' => $this->senderAddress->getState() ? $this->senderAddress->getState()->getShortCode() : '',
            ],
        ];
    }

    /**
     * @param array $exportDocumentItems
     */
    public function setExportDocumentItems(array $exportDocumentItems)
    {
        $this->exportDocumentItems = $exportDocumentItems;
    }

    /**
     * @param string $email
     */
    public function setEmail($email)
    {
        $this->email = $email;
    }

    /**
     * @param string $telephoneNumber
     */
    public function setTelephoneNumber($telephoneNumber)
    {
        $this->telephoneNumber = $telephoneNumber;
    }

    /**
     * @param string $customerReference
     */
    public function setCustomerReference($customerReference)
    {
        $this->customerReference = $customerReference;
    }

    /**
     * @param float $shipmentWeightInKg
     */
    public function setShipmentWeightInKg($shipmentWeightInKg)
    {
        $this->shipmentWeightInKg = $shipmentWeightInKg;
    }

    /**
     * @param array $invoice
     */
    public function setInvoice($invoice)
    {
        $this->invoice = $invoice;
    }
}
