<?php

namespace YSquare\GiftCert\Core;

use OxidEsales\Eshop\Application\Model\Article;
use OxidEsales\Eshop\Core\Field;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
use Throwable;

class GiftCertHandler
{
    // What to handle?
    // - get Certs for an order
    // - send certs by email (plain or PDF attached)
    // - get all certs not yet send and overdue
    // - produce PDF Certs


    protected $_iVoucherLength = "8";
    protected $_sVoucherChars = "0123456789abcdefghijklmnopqrstuvwxyz";
    protected $_pdfFilePath;

    /**
     * GiftCertHandler constructor.
     */
    function __construct()
    {
        $config = Registry::getConfig();
        $length = trim($config->getConfigParam('sYsGiftcertLength'));
        $allowedCahrs = trim($config->getConfigParam('sYsGiftcertAllowedChars'));
        if ($length !== "" && is_numeric($length)) {
            $this->_iVoucherLength = $length;
        }

        if ($allowedCahrs !== "") {
            $this->_sVoucherChars = $allowedCahrs;
        }

        $this->_pdfFilePath = Registry::getConfig()->getConfigParam("sShopDir") . "tmp/";
    }

    /**
     * @param $oArticle
     * @param $dPrice
     * @param null $options
     */
    function generateGiftCert($oArticle, $dPrice, $user = null, $orderId = "", $options = null)
    {
        DebugLogger::debug('GiftCertHandler::generateGiftCert', 'enter', [
            'articleId' => $oArticle ? $oArticle->oxarticles__oxid->value : null,
            'price'     => $dPrice,
            'userId'    => $user ? $user->oxuser__oxid->value : null,
            'orderId'   => $orderId,
        ]);

        $deliveryOption = $oArticle->isOfflineGiftCert() ? 1 : 0; // 0 = email, 1 = offline
        $deliveryDate = date("Y-m-d"); // default is today
        $purchaserMail = "";
        $recipientMail = "";
        $recipientText = "";

        if ($user) {
            $purchaserMail = $user->oxuser__oxusername->value;
        }

        if (null !== $options) {

            if (isset($options["deliveryOption"])) {
                // if there is a setting from outside that this giftcert should be mailed
                // well then, we do this
                $deliveryOption = $options["deliveryOption"];
            }
            if (isset($options["preferredSendDate"])) {
                $deliveryDate = $options["preferredSendDate"];
            }
            if (isset($options["recipientMail"])) {
                $recipientMail = $options["recipientMail"];
            }
            if (isset($options["messageToRecipient"])) {
                $recipientText = $options["messageToRecipient"];
            }
            if (isset($options["originArticleId"])) {
                $originArticleId = $options["originArticleId"];
            }

        }

        $individualOptions = [
            'purchaserMail'   => $purchaserMail,
            'recipientMail'   => $recipientMail,
            'recipientText'   => $recipientText,
            'originArticleId' => $originArticleId,
        ];
        if ($oArticle->isGiftCertArticle() && $dPrice > 0) {
            $sCode = $this->_generateGiftCertCode();
            $oGiftcert = oxNew(\YSquare\GiftCert\Model\GiftCert::class);
            $oGiftcert->ysgiftcert__ysgiftcertnr = new Field($sCode);
            $oGiftcert->ysgiftcert__ysinitialdiscount = new Field($dPrice);
            $oGiftcert->ysgiftcert__ysrestdiscount = new Field($dPrice);
            $oGiftcert->ysgiftcert__ysdatepurchased = new Field(date('Y-m-d H:i:s'));
            $oGiftcert->ysgiftcert__ysdeliveryoption = new Field($deliveryOption);
            $oGiftcert->ysgiftcert__ysdeliverydate = new Field($deliveryDate);
            $oGiftcert->ysgiftcert__ysindividualoptions = new Field(json_encode($individualOptions));
            $oGiftcert->ysgiftcert__oxorderid = new Field($orderId);
            $oGiftcert->save();
            DebugLogger::debug('GiftCertHandler::generateGiftCert', 'cert saved', [
                'certOxid'         => $oGiftcert->getId(),
                'certNr'           => $sCode,
                'deliveryOption'   => $deliveryOption,
                'deliveryDate'     => $deliveryDate,
                'storedOrderId'    => $oGiftcert->ysgiftcert__oxorderid->value,
                'orderIdArgEmpty'  => trim((string) $orderId) === '',
            ]);
            if (trim((string) $orderId) === '') {
                DebugLogger::warning('GiftCertHandler::generateGiftCert', 'cert saved with EMPTY oxorderid — will be invisible to getGiftCertsToDirectlySend later', [
                    'certOxid' => $oGiftcert->getId(),
                    'certNr'   => $sCode,
                    'callers'  => DebugLogger::callers(8),
                ]);
            }
        } else {
            DebugLogger::warning('GiftCertHandler::generateGiftCert', 'skipped — not a gift cert article or price <= 0', [
                'articleId'        => $oArticle ? $oArticle->oxarticles__oxid->value : null,
                'isGiftCertArticle' => $oArticle ? $oArticle->isGiftCertArticle() : false,
                'price'            => $dPrice,
                'orderId'          => $orderId,
            ]);
        }

        return isset($oGiftcert) ? $oGiftcert : null;


    }

