<?php
/**
 * Mageplaza
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Mageplaza.com license that is
 * available through the world-wide-web at this URL:
 * https://www.mageplaza.com/LICENSE.txt
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade this extension to newer
 * version in the future.
 *
 * @category    Mageplaza
 * @package     Mageplaza_Faqs
 * @copyright   Copyright (c) Mageplaza (https://www.mageplaza.com/)
 * @license     https://www.mageplaza.com/LICENSE.txt
 */

namespace Mageplaza\Faqs\Helper;

use DateTime;
use DateTimeZone;
use Exception;
use Magento\Cms\Model\Template\FilterProvider;
use Magento\Framework\App\Area;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\App\Http\Context as HttpContext;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Filter\TranslitUrl;
use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Framework\ObjectManagerInterface;
use Magento\Store\Api\StoreRepositoryInterface;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
use Mageplaza\Core\Helper\AbstractData as CoreHelper;
use Mageplaza\Faqs\Api\Data\ArticleInterface;
use Mageplaza\Faqs\Model\Article;
use Mageplaza\Faqs\Model\ArticleFactory;
use Mageplaza\Faqs\Model\ResourceModel\Article as ArticleResourceModel;
use Mageplaza\Faqs\Model\Category;
use Mageplaza\Faqs\Model\CategoryFactory;
use Mageplaza\Faqs\Model\ResourceModel\Category as CategoryResourceModel;
use Mageplaza\Faqs\Model\Tag;
use Mageplaza\Faqs\Model\TagFactory;
use Mageplaza\Faqs\Model\ResourceModel\Tag as TagResourceModel;
use Mageplaza\Faqs\Model\Config\Source\System\AddQuestion;
use Mageplaza\Faqs\Model\Config\Source\Visibility;
use Mageplaza\Faqs\Model\ResourceModel\Article\Collection;
use Mageplaza\Faqs\Helper\File as HelperFile;

/**
 * Class Data
 * @package Mageplaza\Faqs\Helper
 */
class Data extends CoreHelper
{
    const CONFIG_MODULE_PATH = 'faqs';
    const TYPE_ARTICLE       = 'article';
    const TYPE_CATEGORY      = 'category';
    const TYPE_TAG           = 'tag';

    /**
     * @var TransportBuilder
     */
    protected $_transportBuilder;

    /**
     * @var FilterProvider
     */
    protected $_filterProvider;

    /**
     * @var ArticleFactory
     */
    protected $_articleFactory;

    /**
     * @var CategoryFactory
     */
    protected $_categoryFactory;

    /**
     * @var TagFactory
     */
    protected $_tagFactory;

    /**
     * @var TranslitUrl
     */
    public $translitUrl;

    /**
     * @var HttpContext
     */
    public $httpContext;

    /**
     * @var HelperFile
     */
    protected $helperFile;

    /**
     * @var array
     */
    protected $storesOptionHash = [];

    /**
     * @var StoreRepositoryInterface
     */
    protected $repository;

    /**
     * Data constructor.
     *
     * @param Context $context
     * @param HttpContext $httpContext
     * @param ObjectManagerInterface $objectManager
     * @param StoreManagerInterface $storeManager
     * @param TranslitUrl $translitUrl
     * @param FilterProvider $filterProvider
     * @param TransportBuilder $transportBuilder
     * @param ArticleFactory $articleFactory
     * @param CategoryFactory $categoryFactory
     * @param TagFactory $tagFactory
     * @param File $helperFile
     * @param StoreRepositoryInterface $repository
     */
    public function __construct(
        Context $context,
        HttpContext $httpContext,
        ObjectManagerInterface $objectManager,
        StoreManagerInterface $storeManager,
        TranslitUrl $translitUrl,
        FilterProvider $filterProvider,
        TransportBuilder $transportBuilder,
        ArticleFactory $articleFactory,
        CategoryFactory $categoryFactory,
        TagFactory $tagFactory,
        HelperFile $helperFile,
        StoreRepositoryInterface $repository
    ) {
        $this->translitUrl       = $translitUrl;
        $this->_filterProvider   = $filterProvider;
        $this->_transportBuilder = $transportBuilder;
        $this->_articleFactory   = $articleFactory;
        $this->_categoryFactory  = $categoryFactory;
        $this->_tagFactory       = $tagFactory;
        $this->httpContext       = $httpContext;
        $this->helperFile        = $helperFile;
        $this->repository        = $repository;

        parent::__construct($context, $objectManager, $storeManager);
    }

