<?php
/**
 * Magento 2 Google Analytics 4 (GA4) GTM
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Anowave license that is
 * available through the world-wide-web at this URL:
 * https://www.anowave.com/license-agreement/
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade this extension to newer
 * version in the future.
 *
 * @category 	Anowave
 * @package 	Anowave_Ec
 * @copyright 	Copyright (c) 2026 Anowave (https://www.anowave.com/)
 * @license  	https://www.anowave.com/license-agreement/
 */

namespace Anowave\Ec\Helper;

use Magento\Framework\Locale\Resolver;
use Magento\Framework\Data\Form\FormKey;
use Magento\Framework\Module\ModuleListInterface;
use Anowave\Ec\Model\Logger;
use Magento\Framework\App\ObjectManager;
use Magento\Directory\Model\CurrencyFactory;
use Magento\Framework\App\ResourceConnection;
use Magento\Catalog\Api\CategoryManagementInterface;
use Magento\Catalog\Model\CategoryFactory;
use Anowave\Ec\Helper\Constants;
use Anowave\Ec\Model\Cache;
use Anowave\Ec\Helper\Dom;
use Magento\Framework\App\ProductMetadataInterface;
use Magento\Framework\View\Page\Title;
use Anowave\Ec\Model\Cookie\Onetrust;
use Anowave\Ec\Model\Cookie\Cookiebot;
use Anowave\Ec\Model\Cookie\BuiltIn;

class Utils 
{
	/**
	 * @var Resolver
	 */
	protected $resolver;

    /**
     * @var FormKey
     */
    protected $formKey;

    /**
     * @var Logger
     */
    protected $logger;

    /**
     * @var moduleListInterface
     */
    protected $moduleListInterface;

    /**
     * @var \Magento\LoginAsCustomerApi\Api\GetLoggedAsCustomerAdminIdInterface
     */
    protected $getLoggedAsCustomerAdminId = null;

    /**
     * @var CurrencyFactory
     */
    protected $currencyFactory;

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

     /**
     * @var CategoryManagementInterface
     */
    protected $categoryManagementInterface;

    /**
     * @var Cache
     */
    protected $cache;

    /**
     * @var Dom
     */
    protected $dom;

    /**
     * @var ProductMetadataInterface
     */
    protected $productMetadataInterface;

    /**
     * @var Onetrust
     */
    protected $onetrust;

    protected Cookiebot $cookiebot;

    protected BuiltIn $cookieBuiltIn;

    /**
     * @var Title
     */
    protected $title;

    /**
     * @var ResourceConnection
     */
    private $resourceConnection;

    /**
     * Category tree
     */
    private $tree = [];

    /**
     * Contructor
     */
    public function __construct
    (
        Resolver $resolver,
        FormKey $formKey,
        Logger $logger,
        ModuleListInterface $moduleListInterface,
        CurrencyFactory $currencyFactory,
        ResourceConnection $resourceConnection,
        CategoryManagementInterface $categoryManagementInterface,
        CategoryFactory $categoryFactory,
        Cache $cache,
        Dom $dom,
        Title $title,
        ProductMetadataInterface $productMetadataInterface,
        Onetrust $onetrust,
        Cookiebot $cookiebot,
        BuiltIn $cookieBuiltIn
    )
    {
        $this->resolver = $resolver;
        $this->formKey = $formKey;
        $this->logger = $logger;
        $this->moduleListInterface = $moduleListInterface;
        $this->currencyFactory = $currencyFactory;
        $this->resourceConnection = $resourceConnection;
        $this->categoryManagementInterface = $categoryManagementInterface;
        $this->categoryFactory = $categoryFactory;
        $this->cache = $cache;
        $this->dom = $dom;
        $this->productMetadataInterface = $productMetadataInterface;
        $this->title = $title;
        $this->cookiebot = $cookiebot;
        $this->onetrust = $onetrust;
        $this->cookieBuiltIn = $cookieBuiltIn;

        if ($this->moduleListInterface->has('Magento_LoginAsCustomerApi'))
        {
            $this->getLoggedAsCustomerAdminId = ObjectManager::getInstance()->get(\Magento\LoginAsCustomerApi\Api\GetLoggedAsCustomerAdminIdInterface::class);
        }
    }

    /**
     * Get current locale
     * 
     * @return string
     */
    public function getLocale() : string
    {
        return $this->resolver->getLocale();
    }

    /**
     * Get form key 
     * 
     * @return string
     */
    public function getFormKey() : string
    {
        return $this->formKey->getFormKey();
    }

    /**
     * Get logger 
     * 
     * @return Logger
     */
    public function getLogger() : Logger
    {
        return $this->logger;
    }

    public function getCookieBuiltIn(string $name) : BuiltIn
    {
        return $this->cookieBuiltIn->setName($name);
    }

