<?php
/**
 * 2007-2023 PrestaShop
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
 * versions in the future. If you wish to customize PrestaShop for your
 * needs please refer to http://www.prestashop.com for more information.
 *
 * @author    PrestaShop SA <contact@prestashop.com>
 * @copyright 2007-2023 PrestaShop SA
 * @license   http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
 *  International Registered Trademark & Property of PrestaShop SA
 */

use PrestaShop\Module\AgileMultisellerPro\Entity\Sellerinfo;
use PrestaShop\Module\AgileMultisellerPro\Entity\SellerinfoMembershipCustomer;
use PrestaShop\Module\AgileMultisellerPro\Entity\SellerinfoRating;
use PrestaShop\Module\AgileMultisellerPro\Entity\SellerinfoRatingCriterion;
use PrestaShop\Module\AgileMultisellerPro\Helper\AgileHelper;
use PrestaShop\Module\AgileMultisellerPro\Helper\AgileHelperRating;
use PrestaShop\Module\AgileMultisellerPro\Provider\SellerProductSearchProvider;
use PrestaShop\PrestaShop\Core\Product\Search\Pagination;
use PrestaShop\PrestaShop\Core\Product\Search\ProductSearchQuery;
use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;

if (!defined('_PS_VERSION_')) {
    exit;
}

class AgileMultiSellerProSellerModuleFrontController extends ProductListingFrontController
{
    public $name = 'seller';
    public $seller;
    public $virtualUri;
    public $entityManager;

    public function __construct()
    {
        parent::__construct();
    }

    /**
     * overrides layout if category is not visible.
     *
     * @return bool|string
     */
    public function getLayout()
    {
        return 'layouts/layout-left-column.tpl';
    }

    public function setMedia()
    {
        parent::setMedia();

        $this->registerJavascript(
            'agilemultisellerpro-seller-js',
            '/modules/agilemultisellerpro/views/js/front/multipleshop.seller.js',
            ['position' => 'bottom', 'priority' => 1000]
        );

        $this->registerStylesheet(
            'agilemultisellerpro-seller-css',
            '/modules/agilemultisellerpro/views/css/multipleshop.seller.css',
            ['media' => 'all', 'priority' => 100]
        );

        Media::addJsDef([
            'sellerLink' => $this->context->link->getModuleLink(
                'agilemultisellerpro',
                'seller',
                ['shop_virtual_url' => self::getVirtualUriFromUrl($this->urls['current_url'])]
            ),
        ]);
    }

    public function init()
    {
        parent::init();

        if (
            Tools::isSubmit('ajax')
            && Tools::isSubmit('idSellerinfo')
            && Tools::getValue('action') == 'displayFeedbackList'
        ) {
            $this->ajaxRender(json_encode([
                'status' => 'ok',
                'feedbackList' => $this->getFeedbackList((int) Tools::getValue('idSellerinfo')),
            ]));
            exit;
        }

        $this->getTemplateVarUrls();

        $this->virtualUri = self::getVirtualUriFromUrl($this->urls['current_url']);

        if (!$this->virtualUri || $this->virtualUri == 'shop') {
            header('HTTP/1.0 404 Not Found');
            header('Status: 404 Not Found');
            $this->setTemplate('errors/404.tpl');

            return;
        }

        $this->entityManager = $this->container->get('doctrine.orm.entity_manager');

        $sellerInfoRepository = $this->entityManager->getRepository(Sellerinfo::class);

        $this->seller = $sellerInfoRepository->getSellerInfosByVirtualUri($this->virtualUri, $this->context->language->id);
    }

