<?php

namespace YSquare\GiftCert\Model;

use OxidEsales\Eshop\Core\Registry;
use OxidEdales\Eshop\Application\Model\Article;
use YSquare\GiftCert\Core\GiftCertHandler;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;

class Order extends Order_parent
{


    /**
     * Order gift certificates marked as used
     *
     * @var array
     */
    protected $_aGiftcertList = null;

    /**
     * @var array
     */
    protected $_aGiftcertLogList = null;

    /**
     * @var array
     */
    protected $_aGiftcertToDirectlyMailList = null;


    /****************************************
     * This part handles the redeem process
     ****************************************/

    /**
     * Marks voucher as used (oxvoucher::markAsUsed())
     * and sets them to $this->_aVoucherList.
     *
     * @param oxBasket $oBasket basket object
     * @param oxUser $oUser user object
     */
    protected function _markVouchers($oBasket, $oUser)
    {

        // Do nothing if there is no Basket or no User - cause that makes no sense, right?
        if (!$oBasket || !$oUser) {
            return;
        }

        // Mark the vouchers as normal
        $returnValue = parent::_markVouchers($oBasket, $oUser);

        // Now mark our gift certificates
        $giftcertHandler = oxNew(GiftCertHandler::class);
        $this->_aGiftcertList = $oBasket->getGiftCertList();

        // Mark each voucher as used and add a Log-Entry iff there is no other in this order already (as some payment
        // providers tend to call this function twice)
        if (is_array($this->_aGiftcertList)) {
            foreach ($this->_aGiftcertList as $giftcertId => $oGiftcert) {
                if (!$giftcertHandler->isGiftCertRedeemedInOrder($oGiftcert->ysgiftcert__oxid->value, $this->oxorder__oxid->value)) {
                    $realDiscount = $oBasket->getGiftCertRealDiscount($giftcertId)->getPrice();
                    $oGiftcert->markAsUsed($this->oxorder__oxid->value, $oUser->oxuser__oxid->value, $realDiscount);
                    // TODO: Maybe some storage of Discount to use in Mail later
                } else {
                    $logger = Registry::getLogger();
                    $logger->error('This giftcert is already used: ' . $giftcertId, [__CLASS__, __FUNCTION__]);
                }
            }
        }

        // Finally decrease the pool if it has been used AND if we have not already logged a redeem event
        // (can happen if a payment provider thinks he has to call this function twice or more)
        if (!$giftcertHandler->isPoolRedeemedInOrder($this->oxorder__oxid->value)) {
            $poolMoney = $oBasket->getGiftCertUsedPoolAmount()->getPrice();
            if ($poolMoney > 0) {
                $oUser->subtractYSGiftcertPoolAmount($poolMoney);
                $giftcertHandler->logPoolRedeem($this->oxorder__oxid->value, $oUser->oxuser__oxid->value, $poolMoney);
            }
        }

        return $returnValue;
    }

    /**
     * Creates OrderArticle objects and assigns to them basket articles.
     * Updates quantity of sold articles (\OxidEsales\Eshop\Application\Model\Article::updateSoldAmount()).
     *
     * @param array $aArticleList article list
     * @deprecated underscore prefix violates PSR12, will be renamed to "setOrderArticles" in next major
     */
    protected function _setOrderArticles($aArticleList) // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
    {
        parent::_setOrderArticles($aArticleList);

        // Fix oxprice for gift certificates
        foreach ($this->_oArticles as $oOrderArticle) {
            // Find the corresponding basket item
            foreach ($aArticleList as $oBasketItem) {
                if ($oBasketItem->getProductId() === $oOrderArticle->oxorderarticles__oxartid->value) {
                    $oArticle = $oBasketItem->getArticle();
                    if ($oArticle && $oArticle->isGiftCertArticle()) {
                        // Set oxprice from the basket item's unit price
                        $oUnitPrice = $oBasketItem->getUnitPrice();
                        $oOrderArticle->oxorderarticles__oxprice = new \OxidEsales\Eshop\Core\Field(
                            $oUnitPrice->getBruttoPrice(),
                            \OxidEsales\Eshop\Core\Field::T_RAW
                        );
                    }
                    break;
                }
            }
        }
    }