    public function getOnetrust(array $config = []) : Onetrust
    {
        return $this->onetrust->setConfig($config);
    }

    public function getCookiebot(array $config = []) : Cookiebot
    {
        return $this->cookiebot->setConfig($config);
    }

    /**
     * Get module list 
     * 
     * @return ModuleListInterface
     */
    public function getModuleList() : ModuleListInterface
    {
        return $this->moduleListInterface;
    }

    /**
     * Get admin id 
     * 
     * @return int
     */
    public function getAdminId(bool $logged = false) : int
    {
        if (is_null($this->getLoggedAsCustomerAdminId))
        {
            return 0;
        };

        if (!$logged)
        {
            return 0;
        }
        
        return (int) $this->getLoggedAsCustomerAdminId->execute();
    }

    /**
     * Get page name 
     * 
     * @return string
     */
    public function getPageName() : string 
    {
        return $this->title->getShort();
    }

    /**
     * Convert price to specific currency 
     * 
     * @param float $price
     * @param string $currencyCodeFrom
     * @param string $currencyCodeTo
     * 
     * @return [type]
     */
    public function convertPrice(float $price = 0, string $f = '', string $t = '')
    {   
        $rate = $this->getRate($f,$t);

        if ($rate)
        {
            $price *= $rate;
        }

        return (float) sprintf("%.2f", $price);
    }

    /**
     * Get conversion rate
     * 
     * @param string $from
     * @param string $to
     * 
     * @return float
     */
    public function getRate(string $f = '', string $t = '') : float
    {
        return (float) $this->currencyFactory->create()->load($f)->getAnyRate($t);
    }


    /**
     * Optimized way for retrieving mutiple reviews count
     * 
     * @param \Magento\Quote\Model\Quote $quote
     * 
     * @return [type]
     */
    public function getReviews(\Magento\Quote\Model\Quote $quote)
    {
        $connection = $this->resourceConnection->getConnection();

        $ids = [];

        foreach($quote->getAllVisibleItems() as $item)
        {
            $ids[(int) $item->getProduct()->getId()] = (int) $item->getProduct()->getId();
        }

        /**
         * Reviews array
         */
        $reviews = [];

        if ($ids)
        {
            $select = $connection->select()->from
            (
                ['reviews' => $this->resourceConnection->getTableName('review')],
                [
                    'entity_pk_value', 'reviews' => new \Zend_Db_Expr('count(review_id)')
                ]
            )
            ->where('entity_pk_value IN (:ids)')
            ->group('entity_pk_value');

            foreach($connection->fetchAll($select, [':ids' => join(chr(44),[1])]) as $row)
            {
                $reviews[$row['entity_pk_value']] = (int) $row['reviews'];
            }
        }

        return $reviews;
    }

    /**
     * Get rating 
     * 
     * @param \Magento\Catalog\Model\Product $product
     * 
     * @return float
     */
    public function getRating(\Magento\Catalog\Model\Product $product) : float
    {
        $connection = $this->resourceConnection->getConnection();

        $select = $connection->select()->from
        (
            ['vote' => $this->resourceConnection->getTableName('rating_option_vote')],
            [
                'vote' => new \Zend_Db_Expr('SUM(percent)')
            ]
        )
        ->where('entity_pk_value = :id');

        $rating =  (float) $connection->fetchOne($select, [':id' => $product->getid()]);

        return $rating;
    }