    public function initContent()
    {
        $sellerInfoMembershipCustomerRepository = $this->entityManager
            ->getRepository(SellerinfoMembershipCustomer::class);

        $expiry_date = $sellerInfoMembershipCustomerRepository
            ->getExpiryDate($this->seller['id_customer'], $this->context->language->id);
        $today = new DateTime(date('Y-m-d H:i:s'));

        $membership_valid = ($expiry_date >= $today);

        if (Configuration::get('AGILE_MEMBERSHIP_STATUS') && !$membership_valid) {
            Tools::redirect($this->context->link->getModuleLink('agilemultisellerpro', 'agilesellers'));
        }

        parent::initContent();

        $conf = Configuration::getMultiple(AgileHelper::getCustomFields());
        $custom_labels = AgileHelper::getCustomLabels('');

        $categories = [];

        if ($this->seller) {
            $categories = $this->getCategories();
        }

        $id_selectedcategory = (int) Tools::getValue('id_selectedcategory');

        $current_category = null;
        foreach ($categories as $category) {
            if ($category['id_category'] == $id_selectedcategory) {
                $current_category = $category;

                break;
            }
        }

        if (!$current_category) {
            $current_category = reset($categories);
        }

        $this->context->smarty->assign([
            'logoUrl' => AgileHelper::getImagePath($this->seller['id_sellerinfo'], 'logo'),
            'seller' => $this->seller,
            'conf' => $conf,
            'custom_labels' => $custom_labels,
            'categories' => $categories,
            'current_category' => $current_category,
            'id_selectedcategory' => $id_selectedcategory,
            'seller_filter_query' => Tools::getValue('query', ''),
            'seller_sort_selected' => Tools::getValue('order', ''),
            'shop_name' => (new Shop($this->seller['id_shop']))->name,
            'shop_uri' => ltrim((new Shop($this->seller['id_shop']))->virtual_uri, '/'),
        ]);

        $this->initRating();

        $this->doProductSearch('module:agilemultisellerpro/views/templates/front/multipleshop/seller.tpl');
    }

    public function setTemplate($template, $params = [], $locale = null)
    {
        if (strpos($template, 'module:') === 0) {
            $this->template = $template;
        } else {
            parent::setTemplate($template, $params, $locale);
        }
    }

    public function getListingLabel()
    {
        return $this->getTranslator()->trans('Seller products', [], 'Modules.Agilemultisellerpro.Seller');
    }

    protected function getProductSearchQuery()
    {
        $query = new ProductSearchQuery();
        $query
            ->setSortOrder(new SortOrder('product', Tools::getProductsOrder('by'), Tools::getProductsOrder('way')))
        ;

        return $query;
    }

    protected function getDefaultProductSearchProvider()
    {
        $id_selectedcategory = (int) Tools::getValue('id_selectedcategory');
        $extra_conditions = '';
        if ($id_selectedcategory > 0) {
            $extra_conditions = ' AND p.id_product IN (SELECT DISTINCT id_product 	FROM ' . _DB_PREFIX_ . 'category_product WHERE id_category = ' . (int) $id_selectedcategory . ' )';
        }

        $queryString = trim(Tools::getValue('query'));
        if ($queryString !== '') {
            $queryString = Tools::replaceAccentedChars(urldecode($queryString));

            $extra_conditions .= ' AND pl.name LIKE \'%' . pSQL($queryString) . '%\'';
        }

        return new SellerProductSearchProvider(
            $this->getTranslator(),
            $this->seller['id_sellerinfo'],
            $extra_conditions
        );
    }

    public function getBreadcrumbLinks()
    {
        $breadcrumb = parent::getBreadcrumbLinks();

        $breadcrumb['links'][] = [
            'title' => $this->trans('Sellers list', [], 'Modules.Agilemultisellerpro.Seller'),
            'url' => $this->context->link->getModuleLink('agilemultisellerpro', 'agilesellers'),
        ];

        $breadcrumb['links'][] = [
            'title' => (new Shop($this->seller['id_shop']))->name,
            'url' => '#',
        ];

        return $breadcrumb;
    }

    public static function getVirtualUriFromUrl($url)
    {
        $urlComponents = parse_url($url);

        $path = isset($urlComponents['path']) ? trim($urlComponents['path'], '/') : '';

        $pathSegments = explode('/', $path);

        return end($pathSegments) . '/';
    }

    private function getCategories()
    {
        $categories = Category::getCategories($this->context->cookie->id_lang, true, false);
        $categories = AgileHelper::getSortedFullnameCategory($categories);

        $sql = 'SELECT DISTINCT id_category
				FROM ' . _DB_PREFIX_ . 'sellerinfo_product sip
					LEFT JOIN ' . _DB_PREFIX_ . 'category_product cp on sip.id_product = cp.id_product
				WHERE sip.id_sellerinfo = ' . $this->seller['id_sellerinfo'];
        $ids = AgileHelper::retrieveColumnValues(Db::getInstance()->ExecuteS($sql), 'id_category');

        $ret = [];
        foreach ($categories as $cat) {
            if (in_array((int) $cat['id_category'], $ids)) {
                $ret[] = $cat;
            }
        }

        return $ret;
    }