    /**
     * Generate url_key for article
     *
     * @param ArticleResourceModel|CategoryResourceModel|TagResourceModel $resource
     * @param Article|Category|Tag $object
     * @param string $name
     *
     * @return string
     * @throws LocalizedException
     */
    public function generateUrlKey($resource, $object, $name)
    {
        $attempt = -1;
        do {
            if ($attempt++ >= 10) {
                throw new LocalizedException(__('Unable to generate url key. Please check the setting and try again.'));
            }

            $urlKey = $this->translitUrl->filter($name);
            if ($urlKey) {
                $urlKey .= ($attempt ?: '');
            }
        } while ($this->checkUrlKey($resource, $object, $urlKey));

        return $urlKey;
    }

    /**
     * Check url key if it is existed
     *
     * @param ArticleResourceModel|CategoryResourceModel|TagResourceModel $resource
     * @param Article|Category|Tag $object
     * @param string $urlKey
     *
     * @return bool
     * @throws LocalizedException
     */
    public function checkUrlKey($resource, $object, $urlKey)
    {
        if (empty($urlKey)) {
            return true;
        }

        $adapter = $resource->getConnection();
        $select  = $adapter->select()
            ->from($resource->getMainTable(), '*')
            ->where('url_key = :url_key');

        $binds = ['url_key' => (string) $urlKey];

        if ($id = $object->getId()) {
            $select->where($resource->getIdFieldName() . ' != :object_id');
            $binds['object_id'] = (int) $id;
        }

        return $adapter->fetchOne($select, $binds);
    }

    /**
     * @param string $code
     * @param null $storeId
     *
     * @return mixed
     */
    public function getFaqsPageConfig($code = '', $storeId = null)
    {
        $code = ($code !== '') ? '/' . $code : '';

        return $this->getModuleConfig('faq_home_page' . $code, $storeId);
    }

    /**
     * @param string $code
     * @param null $storeId
     *
     * @return mixed
     */
    public function getProductTabConfig($code = '', $storeId = null)
    {
        $code = ($code !== '') ? '/' . $code : '';

        return $this->getModuleConfig('product_tab' . $code, $storeId);
    }

    /**
     * @param $code
     * @param $storeId
     *
     * @return array|mixed
     */
    public function getSchemaConfig($code = '', $storeId = null)
    {
        $code = ($code !== '') ? '/' . $code : '';

        return $this->getModuleConfig('schema_markup' . $code, $storeId);
    }

    /**
     * @param string $code
     * @param null $storeId
     *
     * @return mixed
     */
    public function getTermConfig($code = '', $storeId = null)
    {
        $code = ($code !== '') ? '/' . $code : '';

        return $this->getModuleConfig('term_condition' . $code, $storeId);
    }

    /**
     * @param string $code
     * @param null $storeId
     *
     * @return mixed
     */
    public function getEmailConfig($code = '', $storeId = null)
    {
        $code = ($code !== '') ? '/' . $code : '';

        return $this->getModuleConfig('email' . $code, $storeId);
    }

    /**
     * @param null $store
     *
     * @return string
     */
    public function getRoute($store = null)
    {
        return $this->getFaqsPageConfig('route', $store) ?: 'faq';
    }

    /**
     * @param null $store
     *
     * @return string
     */
    public function getFaqsName($store = null)
    {
        return $this->getFaqsPageConfig('title', $store) ?: 'Frequently Answer and Question';
    }

    /**
     * @param null $urlKey
     * @param null $type
     *
     * @return string
     */
    public function getFaqsUrl($urlKey = null, $type = null)
    {
        if (is_object($urlKey)) {
            $urlKey = $urlKey->getUrlKey();
        }

        $urlKey = ($type ? $type . '/' : '') . $urlKey;
        $url    = $this->getUrl($this->getRoute() . '/' . $urlKey);
        $url    = explode('?', $url);
        $url    = $url[0];

        return rtrim($url, '/');
    }

    /**
     * @param string $route
     * @param array $params
     *
     * @return string
     */
    public function getUrl($route, $params = [])
    {
        return $this->_urlBuilder->getUrl($route, $params);
    }

    /**
     * @param null $storeId
     *
     * @return mixed
     */
    public function getCategoryCollection($storeId = null)
    {
        return $this->getObjectCollection(self::TYPE_CATEGORY, 'enabled', $storeId);
    }

