<?php

/**
 * @package     VP Prime Framework
 *
 * @author      Abhishek Das <info@virtueplanet.com>
 * @link        https://www.virtueplanet.com
 * @copyright   Copyright (C) 2012-2025 Virtueplanet Services LLP. All Rights Reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

namespace Virtueplanet\Plugin\System\Prime\Helper;

use DOMDocument;
use DOMXPath;
use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Filesystem\Folder;
use Joomla\CMS\Filesystem\Path;
use Joomla\CMS\Uri\Uri;
use Joomla\Database\DatabaseInterface;
use Joomla\Database\ParameterType;
use Joomla\Registry\Registry;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
 * Template Helper class of VP Prime Framework.
 */
abstract class TemplateHelper
{
    /**
     * List of assets to be uploaded.
     *
     * @var   array
     */
    protected static $assetsToBeUnloaded = [];

    /**
     * List of template styles object
     *
     * @var   array
     */
    protected static $styles = [];

    /**
     * List of loaded packages.
     *
     * @var   array
     */
    protected static $packages = [];

    public static function getTemplateStyle($styleId)
    {
        $styleId = (int) $styleId;

        if (array_key_exists($styleId, self::$styles)) {
            return self::$styles[$styleId];
        }

        /**  @var DatabaseInterface $db */
        $db    = Factory::getContainer()->get(DatabaseInterface::class);
        $query = $db->getQuery(true);

        $query->select('*')
            ->from($db->quoteName('#__template_styles'))
            ->where($db->quoteName('id') . ' = :id')
            ->bind(':id', $styleId, ParameterType::INTEGER);

        $db->setQuery($query);

        $template = $db->loadObject();

        if (!empty($template)) {
            $temp = new Registry();

            $temp->loadString($template->params);

            $template->params = $temp;

            self::$styles[$styleId] = $template;
        } else {
            self::$styles[$styleId] = false;
        }

        return self::$styles[$styleId];
    }

    /**
     * Method to get the package information of a template.
     *
     * @param    integer   $styleId   The template style ID.
     *
     * @return   \stdClass|boolean   The package object or boolean false if not found.
     */
    public static function getPackage($styleId)
    {
        $styleId = (int) $styleId;

        if (array_key_exists($styleId, self::$packages)) {
            return self::$packages[$styleId];
        }

        /**  @var DatabaseInterface $db */
        $db    = Factory::getContainer()->get(DatabaseInterface::class);
        $query = $db->getQuery(true);

        $query->select('p.*')
            ->from($db->quoteName('#__extensions') . ' AS ' . $db->quoteName('p'))
            ->join('INNER', $db->quoteName('#__extensions') . ' AS ' . $db->quoteName('t') . ' ON ' . $db->quoteName('t.package_id') . ' = ' . $db->quoteName('p.extension_id'))
            ->join('INNER', $db->quoteName('#__template_styles') . ' AS ' . $db->quoteName('ts') . ' ON ' . $db->quoteName('ts.template') . ' = ' . $db->quoteName('t.element'))
            ->where($db->quoteName('t.type') . ' = ' . $db->quote('template'))
            ->where($db->quoteName('p.type') . ' = ' . $db->quote('package'))
            ->where($db->quoteName('t.enabled') . ' = ' . $db->quote('1'))
            ->where($db->quoteName('p.enabled') . ' = ' . $db->quote('1'))
            ->where($db->quoteName('t.client_id') . ' = ' . $db->quote('0'))
            ->where($db->quoteName('ts.id') . ' = ' . $db->quote($styleId));

        $db->setQuery($query);
        $package = $db->loadObject();

        if (empty($package)) {
            return false;
        }

        if (!empty($package->manifest_cache)) {
            $temp = new Registry();

            $temp->loadString($package->manifest_cache);

            $package->manifest_cache = $temp;
            $package->version        = $package->manifest_cache->get('version', 0);
        }

        if (!empty($package->params)) {
            $temp = new Registry();

            $temp->loadString($package->params);

            $package->params  = $temp;
            $package->pid     = $package->params->get('pid', 0);
            $package->did     = $package->params->get('did', '');
            $package->dlkData = UpdateHelper::decodeData($package->did);

            if (!empty($package->dlkData['last_checked']) && \DateTime::createFromFormat('Y-m-d H:i:s', $package->dlkData['last_checked'])) {
                $date        = Factory::getDate();
                $checkedDate = Factory::getDate($package->dlkData['last_checked']);

                $checkedDate->add(new \DateInterval('P7D'));

                $package->dlkData['revalidate'] = $checkedDate < $date ? 1 : 0;
            }
        }

        self::$packages[$styleId] = $package;

        return self::$packages[$styleId];
    }