    /**
     * This returns all template variables needed for rendering
     * the product list, the facets, the pagination and the sort orders.
     *
     * @return array variables ready for templating
     */
    protected function getProductSearchVariables()
    {
        /*
         * To render the page we need to find something (a ProductSearchProviderInterface)
         * that knows how to query products.
         */

        // the search provider will need a context (language, shop...) to do its job
        $context = $this->getProductSearchContext();

        // the controller generates the query...
        $query = $this->getProductSearchQuery();

        $provider = $this->getDefaultProductSearchProvider();

        $resultsPerPage = (int) Tools::getValue('resultsPerPage');
        if ($resultsPerPage <= 0) {
            $resultsPerPage = (int) Configuration::get('PS_PRODUCTS_PER_PAGE');
        }

        // we need to set a few parameters from back-end preferences
        $query
            ->setResultsPerPage($resultsPerPage)
            ->setPage(max((int) Tools::getValue('page'), 1))
        ;

        // set the sort order if provided in the URL
        if ($encodedSortOrder = Tools::getValue('order')) {
            $query->setSortOrder(SortOrder::newFromString(
                $encodedSortOrder
            ));
        }

        // get the parameters containing the encoded facets from the URL
        $encodedFacets = Tools::getValue('q');

        /*
         * The controller is agnostic of facets.
         * It's up to the search module to use /define them.
         *
         * Facets are encoded in the "q" URL parameter, which is passed
         * to the search provider through the query's "$encodedFacets" property.
         */

        $query->setEncodedFacets($encodedFacets);

        Hook::exec('actionProductSearchProviderRunQueryBefore', [
            'query' => $query,
        ]);

        // We're ready to run the actual query!

        /** @var ProductSearchResult $result */
        $result = $provider->runQuery(
            $context,
            $query
        );

        Hook::exec('actionProductSearchProviderRunQueryAfter', [
            'query' => $query,
            'result' => $result,
        ]);

        if (Configuration::get('PS_CATALOG_MODE') && !Configuration::get('PS_CATALOG_MODE_WITH_PRICES')) {
            $this->disablePriceControls($result);
        }

        // sort order is useful for template,
        // add it if undefined - it should be the same one
        // as for the query anyway
        if (!$result->getCurrentSortOrder()) {
            $result->setCurrentSortOrder($query->getSortOrder());
        }

        // prepare the products
        $products = $this->prepareMultipleProductsForTemplate(
            $result->getProducts()
        );

        // render the facets
        if ($provider instanceof FacetsRendererInterface) {
            // with the provider if it wants to
            $rendered_facets = $provider->renderFacets(
                $context,
                $result
            );
            $rendered_active_filters = $provider->renderActiveFilters(
                $context,
                $result
            );
        } else {
            // with the core
            $rendered_facets = $this->renderFacets(
                $result
            );
            $rendered_active_filters = $this->renderActiveFilters(
                $result
            );
        }

        $pagination = $this->getTemplateVarPagination(
            $query,
            $result
        );

        // prepare the sort orders
        // note that, again, the product controller is sort-orders
        // agnostic
        // a module can easily add specific sort orders that it needs
        // to support (e.g. sort by "energy efficiency")
        $sort_orders = $this->getTemplateVarSortOrders(
            $result->getAvailableSortOrders(),
            $query->getSortOrder()->toString()
        );

        $sort_selected = false;
        if (!empty($sort_orders)) {
            foreach ($sort_orders as $order) {
                if (isset($order['current']) && true === $order['current']) {
                    $sort_selected = $order['label'];

                    break;
                }
            }
        }

        $searchVariables = [
            'result' => $result,
            'label' => $this->getListingLabel(),
            'products' => $products,
            'sort_orders' => $sort_orders,
            'sort_selected' => $sort_selected,
            'seller_sort_selected' => Tools::getValue('order', ''),
            'seller_filter_query' => Tools::getValue('query', ''),
            'pagination' => $pagination,
            'rendered_facets' => $rendered_facets,
            'rendered_active_filters' => $rendered_active_filters,
            'js_enabled' => $this->ajax,
            'current_url' => $this->updateQueryString([
                'q' => $result->getEncodedFacets(),
            ]),
        ];

        Hook::exec('filterProductSearch', ['searchVariables' => &$searchVariables]);
        Hook::exec('actionProductSearchAfter', $searchVariables);

        return $searchVariables;
    }