    /**
     * @param null $storeId
     *
     * @return mixed
     */
    public function getTagCollection($storeId = null)
    {
        return $this->getObjectCollection(self::TYPE_TAG, 'enabled', $storeId);
    }

    /**
     * Get article collection
     *
     * @param null $storeId
     *
     * @return mixed
     */
    public function getArticleCollection($storeId = null)
    {
        return $this->getObjectCollection(self::TYPE_ARTICLE, 'visibility', $storeId);
    }

    /**
     * @param null $storeId
     *
     * @return mixed
     */
    public function getArticleApiCollection($storeId = null)
    {
        $collection = $this->getFactoryByType(self::TYPE_ARTICLE)
            ->create()
            ->getCollection();
        $this->addStoreFilter($collection, $storeId);

        return $collection;
    }

    /**
     * @param string $type
     * @param string $enabled
     * @param null $storeId
     *
     * @return mixed
     */
    public function getObjectCollection($type, $enabled, $storeId = null)
    {
        $collection = $this->getFactoryByType($type)
            ->create()
            ->getCollection()
            ->addFieldToFilter($enabled, 1)
            ->setOrder('position', 'asc');
        $this->addStoreFilter($collection, $storeId);

        return $collection;
    }

    /**
     * Filter by store
     *
     * @param $collection
     * @param null $storeId
     *
     * @return mixed
     */
    public function addStoreFilter($collection, $storeId = null)
    {
        if ($storeId === null) {
            try {
                $storeId = $this->storeManager->getStore()->getId();
            } catch (Exception $e) {
                $storeId = null;
            }
        }

        if ($storeId) {
            $collection->addFieldToFilter('main_table.store_ids', [
                ['finset' => Store::DEFAULT_STORE_ID],
                ['finset' => $storeId]
            ]);
        } else {
            $collection->addFieldToFilter('main_table.store_ids', [
                ['finset' => Store::DEFAULT_STORE_ID]
            ]);
        }

        return $collection;
    }

    /**
     * @param int $catId
     * @param null $where
     *
     * @return Collection
     */
    public function getArticleByCategory($catId, $where = null)
    {
        /** @var Collection $collection */
        $collection = $this->getArticleCollection();

        $collection->getSelect()
            ->joinLeft(
                ['category' => $collection->getTable('mageplaza_faqs_article_category')],
                'main_table.article_id = category.article_id'
            )->where('category.category_id = ' . $catId . ' ' . $where);

        return $collection;
    }

    /**
     * @param int $tagId
     * @param null $where
     *
     * @return Collection
     */
    public function getArticleByTag($tagId, $where = null)
    {
        /** @var Collection $collection */
        $collection = $this->getArticleCollection();

        $collection->getSelect()
            ->joinLeft(
                ['tag' => $collection->getTable('mageplaza_faqs_article_tag')],
                'main_table.article_id = tag.article_id'
            )->where('tag.tag_id = ' . $tagId . ' ' . $where);

        return $collection;
    }

    /**
     * @param int $articleId
     * @param null $where
     *
     * @return mixed
     */
    public function getRelatedArticles($articleId, $where = null)
    {
        /** @var Collection $collection */
        $collection = $this->getArticleCollection();

        $collection->getSelect()
            ->joinLeft(
                ['mp_article' => $collection->getTable('mageplaza_faqs_article_article')],
                'main_table.article_id = mp_article.related_article_id'
            )->where('mp_article.article_id = ' . $articleId . ' ' . $where);

        return $this->getArticleCollection()->addFieldToFilter('article_id', ['in' => $collection->getAllIds()]);
    }

    /**
     * @param string $value
     * @param null $code
     * @param null $type
     *
     * @return mixed
     */
    public function getObjectByParam($value, $code = null, $type = null)
    {
        if ($code === 'url_key') {
            $storeId = $this->getStoreId();
            $object  = $this->getFactoryByType($type)
                ->create()
                ->loadByStoreIds($value, $storeId);
        } else {
            $object = $this->getFactoryByType($type)
                ->create()
                ->load($value, $code);
        }

        return $object;
    }

    /**
     * @return int
     */
    public function getStoreId()
    {
        try {
            $storeId = $this->storeManager->getStore()->getId();
        } catch (\Exception $e) {
            $storeId = Store::DEFAULT_STORE_ID;
        }

        return $storeId;
    }