    /**
     * Generates the actual code of the cert
     *
     * @return string
     */
    protected function _generateGiftCertCode()
    {
        $string = '';
        for ($p = 0; $p < $this->_iVoucherLength; $p++) {
            $string .= $this->_sVoucherChars[mt_rand(0, strlen($this->_sVoucherChars) - 1)];
        }

        $prefix = Registry::getConfig()->getConfigParam('aYsGiftcertPrefix');
        $string = $prefix . "-" . $string;

        return $string;
    }

    /**
     * @param $giftcert Giftcert Object that holds the setup
     * @param $sImage Image that should be used as background (old style - better use 12th picture in article
     * @param $output Output Type for PDF (F = as file e.g. for attachment, D = direct download in Browser e.g. for testing)
     * @param $template Different Template file (currently not in use)
     * @return string
     */
    public function generateAttachmentPDF($giftcert, $sImage = '', $output = 'F', $template = null)
    {
        $certNr = $giftcert ? $giftcert->ysgiftcert__ysgiftcertnr->value : null;
        $filename = $this->_pdfFilePath . uniqid(rand(), true) . '.pdf';
        $config = Registry::getConfig();
        $currency = $config->getActShopCurrencyObject();

        DebugLogger::debug('GiftCertHandler::generateAttachmentPDF', 'enter', [
            'certNr'    => $certNr,
            'pdfDir'    => $this->_pdfFilePath,
            'targetPdf' => $filename,
            'outputMode' => $output,
            'sImage'    => $sImage,
        ]);

        // Output mode 'F' writes to disk — verify directory is writable before mPDF silently fails.
        if ($output === 'F' && !is_writable($this->_pdfFilePath)) {
            DebugLogger::error('GiftCertHandler::generateAttachmentPDF', 'pdf target directory not writable', [
                'pdfDir' => $this->_pdfFilePath,
                'certNr' => $certNr,
            ]);
            return null;
        }

        // Resolve background image: explicit $sImage (module dir) wins, otherwise fall back to pic12 of origin article.
        $imageFullPath = null;
        $params = json_decode($giftcert->ysgiftcert__ysindividualoptions->rawValue, true);
        if (!is_array($params)) {
            $params = [];
        }

        if ($sImage !== '') {
            $imageFullPath =
                Registry::getConfig()->getConfigParam("sShopDir") .
                'modules/ysquare/ys_giftcert/out/img/' . $sImage;
            DebugLogger::debug('GiftCertHandler::generateAttachmentPDF', 'image: using module-provided', [
                'path'   => $imageFullPath,
                'exists' => file_exists($imageFullPath),
            ]);
        } elseif (!empty($params['originArticleId'])) {
            $article = oxNew(Article::class);
            if ($article->load($params["originArticleId"])) {
                $imageFullPath =
                    Registry::getConfig()->getConfigParam("sShopDir") .
                    'out/pictures/master/product/12/' . $article->oxarticles__oxpic12->value;
                DebugLogger::debug('GiftCertHandler::generateAttachmentPDF', 'image: using article pic12', [
                    'articleId' => $params['originArticleId'],
                    'path'      => $imageFullPath,
                    'exists'    => file_exists($imageFullPath),
                ]);
            } else {
                DebugLogger::warning('GiftCertHandler::generateAttachmentPDF', 'origin article could not be loaded', [
                    'articleId' => $params['originArticleId'],
                    'certNr'    => $certNr,
                ]);
            }
        } else {
            DebugLogger::warning('GiftCertHandler::generateAttachmentPDF', 'no image source — neither sImage nor originArticleId', [
                'certNr' => $certNr,
            ]);
        }

        $imageFullPath_extension = '';
        $imageDiv = '';
        if ($imageFullPath && file_exists($imageFullPath)) {
            $info = pathinfo($imageFullPath);
            $imageFullPath_extension = isset($info['extension']) ? strtolower($info['extension']) : '';
        } elseif ($imageFullPath) {
            DebugLogger::warning('GiftCertHandler::generateAttachmentPDF', 'background image file does not exist on disk', [
                'path'   => $imageFullPath,
                'certNr' => $certNr,
            ]);
            $imageFullPath = null;
        }

        try {
            // Setup Fonts
            $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
            $fontDirs = $defaultConfig['fontDir'];
            $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
            $fontData = $defaultFontConfig['fontdata'];

            $fontDirs = array_merge($fontDirs, [
                __DIR__ . "/../out/fonts/Barlow_Semi_Condensed",
                __DIR__ . "/../out/fonts/Oswald/static",
            ]);
            $fontData = $fontData + [
                    'barlowbold'   => [
                        'R' => 'BarlowSemiCondensed-Bold.ttf',
                        'I' => 'BarlowSemiCondensed-BoldItalic.ttf',
                    ],
                    'barlowregular' => [
                        'R' => 'BarlowSemiCondensed-Regular.ttf',
                        'I' => 'BarlowSemiCondensed-MediumItalic.ttf',
                    ],
                    'oswaldregular'       => [
                        'R' => 'Oswald-Regular.ttf',
                    ],
                    'oswaldsemibold'       => [
                        'R' => 'Oswald-SemiBold.ttf',
                    ],
                ];

            $mpdf = new \Mpdf\Mpdf([
                'fontDir' => $fontDirs,
                'fontdata' => $fontData,
                'default_font' => 'barlowregular'
            ]);

            // Choose weather to load a pdf as template or use an image as background
            if ($imageFullPath && ($imageFullPath_extension == "jpg" || $imageFullPath_extension == "jpeg" || $imageFullPath_extension == "png")) {
                $imageDiv = "<div><img src='" . $imageFullPath . "' style='width:100%'></div>";
            } elseif ($imageFullPath && $imageFullPath_extension == "pdf") {
                $imageDiv = "";
                $pagecount = $mpdf->setSourceFile($imageFullPath);
                $tplIdx = $mpdf->importPage($pagecount);
                $mpdf->useTemplate($tplIdx);
            }

            $params["imageDiv"] = $imageDiv;
            $params["currencySign"] = $currency->sign;

            $renderedHtml = $this->renderGiftcertTemplate($params, $giftcert);
            DebugLogger::debug('GiftCertHandler::generateAttachmentPDF', 'template rendered', [
                'certNr'     => $certNr,
                'htmlLength' => strlen((string) $renderedHtml),
            ]);

            $mpdf->writeHtml($renderedHtml);

            // outputting file to browser / disk
            $mpdf->output($filename, $output);

        } catch (Throwable $e) {
            DebugLogger::error('GiftCertHandler::generateAttachmentPDF', 'mPDF threw exception', [
                'certNr'    => $certNr,
                'exception' => get_class($e),
                'message'   => $e->getMessage(),
                'file'      => $e->getFile(),
                'line'      => $e->getLine(),
            ]);
            return null;
        }

        if ($output === 'F') {
            $exists = file_exists($filename);
            $size   = $exists ? filesize($filename) : 0;
            if (!$exists || $size <= 0) {
                DebugLogger::error('GiftCertHandler::generateAttachmentPDF', 'pdf file missing or empty after output', [
                    'certNr'   => $certNr,
                    'filename' => $filename,
                    'exists'   => $exists,
                    'size'     => $size,
                ]);
                return null;
            }
            DebugLogger::debug('GiftCertHandler::generateAttachmentPDF', 'pdf written', [
                'certNr'   => $certNr,
                'filename' => $filename,
                'size'     => $size,
            ]);
        }

        return $filename;
    }

