<?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\Plugins\ViisonCommon\Classes\Util\Util as ViisonCommonUtil;

/**
 * TODO refector Zend table to Doctrine with DHL, becasue DHL can be used to test this feature
 * An implementation of the the daily closing generator interface that generates the daily closing PDF locally without
 * using a webservice and indepentently of the respective dispatch service provider.
 */
class LocalDailyClosingGenerator extends DailyClosingGenerator
{
    /**
     * @var PluginInfo $pluginInfo
     */
    private $pluginInfo;

    public function __construct(PluginInfo $pluginInfo)
    {
        $this->pluginInfo = $pluginInfo;
    }

    /**
     * Creates a daily closing document and returns the created PDF.
     *
     * @param int|null $shopId The id of the subshop, for which the daily closing should be generated or null if the daily closing should be created for all subshops.
     * @param string $fromDate The first day of the date range, whose shipments will be included in manifest.
     * @param string $toDate The last day of the date range, whose shipments will be included in manifest.
     * @return string The raw data of the manifest PDF file.
     */
    public function getDailyClosing($shopId, $fromDate, $toDate)
    {
        return $this->renderDailyClosingData($shopId, $fromDate, $toDate, false);
    }

    /**
     * {@Inheritdoc}
     * @return string
     */
    public function getDailyClosingForNewLabels($shopId, $fromDate = null, $toDate = null)
    {
        return $this->renderDailyClosingData($shopId, $fromDate, $toDate, true);
    }

    /**
     * Generates the daily closing data for a given time span and subshop Id.
     *
     * @param int|null $shopId The id of the subshop, for which the daily closing
     * should be generated or null if the daily closing should be created for all subshops.
     * @param \DateTime|null $fromDate
     * @param \DateTime|null $toDate
     * @param bool $onlyNewLabels
     * @return array
     */
    private function getDailyClosingData($shopId, $fromDate, $toDate, $onlyNewLabels)
    {
        $select = Shopware()->Db()->select()
            ->from(array('o' => $this->pluginInfo->getOrderTableName()), array(
                'id AS labelId',
                'orderId', // Required to distinguish labels with and without order
                'trackingCode',
                'DATE_FORMAT(created, \'%d.%m.%Y %H:%i\') AS dateTime',
                'customerAddress',
                'weight'
            ))
            ->join(array('p' => $this->pluginInfo->getProductTableName()), 'o.productId = p.id', array(
                'name AS productName'
            ))
            ->joinLeft(array('s' => 's_order_shippingaddress'), 'o.orderId = s.orderId', array(
                'firstname',
                'lastname',
                'company',
                'street',
                'zipcode',
                'city'
            ))
            ->joinLeft(array('c' => 's_core_countries'), 's.countryId = c.id', array(
                'countryName'
            ))
            ->joinLeft(array('order' => 's_order'), 'o.orderId = order.id');
        $select->where('o.returnShipment = 0');

        if ($onlyNewLabels) {
            $select->where('o.containedInDailyClosing = 0');
        }
        if ($fromDate !== null) {
            $select->where('o.created >= ?', $fromDate->format('Y-m-d H:i:s'));
        }
        if ($toDate !== null) {
            $select->where('o.created < ?', $toDate->format('Y-m-d H:i:s'));
        }

        // Count labels without order towards default shop
        if ($shopId == $this->defaultShop()) {
            $select->where('order.language = ? OR o.orderId IS NULL', array($shopId));
        } elseif (!is_null($shopId)) {
            $select->where('order.language = ?', array($shopId));
        }

        $select->order('o.created DESC');

        $result = Shopware()->Db()->query($select)->fetchAll();

        $data = array();
        foreach ($result as $row) {
            if (!is_null($row['customerAddress'])) {
                list($receiver, $address) = preg_split('/, /', $row['customerAddress'], 2);
                // Remove salutation
                $splitted = preg_split('/ /', $receiver, 2);
                $receiver = $splitted[1];
            } elseif (!is_null($row['orderId'])) {
                // Use shipping address as fallback for labels with order (in old ShippingCommon versions, the actually used address was not saved)
                $receiver = $row['firstname'] . ' ' . $row['lastname'];
                $address = join(', ', array_filter(array(
                    $row['company'],
                    $row['street'],
                    $row['zipcode'],
                    $row['city'],
                    $row['countryName']
                )));
            }
            if ($this->pluginInfo->getShipmentDocumentModelName() !== null) {
                // assume stored date is local time (new shippingCommon logic)
                $created = new \DateTime($row['dateTime']);
            } else {
                // assume stored date is utc
                $created = new \DateTime($row['dateTime'], new \DateTimeZone('UTC'));
            }
            $data[] = array(
                'labelId' => $row['labelId'],
                'receiver' => $receiver,
                'dateTime' => date('d.m.Y H:i', $created->getTimestamp()),
                'trackingCode' => $row['trackingCode'],
                'product' => $row['productName'],
                'address' => $address,
                'weight' => $row['weight']
            );
        }

        return $data;
    }