    public static function isFullWidthView($selectedItems): bool
    {
        $app         = Factory::getApplication();
        $option      = strtolower($app->input->getCmd('option', ''));
        $view        = strtolower($app->input->getCmd('view', ''));
        $layout      = strtolower($app->input->getCmd('layout', ''));
        $isFullWidth = false;

        foreach ($selectedItems as $selectedItem) {
            $item = json_decode(base64_decode($selectedItem), true);

            $selectedOption = isset($item['option']) ? $item['option'] : '';
            $selectedView   = isset($item['view']) ? $item['view'] : '';
            $selectedLayout = isset($item['layout']) ? $item['layout'] : '';

            if ($option === $selectedOption && $view === $selectedView && $layout === $selectedLayout) {
                $isFullWidth = true;

                break;
            }
        }

        return $isFullWidth;
    }

    public static function getValidGridSize($size, $device = 'md'): string
    {
        $size   = (int) $size;
        $device = trim((string) $device);
        $valid  = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];

        if (in_array($size, $valid)) {
            return $device === 'xs' ? 'col-' . $size : 'col-' . $device . '-' . $size;
        }

        return $device === 'xs' ? 'd-none' : 'd-' . $device . '-none';
    }

    /**
     * Enqueue assets to be unloaded.
     *
     * @param  string  $type  Type of the asset i.e. script or stylesheet.
     * @param  string  $url   Relative url of the asset.
     *
     * @return  void
     */
    public static function enqueueToUnloadAssets(string $type, string $url): void
    {
        if (!isset(self::$assetsToBeUnloaded[$type])) {
            self::$assetsToBeUnloaded[$type] = [];
        }

        self::$assetsToBeUnloaded[$type][] = $url;
    }

    /**
     * Unload assets.
     *
     * @return  void
     */
    public static function unloadAssets(): void
    {
        /** @var \Joomla\CMS\Application\SiteApplication $app */
        $app = Factory::getApplication();

        /** @var \Joomla\CMS\Document\HtmlDocument $document */
        $document = $app->getDocument();
        $headData = $document->getHeadData();

        // VirtueMart does not use Web Asset Manager. So using old ways to unload assets.
        foreach (self::$assetsToBeUnloaded as $type => $toBeUnloadedUrls) {
            if (isset($headData[$type])) {
                foreach ($headData[$type] as $url => &$options) {
                    foreach ($toBeUnloadedUrls as $toBeUnloadedUrl) {
                        if (strpos($url, $toBeUnloadedUrl) !== false) {
                            unset($headData[$type][$url]);
                        }
                    }
                }
            }
        }

        $document->setHeadData($headData);
    }

    /**
     * Prepare HTML image element for lazy loading.
     *
     * @param   string  $html
     * @param   bool    $lazyLoad
     * @param   int     $circleDiameter
     * @param   int     $circleMargin
     * @param   int     $defaultWidth
     * @param   int     $defaultHeight
     * @param   string  $backgroundColor
     * @param   string  $fillColor
     *
     * @return  string
     */
    public static function prepareLazyImageHTML($html, $lazyLoad = false, $circleDiameter = 10, $circleMargin = 10, $defaultWidth = null, $defaultHeight = null, $backgroundColor = '#f7f7f7', $fillColor = '#222')
    {
        $width   = 100;
        $height  = 100;
        $addSize = false;

        if (empty($html)) {
            return $html;
        }

        if (is_numeric($defaultWidth) && $defaultWidth > 0 && is_numeric($defaultHeight) && $defaultHeight > 0) {
            $width  = $defaultWidth;
            $height = $defaultHeight;
        }

        $imageInfo = self::getImageInformationFromHTML($html);

        if (!empty($imageInfo) && !empty($imageInfo[0]) && !empty($imageInfo[1])) {
            $width   = round($imageInfo[0], 2);
            $height  = round($imageInfo[1], 2);
            $addSize = true;
        }

        $searchAttribute = 'src';

        if ($lazyLoad && strpos($html, 'data-src') === false) {
            $svg  = self::getLoadingSVGImage($width, $height, $circleDiameter, $circleMargin, $backgroundColor, $fillColor);
            $html = str_replace($searchAttribute . '=', 'src="data:image/svg+xml;base64,' . base64_encode($svg) . '" data-src=', $html);

            $searchAttribute = 'data-src';
        }

        if ($addSize && strpos($html, 'width=') === false) {
            $html = str_replace($searchAttribute . '=', 'width="' . $width . '" ' . $searchAttribute . '=', $html);
        }

        if ($addSize && strpos($html, 'height=') === false) {
            $html = str_replace($searchAttribute . '=', 'height="' . $height . '" ' . $searchAttribute . '=', $html);
        }

        return $html;
    }

    /**
     * Get image information from image HTML element
     *
     * @param   string  $html
     *
     * @return  array|false   Image information array. False in case of failure.
     */
    public static function getImageInformationFromHTML($html)
    {
        if (empty($html)) {
            return false;
        }

        $document = new DOMDocument();

        $document->loadHTML($html);

        $xpath = new DOMXPath($document);

        $src = $xpath->evaluate('string(//img/@src)');

        if (empty($src)) {
            return false;
        }

        $path     = $src;
        $base     = Uri::root(true);
        $baseFull = Uri::root();

        if ($baseFull && strpos($path, $baseFull) === 0) {
            $path = substr($path, strlen($baseFull));
        } elseif ($base && strpos($path, $base) === 0) {
            $path = substr($path, strlen($base));
        }

        $fileName = JPATH_ROOT . '/' . parse_url($path, PHP_URL_PATH);
        $fileName = Path::clean($fileName);

        if (!File::exists($fileName)) {
            return false;
        }

        return getimagesize($fileName);
    }

    /**
     * Get SVG loading image
     *
     * @param  int     $width            The width of the image is pixels.
     * @param  int     $height           The height of the image is pixels.
     * @param  int     $circleDiameter   The diameter of the circles.
     * @param  int     $circleMargin     The margin between the circles.
     * @param  string  $backgroundColor  The background color.
     * @param  string  $fillColor        The fill color.
     *
     * @return string  The SVG image string
     */
    public static function getLoadingSVGImage($width, $height, $circleDiameter = 10, $circleMargin = 10, $backgroundColor = '#f7f7f7', $fillColor = '#222')
    {
        $circleRadius    = $circleDiameter / 2;
        $totalInnerWidth = ($circleDiameter + $circleMargin) * 3;
        $xPosition       = (($width - $totalInnerWidth) / 2) + ($circleMargin + $circleRadius);
        $yPosition       = $height / 2;

        $svg = '<svg width="' . $width . '" height="' . $height . '" viewBox="0 0 ' . $width . ' ' . $height . '" xmlns="http://www.w3.org/2000/svg">';

        $svg .= '<rect width="100%" height="100%" fill="' . $backgroundColor . '" />';

        $svg .= '<circle fill="' . $fillColor . '" stroke="none" cx="' . $xPosition . '" cy="' . $yPosition . '" r="' . $circleRadius . '">';
        $svg .= '<animate attributeName="opacity" dur="1s" values="0; 1; 0" repeatCount="indefinite" begin="0.1"/>';
        //$svg .= '<animateTransform attributeName="transform" dur="1s" type="translate" values="0 15 ; 0 -15; 0 15" repeatCount="indefinite" begin="0.1"/>';
        $svg .= '</circle>';

        $xPosition += ($circleMargin + $circleRadius);

        $svg .= '<circle fill="' . $fillColor . '" stroke="none" cx="' . $xPosition . '" cy="' . $yPosition . '" r="' . $circleRadius . '">';
        $svg .= '<animate attributeName="opacity" dur="1s" values="0; 1; 0" repeatCount="indefinite" begin="0.2"/>';
        //$svg .= '<animateTransform attributeName="transform" dur="1s" type="translate" values="0 10 ; 0 -10; 0 10" repeatCount="indefinite" begin="0.2"/>';
        $svg .= '</circle>';

        $xPosition += ($circleMargin + $circleRadius);

        $svg .= '<circle fill="' . $fillColor . '" stroke="none" cx="' . $xPosition . '" cy="' . $yPosition . '" r="' . $circleRadius . '">';
        $svg .= '<animate attributeName="opacity" dur="1s" values="0; 1; 0" repeatCount="indefinite" begin="0.3"/>';
        //$svg .= '<animateTransform attributeName="transform" dur="1s" type="translate" values="0 5 ; 0 -5; 0 5" repeatCount="indefinite" begin="0.3"/>';
        $svg .= '</circle>';

        $svg .= '</svg>';

        return $svg;
    }

    /**
     * Get readable color on a given background color
     *
     * @param    string    $background   The background color in hexa
     * @param    string    $forDark      The color for dark background
     * @param    string    $forLight     The color for light background
     *
     * @return   string    The calculated color
     */
    public static function getReadableColour($background, $forDark = '#FFFFFF', $forLight = '#000000')
    {
        $background = trim(str_replace('#', '', $background));

        if (strlen($background) == 3) {
            $r = hexdec(str_repeat(substr($background, 0, 1), 2));
            $g = hexdec(str_repeat(substr($background, 1, 1), 2));
            $b = hexdec(str_repeat(substr($background, 2, 1), 2));
        } else {
            $r = hexdec(substr($background, 0, 2));
            $g = hexdec(substr($background, 2, 2));
            $b = hexdec(substr($background, 4, 2));
        }

        if (($r + $g + $b) > 382) {
            return $forLight;
        } else {
            return $forDark;
        }
    }

    /**
     * Method to get the clean current page Url for return.
     *
     * @param    boolean   $noTmpl  If tmpl parameter should be removed.
     *
     * @return   string    The clean URL
     */
    public static function getReturnUrl($noTmpl = true)
    {
        $uri = Uri::getInstance();

        $uri->delVar('virtuemart_user_id');
        $uri->delVar('virtuemart_userinfo_id');
        $uri->delVar('limitstart');

        if ($noTmpl) {
            $uri->delVar('tmpl');
        }

        return $uri->toString();
    }

    public static function getCSSVariables($params, $baseDir, $fieldsetName)
    {
        $manifestFile = $baseDir . '/templateDetails.xml';
        $variables    = [];

        if (!file_exists($manifestFile)) {
            throw new \Exception('Template manifest file not found.');
        }

        $manifest = simplexml_load_file($manifestFile);
        $config   = $manifest->config;
        $fieldset = false;

        foreach ($config->fields as $fields) {
            if ($fields->attributes()->name == 'params' && isset($fields->fieldset)) {
                $fieldset = self::getFieldset($fields, $fieldsetName);
            }
        }

        if (!$fieldset && !isset($fieldset->field)) {
            throw new \Exception('Fieldset of CSS variables not found in the template manifest file.');
        }

        foreach ($fieldset->field as $field) {
            $name    = (string) $field->attributes()->name;
            $default = (string) $field->attributes()->default;
            $value   = $params->get($name, $default);

            if (!$name || !$value) {
                continue;
            }

            $variables[$name] = $value;
        }

        return $variables;
    }

    public static function loadCustomAssets()
    {
        /** @var \Joomla\CMS\Application\SiteApplication $app */
        $app      = Factory::getApplication();
        $template = $app->getTemplate(true);

        if (!$template->params->get('auto_load_custom_assets', 0)) {
            return false;
        }

        self::registerAndUseCustomAssets($template, 'css');
        self::registerAndUseCustomAssets($template, 'js');
    }

    /**
     * Register and use custom assets automatically
     *
     * @param   object   $template   The template object
     * @param   string   $type       The type of asset (css or js)
     */
    protected static function registerAndUseCustomAssets($template, $type): void
    {
        /** @var \Joomla\CMS\Application\SiteApplication $app */
        $app             = Factory::getApplication();
        $document        = $app->getDocument();
        $wa              = $document->getWebAssetManager();
        $directory       = JPATH_SITE . '/media/templates/site/' . $template->template . '/' . $type . '/custom';
        $parentDirectory = null;
        $files           = [];

        if (Folder::exists($directory)) {
            $files = Folder::files($directory, '\.' . $type);
        }

        if ($template->parent) {
            $parentDirectory = JPATH_SITE . '/media/templates/site/' . $template->parent . '/' . $type . '/custom';

            if (Folder::exists($parentDirectory)) {
                $parentFiles = Folder::files($parentDirectory, '\.' . $type);
                $files       = array_merge($files, $parentFiles);
            }
        }

        $files = array_unique($files);

        foreach ($files as $file) {
            $version = 'auto';
            $name    = 'template.custom.' . basename($file, '.' . $type);

            if (function_exists('sha1_file')) {
                $filePath = $directory . '/' . $file;

                if (!File::exists($filePath) && $parentDirectory) {
                    $filePath = $parentDirectory . '/' . $file;
                }

                if (File::exists($filePath)) {
                    $version = sha1_file($filePath);
                }
            }

            if ($type === 'css') {
                $wa->registerAndUseStyle($name, 'custom/' . $file, ['version' => $version]);
            } elseif ($type === 'js') {
                $wa->registerAndUseScript($name, 'custom/' . $file, ['version' => $version]);
            }
        }
    }

    protected static function getFieldset($fields, $fieldsetName)
    {
        foreach ($fields->fieldset as $fieldset) {
            if (!count($fieldset->children())) {
                // Either the tag does not exist or has no children therefore we return zero fields processed.
                continue;
            }

            $name = (string) $fieldset->attributes()->name;

            if ($name == $fieldsetName) {
                return $fieldset;
            }

            if (isset($fieldset->fieldset)) {
                $result = self::getFieldset($fieldset, $fieldsetName);

                if ($result) {
                    return $result;
                }
            }
        }

        return false;
    }

    /**
     * Get the version from the manifest file.
     *
     * @param  string $manifestFile  The manifest file path.
     *
     * @return string
     */
    public static function getVersionFromManifest($manifestFile)
    {
        if (!file_exists($manifestFile)) {
            return 'unknown';
        }

        $manifest = simplexml_load_file($manifestFile);

        if (!isset($manifest->version)) {
            return 'unknown';
        }

        return (string) $manifest->version;
    }
}