    /**
     * @param Article|ArticleInterface $article
     *
     * @throws NoSuchEntityException
     */
    public function sendEmailToCustomer($article)
    {
        /** Send mail to customer when the question is answered */
        $toEmail         = $article->getAuthorEmail();
        $emailTemplate   = $this->getEmailConfig('customer/template');
        $questionContent = $article->getArticleContent();
        $questionContent = str_replace("&nbsp;", "", $questionContent);
        $currentStore    = $this->storeManager->getStore();
        $sender          = $this->getEmailConfig('customer/sender');

        if ($toEmail
            && $this->getEmailConfig('enabled')
            && $this->getEmailConfig('customer/enabled')
            && $article->getEmailNotify()
            && (int) $article->getVisibility() === Visibility::PUBLISH
            && (int) $article->getStatus() === 0) {
            try {
                $vars = [
                    'customer_name'    => $article->getAuthorName(),
                    'question'         => $article->getName(),
                    'question_content' => $questionContent,
                    'date'             => $article->getCreatedAt(),
                    'store_name'       => $currentStore->getName()
                ];
                $this->sendMail($currentStore, $toEmail, $emailTemplate, $vars, $sender);
            } catch (Exception $e) {
                $this->_logger->critical($e);
            }
        }
    }

    /**
     * @param Article|ArticleInterface $article
     *
     * @throws NoSuchEntityException
     */
    public function sendEmailToAdmin($article)
    {
        $toEmail       = $this->getEmailConfig('admin/send_to');
        $emailTemplate = $this->getEmailConfig('admin/template');
        $store         = $this->storeManager->getStore();
        if ($toEmail
            && $this->getEmailConfig('enabled')
            && $this->getEmailConfig('admin/enabled')) {
            try {
                $vars = [
                    'question'    => $article->getName(),
                    'question_id' => $article->getId(),
                    'date'        => $article->getCreatedAt(),
                    'store_name'  => $store->getName()
                ];
                $this->sendMail($store, $toEmail, $emailTemplate, $vars);
            } catch (Exception $e) {
                $this->_logger->critical($e);
            }
        }
    }

    /**
     * @param null $type
     *
     * @return mixed
     */
    public function getFactoryByType($type = null)
    {
        if ($type === self::TYPE_CATEGORY) {
            $object = $this->_categoryFactory;
        } elseif ($type === self::TYPE_TAG) {
            $object = $this->_tagFactory;
        } else {
            $object = $this->_articleFactory;
        }

        return $object;
    }

    /**
     * @param array $ids
     *
     * @return mixed
     */
    public function getCategoriesByArticle($ids)
    {
        $collection = $this->getObjectCollection(self::TYPE_CATEGORY, 'enabled')
            ->addFieldToFilter('main_table.category_id', ['in' => $ids]);

        return $collection;
    }

    /**
     * Get is enabled faq detail page
     *
     * @return mixed
     */
    public function isEnabledDetailPage()
    {
        return $this->getConfigGeneral('question_detail_page/enabled');
    }

    /**
     * @return mixed
     */
    public function isEnabledFaqsPage()
    {
        return $this->getFaqsPageConfig('enabled');
    }

    /**
     * Check if this is home page
     *
     * @return bool
     */
    public function isFaqsHomePage()
    {
        return $this->_request->getFullActionName() === 'mpfaqs_article_index';
    }

    /**
     * @param Store $store
     * @param string $toEmail
     * @param string $template
     * @param array $vars
     * @param string $sender
     *
     * @throws LocalizedException
     */
    public function sendMail($store, $toEmail, $template, $vars, $sender = 'general')
    {
        if ($toEmail) {
            $transport = $this->_transportBuilder
                ->setTemplateIdentifier($template)
                ->setTemplateOptions([
                    'area'  => Area::AREA_FRONTEND,
                    'store' => $store->getId()
                ])
                ->setFrom($sender)
                ->addTo($toEmail)
                ->setTemplateVars($vars)
                ->getTransport();

            try {
                $transport->sendMessage();
            } catch (Exception $e) {
                $this->_logger->error($e->getMessage());
            }
        }
    }

    /**
     * Get date formatted
     *
     * @param string $date
     * @param string $dateType
     *
     * @return string
     * @throws Exception
     */
    public function getDateFormat($date, $dateType)
    {
        $dateTime = new DateTime($date, new DateTimeZone('UTC'));
        $dateTime->setTimezone(new DateTimeZone($this->getTimezone()));

        return $dateTime->format($dateType);
    }