    /**
     * @param int|null $shopId
     * @param string|null $fromDate
     * @param string|null $toDate
     * @param bool $onlyNewLabels
     * @return string
     * @throws \Exception
     * @throws \Zend_Db_Adapter_Exception
     */
    private function renderDailyClosingData($shopId, $fromDate, $toDate, $onlyNewLabels)
    {
        if ($fromDate !== null) {
            $fromDate = \DateTime::createFromFormat('Y-m-d H:i:s', $fromDate);
        }
        if ($toDate !== null) {
            $toDate = \DateTime::createFromFormat('Y-m-d H:i:s', $toDate);
        }

        $now = new \DateTime();
        $dailyClosingData = $this->getDailyClosingData($shopId, $fromDate, $toDate, $onlyNewLabels);

        // Determine from and to dates from the data in the daily closing if all new labels are used
        if ($onlyNewLabels) {
            $dates = array_map(function ($row) {
                return \DateTime::createFromFormat('d.m.Y H:i', $row['dateTime']);
            }, $dailyClosingData);
            if (count($dates) === 0) {
                $fromDate = new \DateTime();
                $toDate = new \DateTime();
            } else {
                $fromDate = min($dates);
                $toDate = max($dates);
            }
        }

        $templateEngine = clone Shopware()->Template();
        $template = $templateEngine->createTemplate(__DIR__ . '/../Documents/dailyClosing.tpl');
        // Extract the display name of the dispatch service provider from the plugin name, e.g. "DHL Versand" -> "DHL"
        $template->assign('dispatchServiceProviderName', str_replace(' Versand', '', $this->pluginInfo->getPluginDisplayName()));
        $wholeDays = $fromDate->format('H:i') === '00:00' && $toDate->format('H:i') === '00:00';
        $template->assign('wholeDays', $wholeDays);
        if ($wholeDays) {
            $template->assign('fromDate', $fromDate->format('d.m.Y'));
            $template->assign('toDate', $toDate->sub(new \DateInterval('P1D'))->format('d.m.Y'));
        } else {
            $template->assign('fromDate', $fromDate->format('d.m.Y H:i:s'));
            $template->assign('toDate', $toDate->format('d.m.Y H:i:s'));
        }

        $shopName = null;
        if (!is_null($shopId)) {
            $config = Shopware()->Config();
            $config->setShop(
                Shopware()->Container()->get('models')->getRepository('Shopware\Models\Shop\Shop')->find($shopId)
            );
            $shopName = $config->get('ShopName');
        }
        $template->assign('shopName', $shopName);
        $template->assign('dailyClosingData', $dailyClosingData);
        $template->assign('creationTime', $now->format('d.m.Y H:i'));
        $template->assign('numberOfParcels', count($dailyClosingData));
        $template->assign('totalWeight', array_reduce($dailyClosingData, function ($sum, $item) {
            return $sum + $item['weight'];
        }, 0));
        $html = $template->fetch();

        $dompdf = ViisonCommonUtil::createDompdfInstance();
        $dompdf->loadHtml($html);
        $dompdf->render();
        $canvas = $dompdf->get_canvas();
        $font = $dompdf->getFontMetrics()->get_font("helvetica", "bold");
        $canvas->page_text(522, 770, "Seite {PAGE_NUM} / {PAGE_COUNT}", $font, 10, array(0, 0, 0));
        $pdf = $dompdf->output();

        if (count($dailyClosingData) > 0) {
            // Mark all returned labels as contained in a daily closing
            Shopware()->Db()->update($this->pluginInfo->getOrderTableName(), array(
                'containedInDailyClosing' => 1
            ), 'id IN (' . join(',', array_map(function ($row) {
                return $row['labelId'];
            }, $dailyClosingData)) . ')');
        }

        return $pdf;
    }

    private function defaultShop()
    {
        $shopTable = new \Zend_Db_Table('s_core_shops');
        $shop = $shopTable->fetchRow($shopTable->select()->from($shopTable, 'id')->where('`default` = TRUE')); // default is a reserved word in MySQL

        if (empty($shop)) {
           return null;
        }

        return $shop->id;
    }

}
