<?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\Components\Export;

use Enlight_Components_Db_Adapter_Pdo_Mysql;
use Shopware\Components\Model\ModelManager;
use Shopware\Models\Order\Order;
use Shopware\Plugins\ViisonShippingCommon\Components\ViisonShippingCommonDispatchData;
use Shopware_Components_Config;

/**
 * Class ExportInformationService
 */
class InformationService
{
    const INVOICE_DOCUMENT_ID = 1;

    /**
     * @var Enlight_Components_Db_Adapter_Pdo_Mysql
     */
    private $db;

    /**
     * @var ModelManager
     */
    private $entityManager;

    /**
     * @var ViisonShippingCommonDispatchData
     */
    private $dispatchDataService;

    /**
     * @var Shopware_Components_Config $shopConfig
     */
    private $shopConfig;

    /**
     * @var FakePluginInfo
     */
    private $fakeAdapterInfo;

    /**
     * @param Enlight_Components_Db_Adapter_Pdo_Mysql $db
     * @param ModelManager $entityManager
     * @param Shopware_Components_Config $shopConfig
     * @param ViisonShippingCommonDispatchData $dispatchData
     */
    public function __construct(
        Enlight_Components_Db_Adapter_Pdo_Mysql $db,
        ModelManager $entityManager,
        Shopware_Components_Config $shopConfig,
        ViisonShippingCommonDispatchData $dispatchData
    ) {
        $this->db = $db;
        $this->entityManager = $entityManager;
        $this->dispatchDataService = $dispatchData;
        // Is necessary because the ViisonShippingCommonDispatchProvider expects the class PluginInfo as type hint
        $this->fakeAdapterInfo = new FakePluginInfo();
    }

    /**
     * @param Order $order
     * @param null|float $shipmentWeight The positions weight will be set via the shipment weight.
     * @param array $positionFilter The picked positions example if picked via the picking app.
     * @return Information
     */
    public function createExportInformationFromOrder(Order $order, $shipmentWeight = null, array $positionFilter = array())
    {
        $dispatchOrderPositions = $this->dispatchDataService->getDispatchOrderPositions(
            $order->getId(),
            $this->fakeAdapterInfo
        );

        // Set the position weight via the shipment weight
        $multiplicationFactor = 1;
        if ($shipmentWeight) {
            $multiplicationFactor = $this->calculateWeightFactorForPositionsViaShipmentWeight(
                $dispatchOrderPositions,
                $shipmentWeight
            );
        }

        // Case: Picking app, the $pickedPositions have the format ID => Quantity
        if ($positionFilter && count($positionFilter) > 0) {
            $dispatchOrderPositions = $this->filterPickedPositionsFromDispatchOrderPositions(
                $positionFilter,
                $dispatchOrderPositions
            );
        }

        $exportPositions = [];
        foreach ($dispatchOrderPositions as $dispatchPosition) {
            $unitWeight = floatval($dispatchPosition['weight']) * $multiplicationFactor;

            $exportPosition = new Position();
            $exportPosition->setName($dispatchPosition['name'])
                ->setArticleOrderNumber($dispatchPosition['articleordernumber'])
                ->setQuantity($dispatchPosition['quantity'])
                ->setNetWeightInKg($unitWeight)
                ->setNetPrice(floatval($dispatchPosition['price']))
                ->setCountryIsoOfOrigin($dispatchPosition['countryOfOriginCode'])
                ->setTariffNumber($dispatchPosition['customsTariffNumber'] ?: '');

            $exportPositions[] = $exportPosition;
        }

        /** @var \Shopware\Models\Order\Document\Document $invoiceDocument */
        $invoiceNumber = $this->getInvoiceNumber(
            $order->getDocuments(),
            $order->getId()
        );

        $exportInformation = new Information();
        $exportInformation->setCurrency($order->getCurrency())
            ->setInvoiceNumber($invoiceNumber)
            ->setPositions($exportPositions)
            ->setOrderNumber($order->getNumber())
            ->setVatNumber($this->shopConfig->taxNumber)
            ->setInvoiceShippingAmount($order->getInvoiceShipping())
            ->setNetInvoiceAmount($order->getInvoiceAmountNet());

        return $exportInformation;
    }

    /**
     * @param string $positionName
     * @param float $shipmentWeight
     * @param float $positionPrice
     * @param string $currency
     * @return Information
     */
    public function createExportInformationWithSinglePosition($positionName, $shipmentWeight, $positionPrice, $currency)
    {
        $exportPosition = new Position();
        $exportPosition->setName($positionName)
            ->setQuantity(1)
            ->setNetWeightInKg($shipmentWeight)
            ->setNetPrice($positionPrice);

        $exportInformation = new Information();
        $exportInformation->setCurrency($currency)
            ->setVatNumber($this->shopConfig->taxNumber)
            ->addPosition($exportPosition);

        return $exportInformation;
    }

    /**
     * @param array $pickedPositions
     * @param array $dispatchOrderPositions
     * @return array
     */
    private function filterPickedPositionsFromDispatchOrderPositions($pickedPositions, $dispatchOrderPositions)
    {
        return array_filter(
            $dispatchOrderPositions,
            function ($dispatchOrderPosition) use ($pickedPositions) {
                $positionId = $dispatchOrderPosition['id'];
                if (array_key_exists($positionId, $pickedPositions)) {
                    // Update quantity, because the user can pick a different amount
                    $dispatchOrderPosition['quantity'] = $pickedPositions[$positionId];

                    return true;
                }
            }
        );
    }

    /**
     * @param array $dispatchOrderPositions
     * @param float $shipmentWeight
     */
    private function calculateWeightFactorForPositionsViaShipmentWeight($dispatchOrderPositions, $shipmentWeight)
    {
        $totalShipmentWeight = array_reduce(
            $dispatchOrderPositions,
            function ($shipmentWeight, $dispatchPosition) {
                return $shipmentWeight + $dispatchPosition['weight'];
            },
            0
        );

        // We shouldn't break the label creation process, if the $totalShipmentWeight is 0,
        // because of that we are returning 1
        return ($totalShipmentWeight) ? $shipmentWeight / $totalShipmentWeight : 1;
    }

    /**
     * @param \Shopware\Models\Order\Document\Document[] $orderDocuments
     * @param int $orderId
     * @return string
     */
    private function getInvoiceNumber($orderDocuments, $orderId)
    {
        $invoiceDocument = $orderDocuments->filter(
            function ($document) use ($orderId) {
                /** @var \Shopware\Models\Order\Document\Document $document */
                return $document->getOrderId() === $orderId && $document->getId() === self::INVOICE_DOCUMENT_ID;
            }
        );

        return ($invoiceDocument && $invoiceDocument->count()) ? $invoiceDocument->first()->getDocumentId() : '';
    }
}