    protected function renderGiftcertTemplate($params, $giftcert)
    {
        $renderer = oxNew(\OxidEsales\Eshop\Application\Model\SmartyRenderer::class);
        $aViewData = [
            "params"       => $params,
            "giftcert"     => $giftcert
        ];

        return $renderer->renderTemplate("giftcert/default.tpl", $aViewData);
    }


    public function generatePrintPDF($giftcert)
    {

    }

    /**
     * Some payment Modules call the voucher redeem Process twice.
     * This function checks if the pool was already used for this order
     *
     * @param $orderid
     */
    public function isPoolRedeemedInOrder($orderid)
    {
        $container = ContainerFactory::getInstance()->getContainer();
        $queryBuilderFactory = $container->get(QueryBuilderFactoryInterface::class);


        $queryBuilder = $queryBuilderFactory->create();
        $queryBuilder
            ->select("count(*) as cnt")
            ->from("ysgiftcertlog")
            ->where("ysorderid = :ysorderid")
            ->andWhere("yspoolredeem = 1")
            ->setParameter("ysorderid", $orderid);
        $blocksData = $queryBuilder->execute();
        $blocksData = $blocksData->fetchAll();

        if ($blocksData[0]['cnt'] > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Some payment modules call the voucher redeem process twice.
     * This function checks if the given giftcert was already used for this order
     *
     * @param $giftcertid
     * @param $orderid
     * @return bool
     */
    public function isGiftCertRedeemedInOrder($giftcertid, $orderid) {
        $container = ContainerFactory::getInstance()->getContainer();
        $queryBuilderFactory = $container->get(QueryBuilderFactoryInterface::class);


        $queryBuilder = $queryBuilderFactory->create();
        $queryBuilder
            ->select("count(*) as cnt")
            ->from("ysgiftcertlog")
            ->where("ysorderid = :ysorderid")
            ->andWhere("ysgiftcertid = :ysgiftcertid")
            ->setParameters(["ysorderid" => $orderid,"ysgiftcertid" => $giftcertid]);
        $blocksData = $queryBuilder->execute();
        $blocksData = $blocksData->fetchAll();

        if ($blocksData[0]['cnt'] > 0) {
            return true;
        } else {
            return false;
        }
    }

    public function logPoolRedeem($orderid, $userid, $valueRedeemed)
    {
        // add log entry for purchase
        $giftcertLog = oxNew(\YSquare\GiftCert\Model\GiftCertLog::class);
        $giftcertLog->ysgiftcertlog__ysgiftcertid = new Field('-');
        $giftcertLog->ysgiftcertlog__yspoolredeem = new Field(1);
        $giftcertLog->ysgiftcertlog__ysamountused = new Field($valueRedeemed);
        $giftcertLog->ysgiftcertlog__ysorderid = new Field($orderid);
        $giftcertLog->ysgiftcertlog__ysuserid = new Field($userid);
        $giftcertLog->ysgiftcertlog__ysdateused = new Field(date("Y-m-d H:i:s", Registry::getUtilsDate()->getTime()));
        $giftcertLog->save();
    }
}