    public function getGiftcertLogs()
    {
        if (null === $this->_aGiftcertLogList) {
            $oDb = \OxidEsales\Eshop\Core\DatabaseProvider::getDb(\OxidEsales\Eshop\Core\DatabaseProvider::FETCH_MODE_ASSOC);
            $sQ = "SELECT oxid from ysgiftcertlog where ysorderid=?";

            $rs = $oDb->select($sQ, [$this->oxorder__oxid->value]);
            if ($rs != false && $rs->count() > 0) {
                while (!$rs->EOF) {
                    $giftcertlog = oxNew(\YSquare\GiftCert\Model\GiftCertLog::class);
                    $giftcertlog->load($rs->fields['oxid']);
                    $this->_aGiftcertLogList[$rs->fields['oxid']] = $giftcertlog;
                    $rs->fetchRow();
                }
            }
        }
        return $this->_aGiftcertLogList;
    }

    public function getSumTotalBeforeGiftcert()
    {
        $sum = 0;
        $logs = $this->getGiftcertLogs();
        if (is_array($logs)) {
            foreach ($this->getGiftcertLogs() as $giftcertlog) {
                $sum += $giftcertlog->ysgiftcertlog__ysamountused->value;
            }
        }
        $sum += $this->oxorder__oxtotalordersum->value;
        $price = oxNew(\OxidEsales\Eshop\Core\Price::class);
        $price->setBruttoPriceMode();
        $price->setPrice($sum);
        return $price;
    }


    /***********************************************************
     * This part handles the purchase process and the money pool
     ***********************************************************/
    public function finalizeOrder(\OxidEsales\Eshop\Application\Model\Basket $oBasket, $oUser, $blRecalculatingOrder = false)
    {

        $giftCertHandler = oxNew(GiftCertHandler::class);
        $orderId = Registry::getSession()->getVariable('sess_challenge');

        // Get all gift certs and check how they should be handled,
        // email now or later?
        // Print Option?
//        $aCodes = array();
        $aBasketItems = $oBasket->getContents();

        if ($aBasketItems) {
            foreach ($aBasketItems as $oItem) {
                $oArticle = $oItem->getArticle();
                $isGiftCert = $oArticle->isGiftCertArticle();

                if ($oArticle && $isGiftCert) {
                    $amount = $oItem->getAmount();
                    $sValue = $oItem->getUnitPrice()->getPrice();
                    $persParams = $oItem->getPersParams();
                    if (isset($persParams["additionalInfo"])) {
                        $options = json_decode(html_entity_decode($persParams["additionalInfo"]), true);
                    }
                    // Maybe it's a good idea to have a reference to the article for later
                    $options["originArticleId"] = $oArticle->oxarticles__oxid->value;
                    $options["isBusinessGiftcert"] = $oArticle->oxarticles__ys_giftcert_business->value;

                    // Create Giftcert for each single item
                    $aCodes = [];
                    $aGiftcerts = [];
                    for ($i = 0; $i < $amount; $i++) {
                        $oGiftCert = $giftCertHandler->generateGiftCert($oArticle, $sValue, $this->getUser(), $orderId, $options);
                        $sCode = $oGiftCert->ysgiftcert__ysgiftcertnr->value;
                        $aCodes[$i] = $sCode;
                        $aGiftcerts[$i] = $oGiftCert;
                    }

                    // Add code to pers params for later use (maybe in ERP)
                    $options["code"] = $aCodes;
                    $persParams["additionalInfo"] = json_encode($options);
                    $oItem->setPersParams($persParams);

                    // If
                    // - the giftcert is an "online" giftcert (deliveryOption == 0)
                    // - the giftCert should be mailed
                    // - no individual data is given or preferred date is not in the future
                    // add it to the direct mail pool
                    // otherwise it will be processed later
                    if ($oGiftCert->ysgiftcert__ysdeliveryoption->value === 0) {
                        if (!$options["recipientMail"] || !$options["preferredSendDate"] || $options["preferredSendDate"] <= date("Y-m-d")) {
                            if (null === $this->_aGiftcertToDirectlyMailList) {
                                $this->_aGiftcertToDirectlyMailList = [];
                            }
                            foreach ($aCodes as $myKey => $myCode) {
                                $this->_aGiftcertToDirectlyMailList[$myCode] = [
                                    'dValue'   => $sValue,
                                    'sImage'   => $oArticle->oxarticles__ys_giftcert_image->value,
                                    // new Parameters
                                    'giftcert' => $aGiftcerts[$myKey]
                                ];
                            }
                        }
                    }
                }
            }
        }

        // Now check for each Code what to do...
        // --> if deliveryOption is email == 0
        // 1) Send directly via eMail to special recipient -> if recipient given
        // 2) Send directly as PDF in Order delivery Mail, means add ALL to one eMail -> if no recipient given
        // 3) Store for later sending to special recipient -> if recipient AND delivery date given
        // --> if deliveryOption is print (offline) == 1
        // 4) Store for Printing (Offline) -> mark in Backend, in mail to owner, elsewhere?!


        return parent::finalizeOrder($oBasket, $oUser, $blRecalculatingOrder);

    }