    /**
     * Get tree 
     * 
     * @param int $store_id
     * 
     * @return array
     */
    public function getTree(int $store_id = 0) : array
    {
        if (!$this->tree)
        {
            $key = (Constants::EDITION_COMMUNITY === $this->productMetadataInterface->getEdition()) ? 'entity_id':'row_id';

            $connection = $this->resourceConnection->getConnection();

            $select = $connection->select()->from
            (
                [
                    'e' => $this->resourceConnection->getTableName('catalog_category_entity')
                ],
                [
                    'e' => new \Zend_Db_Expr('DISTINCT e.entity_id'),
                    'p' => new \Zend_Db_Expr('e.path'),
                    'v' => new \Zend_Db_Expr('v.value')
                ]
            )
            ->joinLeft(
                
                [
                    'i' => $this->resourceConnection->getTableName('catalog_category_entity_int')
                ],
                'i.' . $key . ' = e.entity_id',[]
            )
            ->joinLeft(
                
                [
                    'v' => $this->resourceConnection->getTableName('catalog_category_entity_varchar')
                ],
                'v.' . $key . ' = e.entity_id',[]
            )
            ->joinLeft(
                
                [
                    'a' => $this->resourceConnection->getTableName('eav_attribute')
                ],
                'a.attribute_id = v.attribute_id',[]
            )
            ->joinLeft(
                
                [
                    'et' => $this->resourceConnection->getTableName('eav_entity_type')
                ],
                'et.entity_type_id = a.entity_type_id',[]
            )
            ->where('v.store_id = ' . (int) $store_id . ' AND et.entity_type_code = "catalog_category" AND a.attribute_code = "name" AND e.level >= 1');
            
            $rows = $connection->fetchAll($select);
            
            /**
             * Category name map
             */
            $map = [];

            foreach($rows as $row)
            {
                $map[$row['e']] = $row['v'];
            }

            foreach($rows as $row)
            {
                $segments = [];
                
                foreach(explode(Constants::CATEGORY_SEPARATOR, $row['p']) as $id)
                {
                    if (array_key_exists($id, $map))
                    {
                        $segments[] = $map[$id];
                    }
                }

                if ($segments)
                {
                    array_shift($segments);
                }

                $category = $this->categoryFactory->create();

                $category->setId($row['e']);
                $category->setName($row['v']);
                $category->setSegments
                (
                    join(Constants::CATEGORY_SEPARATOR, $segments)
                );

                /**
                 * Emulate parent categories
                 */
                $category->setParentCategories([]);
                
                $this->tree[(int) $category->getId()] = $category;
            }
        }

        return $this->tree;
    }


    /**
	 * Get DOM wrappers
	 * 
	 * @return array
	 */
	public function getDom() : array
	{
	    return 
	    [
	        new \Anowave\Ec\Model\Dom('1.0','utf-8'), 
	        new \Anowave\Ec\Model\Dom('1.0','utf-8')
	    ];
	}


    /**
     * Alter TikTok snippet
     * 
     * @param string $script
     * @param string $nonce
     * @param bool $consent
     * @param bool $lazyload
     * 
     * @return string
     */
    public function alterTikTokSnippet(string $script = '', string $nonce = '', bool $consent = false, bool $lazyload = false) : string
    {
        list($doc, $dom) = $this->getDom();
	    
	    $dom->loadHTML($script);
	    
	    $query = new \DOMXPath($dom);
	    
	    foreach ($query->query('//script') as $element)
	    {
	        $element->setAttribute('nonce', $nonce);
            $element->setAttribute('data-ommit', true);

            if ($consent && !$element->getAttribute('data-consent'))
            {
                /**
                 * Set data consent
                 */
                $element->setAttribute('data-consent', Constants::COOKIE_CONSENT_GRANTED_EVENT);

                /**
                 * Change type
                 */
                $element->setAttribute('type', Constants::SCRIPT_TYPE);
            }
            
            if ($lazyload)
            {
                $element->setAttribute('type', Constants::SCRIPT_TYPE_LAZYLOAD);             
            }
	    }

        $snippet = $this->dom->getDOMContent($dom, $doc);

	    return $snippet;
    }

    /**
     * Alter head snippet 
     * 
     * @param string $script
     * @param string $nonce
     * 
     * @return string
     */
    public function alterSnippet(string $script = '', string $nonce = '', bool $consent = false, bool $lazyload = false) : string
    {
        list($doc, $dom) = $this->getDom();
	    
	    $dom->loadHTML($script);
	    
	    $query = new \DOMXPath($dom);
	    
	    foreach ($query->query('//script') as $element)
	    {
	        $element->setAttribute('nonce', $nonce);

            if ($consent && !$element->getAttribute('data-consent'))
            {
                /**
                 * Set data consent
                 */
                $element->setAttribute('data-consent', Constants::COOKIE_CONSENT_GRANTED_EVENT);

                /**
                 * Change type
                 */
                $element->setAttribute('type', Constants::SCRIPT_TYPE);
            }
            
            if ($lazyload)
            {
                $element->setAttribute('type', Constants::SCRIPT_TYPE_LAZYLOAD);             
            }
	    }

        $snippet = $this->dom->getDOMContent($dom, $doc);

        /**
         * Add a j.nonce
         */
        $snippet = str_replace("j.src=","j.nonce='{$nonce}';j.src=",$snippet);

	    return $snippet;
    }

    /**
     * Alter Gtag Snippet
     * 
     * @param string $script
     * @param string $nonce
     * 
     * @return string
     */
    public function alterGtagSnippet(string $script = '', string $nonce = '') : string
    {
        list($doc, $dom) = $this->getDom();
	    
	    $dom->loadHTML($script);

        $query = new \DOMXPath($dom);
	    
	    foreach ($query->query('//script') as $element)
	    {
	        $element->setAttribute('nonce', $nonce);
            $element->setAttribute('data-ommit', json_encode(true));
	    }

        $snippet = $this->dom->getDOMContent($dom, $doc);

        return $snippet;
    }
}