    protected function initRating()
    {
        $idSellerinfo = (int) $this->seller['id_sellerinfo'];
        $sellerinfoRatingRepository = $this->entityManager->getRepository(SellerinfoRating::class);
        $sellerinfoRatingCriterionRepository = $this->entityManager->getRepository(SellerinfoRatingCriterion::class);

        $avgSellerRating = $sellerinfoRatingRepository->getAverageBySellerInfo($idSellerinfo);
        $avgSellerRatingsCriterion = $sellerinfoRatingRepository->getAveragesCriterionBySellerInfo($idSellerinfo);

        $criterions = [];

        $langId = (int) $this->context->language->id;

        foreach ($sellerinfoRatingCriterionRepository->findAll() as $sellerinfoRatingCriterion) {
            $idSellerinfoRatingCriterion = $sellerinfoRatingCriterion->getIdSellerinfoRatingCriterion();
            $rating = isset($avgSellerRatingsCriterion[$idSellerinfoRatingCriterion])
                ? $avgSellerRatingsCriterion[$idSellerinfoRatingCriterion]
                : 0;

            $criterions[] = [
                'name' => $sellerinfoRatingCriterion->getLangByLangId($langId)->getName(),
                'rating' => $rating,
                'stars' => AgileHelperRating::getStarsImages($rating),
            ];
        }

        $this->context->smarty->assign([
            'avgSellerRating' => $avgSellerRating,
            'criterions' => $criterions,
            'feedbackList' => $this->getFeedbackList((int) $this->seller['id_sellerinfo']),
            'agileRatings' => $sellerinfoRatingRepository->getRatingsBySellerInfo($idSellerinfo),
        ]);
    }

    private function getFeedbackList($id_sellerinfo)
    {
        $feedbackList = null;
        $page = (int) Tools::getValue('feedbackListPage', 1);
        $perPage = 10;

        if ($page < 1) {
            $page = 1;
        }

        if (empty($this->entityManager)) {
            $this->entityManager = $this->container->get('doctrine.orm.entity_manager');
        }

        if (empty($this->seller)) {
            $sellerInfo = $this->entityManager->getRepository(Sellerinfo::class)->find($id_sellerinfo);

            if ($sellerInfo) {
                $this->context->smarty->assign([
                    'shop_name' => (new Shop($sellerInfo->getidShop()))->name,
                ]);
            }
        }

        $sellerinfoRatingRepository = $this->entityManager->getRepository(SellerinfoRating::class);

        if ($agileRatings = $sellerinfoRatingRepository
                ->getRatingsBySellerInfo(
                    (int) $id_sellerinfo,
                    $page,
                    $perPage
                )
        ) {
            $this->context->smarty->assign([
                'agileRatings' => $agileRatings,
                'agileRatingsPagination' => $this->getTemplateRatingVarPagination($id_sellerinfo, $page, $perPage),
            ]);

            $feedbackList = $this->context->smarty->fetch('module:agilemultisellerpro/views/templates/front/reviews/feedbackList.tpl');
        }

        return $feedbackList;
    }

    private function getTemplateRatingVarPagination($id_sellerinfo, $page, $perPage)
    {
        $totalRatingsCount = $this->entityManager->getRepository(SellerinfoRating::class)
            ->getTotalRatingsBySellerInfo((int) $id_sellerinfo);

        $pagination = new Pagination();
        $pagination
            ->setPage($page)
            ->setPagesCount(
                (int) ceil($totalRatingsCount / $perPage)
            )
        ;

        $itemsShownFrom = ($perPage * ($page - 1)) + 1;
        $itemsShownTo = $perPage * $page;

        $pages = array_map(function ($link) {
            $link['url'] = $link['page'] > 1 ? $link['page'] : null;

            return $link;
        }, $pagination->buildLinks());

        // Filter next/previous link on first/last page
        $pages = array_filter($pages, function ($page) use ($pagination) {
            if ('previous' === $page['type'] && 1 === $pagination->getPage()) {
                return false;
            }
            if ('next' === $page['type'] && $pagination->getPagesCount() === $pagination->getPage()) {
                return false;
            }

            return true;
        });

        return [
            'total_items' => $totalRatingsCount,
            'items_shown_from' => $itemsShownFrom,
            'items_shown_to' => ($itemsShownTo <= $totalRatingsCount) ? $itemsShownTo : $totalRatingsCount,
            'current_page' => $pagination->getPage(),
            'pages_count' => $pagination->getPagesCount(),
            'pages' => $pages,
            // Compare to 3 because there are the next and previous links
            'should_be_displayed' => (count($pagination->buildLinks()) > 3),
        ];
    }
}