    /**
     * Get configuration zone
     *
     * @return mixed
     */
    public function getTimezone()
    {
        return $this->getConfigValue('general/locale/timezone');
    }

    /**
     * Check customer is logged in or not
     *
     * @return bool
     */
    public function isLoggedIn()
    {
        return $this->httpContext->getValue(\Magento\Customer\Model\Context::CONTEXT_AUTH);
    }

    /**
     * @param string $content
     *
     * @return string
     * @throws Exception
     */
    public function getPageFilter($content)
    {
        return $this->_filterProvider->getPageFilter()->filter($content);
    }

    /**
     * Check module is available
     *
     * @param string $moduleName
     *
     * @return bool
     */
    public function checkModuleEnabled($moduleName)
    {
        return $this->_moduleManager->isEnabled($moduleName);
    }

    /**
     * Escape string for the JavaScript context
     *
     * @param string $string
     *
     * @return string
     * @since 100.2.0
     */
    public function escapeJs($string)
    {
        if ($string === '' || ctype_digit($string)) {
            return $string;
        }

        return preg_replace_callback(
            '/[^a-z0-9,\._]/iSu',
            function ($matches) {
                $chr = $matches[0];
                if (strlen($chr) !== 1) {
                    $chr = mb_convert_encoding($chr, 'UTF-16BE', 'UTF-8');
                    $chr = ($chr === false) ? '' : $chr;
                }

                return sprintf('\\u%04s', strtoupper(bin2hex($chr)));
            },
            $string
        );
    }

    /**
     * Check is show question form
     *
     * @return bool
     */
    public function isShowForm()
    {
        $questionConfig = $this->getConfigGeneral('question/enabled');
        $isLoggedIn     = (int) $questionConfig === AddQuestion::LOGGED && $this->isLoggedIn();

        return $isLoggedIn || (int) $questionConfig === AddQuestion::YES;
    }

    /**
     * @param null $storeId
     *
     * @return mixed
     */
    public function getPositiveHelpful($storeId = null)
    {
        if (!$storeId) {
            try {
                $storeId = $this->storeManager->getStore()->getId();
            } catch (Exception $e) {
                $storeId = Store::DEFAULT_STORE_ID;
            }
        }

        return $this->getConfigGeneral('positive_helpful', $storeId);
    }

    /**
     * @param null $storeId
     *
     * @return mixed
     */
    public function getNegativeHelpful($storeId = null)
    {
        if (!$storeId) {
            try {
                $storeId = $this->storeManager->getStore()->getId();
            } catch (Exception $e) {
                $storeId = Store::DEFAULT_STORE_ID;
            }
        }

        return $this->getConfigGeneral('negative_helpful', $storeId);
    }

    /**
     * @param string $file
     *
     * @return string
     */
    public function getFileUrl($file)
    {
        try {
            if ($file) {
                $iconUrl = $this->helperFile->getBaseMediaUrl() .
                    '/' . $this->helperFile->getMediaPath(
                        $file,
                        HelperFile::TEMPLATE_MEDIA_TYPE_FILE
                    );
            }
        } catch (Exception $e) {
            $iconUrl = '';
        }

        return $iconUrl;
    }

    /**
     * @param Article|Category|Tag $object
     *
     * @return bool
     * @throws NoSuchEntityException
     */
    public function checkStore($object)
    {
        $storeEnable = explode(',', $object->getStoreIds());

        return in_array('0', $storeEnable, true)
            || in_array((string) $this->storeManager->getStore()->getId(), $storeEnable, true);
    }

    /***
     * @return array
     */
    public function getStores()
    {
        if (!$this->storesOptionHash) {
            $options = [];
            $stores  = $this->repository->getList();

            foreach ($stores as $store) {
                $options[$store->getId()] = $store->getName();
            }
            $this->storesOptionHash = $options;

        }

        return $this->storesOptionHash;
    }

    /**
     * @param $faqSchema
     * @param $prefixComment
     * @param $subfixComment
     *
     * @return string
     */
    public function createFaqSchema($faqSchema, $prefixComment = '', $subfixComment = '')
    {
        $applicationLdJson = $prefixComment;
        $applicationLdJson .= '<script type="application/ld+json">'
            . json_encode(
                $faqSchema,
                JSON_PRETTY_PRINT
            ) . '</script>';
        $applicationLdJson .= $subfixComment;

        return $applicationLdJson;
    }
}