    public function getGiftCertsToDirectlySend()
    {
        // If variable is still NULL we possibly come back from Paypal&Co and
        // have to reload giftcerts for this order from Database
        if (null === $this->_aGiftcertToDirectlyMailList) {
            $this->_aGiftcertToDirectlyMailList = [];
            // Get order ID from session (normally paypal uses this) or get the already loaded one
            $orderId = Registry::getSession()->getVariable('sess_challenge');
            if (trim($orderId) == "") {
                $orderId = $this->getId();
            }
            $container = ContainerFactory::getInstance()->getContainer();
            $this->queryBuilderFactory = $container->get(QueryBuilderFactoryInterface::class);
            $queryBuilder = $this->queryBuilderFactory->create();
            $queryBuilder
                ->select('*')
                ->from("ysgiftcert")
                ->where('ysdelivered = 0')
                ->andWhere('ysdeliverydate <= :deliverydate')
                ->andWhere('oxorderid = :orderid')
                ->setParameters([
                    "deliverydate" => date("Y-m-d"),
                    "orderid"      => $orderId
                ]);
            $blocksData = $queryBuilder->execute();
            $blocksData = $blocksData->fetchAll();
            foreach ($blocksData as $entry) {
                $options = json_decode($entry["YSINDIVIDUALOPTIONS"], true);
                $image = "";

                // Only add the ones that should be sent directly
                // - Business giftcerts are never send by email
                // - All others only if delivery option is email AND preferred send date is not set or already over
                if (!isset($options["isBusinessGiftcert"]) || $options["isBusinessGiftcert"] == 0) {
                    if ($entry["YSDELIVERYOPTION"] == 0 && (!$options["recipientMail"] || !$options["preferredSendDate"] || $options["preferredSendDate"] <= date("Y-m-d"))) {
                        if (is_array($options) && isset($options['originArticleId'])) {
                            $queryBuilder = $this->queryBuilderFactory->create();
                            $queryBuilder
                                ->select('YS_GIFTCERT_IMAGE')
                                ->from("oxarticles")
                                ->where('oxid = :id')
                                ->setParameters([
                                    "id" => $options['originArticleId']
                                ]);
                            $blocksData = $queryBuilder->execute();
                            $blocksData = $blocksData->fetchAll();
                            if (is_array($blocksData) && isset($blocksData[0]['YS_GIFTCERT_IMAGE'])) {
                                $image = $blocksData[0]['YS_GIFTCERT_IMAGE'];
                            }
                        }
                        $cert = oxNew(GiftCert::class);
                        $cert->load($entry["OXID"]);
                        $this->_aGiftcertToDirectlyMailList[$entry["YSGIFTCERTNR"]] = [
                            'dValue'   => $entry["YSINITIALDISCOUNT"],
                            'sImage'   => $image,
                            // new Parameters
                            'giftcert' => $cert
                        ];
                    }
                }
            }
        }

        return $this->_aGiftcertToDirectlyMailList;
    }


}