<?php
/**
 * @package SP Page Builder
 * @author JoomShaper http://www.joomshaper.com
 * @copyright Copyright (c) 2010 - 2024 JoomShaper
 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPLv2 or later
 */

namespace JoomShaper\SPPageBuilder\DynamicContent\Site;

use AddonParser;
use ApplicationHelper;
use DateTime;
use FieldsHelper;
use JLoader;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Version;
use JoomShaper\SPPageBuilder\DynamicContent\Constants\CollectionIds;
use JoomShaper\SPPageBuilder\DynamicContent\Models\CollectionField;
use JoomShaper\SPPageBuilder\DynamicContent\Models\CollectionItem;
use JoomShaper\SPPageBuilder\DynamicContent\Models\CollectionItemValue;
use JoomShaper\SPPageBuilder\DynamicContent\Models\Menu;
use JoomShaper\SPPageBuilder\DynamicContent\Models\Page;
use JoomShaper\SPPageBuilder\DynamicContent\Services\CollectionDataService;
use JoomShaper\SPPageBuilder\DynamicContent\Services\CollectionItemsService;
use JoomShaper\SPPageBuilder\DynamicContent\Supports\Arr;
use JoomShaper\SPPageBuilder\DynamicContent\Supports\Date;
use SppagebuilderHelperArticles;

class CollectionHelper
{
    /**
     * Cache for detail page IDs indexed by collection ID
     * 
     * @var array
     * 
     * @since 6.0.0
     */
    protected static $detailPageIdCache = [];

    /**
     * Cache for menu item IDs indexed by collection ID
     * 
     * @var array
     * 
     * @since 6.0.0
     */
    protected static $menuItemIdCache = [];

    /**
     * List of fields that are not prefixed with the collection title.
     *
     * @var array
     *
     * @since 5.5.0
     */
    public const NON_PREFIXED_FIELDS = [
        'created', 'modified', 'language', 'created_by', 'modified_by', 'published'
    ];

    /**
     * Get the selector from the CSS.
     *
     * @param string $css The CSS content.
     * @return string|null The selector or null if not found.
     *
     * @since 5.5.0
     */
    protected static function getSelector($css)
    {
        $pattern = "/^(.*?)\{/";
        preg_match($pattern, $css, $matches);
        return $matches[1] ?? null;
    }

    /**
     * Generate the CSS for the collection addon.
     *
     * @param object $addon The addon object.
     * @param object $layouts The layouts object.
     * @return array The generated CSS.
     *
     * @since 5.5.0
     */
    public static function generateDynamicContentCSS($addon, $layouts)
    {
        if (empty($addon->name))
        {
            return '';
        }

        $addonPath = AddonParser::getAddonPath($addon->name);
        $output = '';

        if (file_exists($addonPath . '/site.php'))
        {
            require_once $addonPath . '/site.php';

            $addonClassName = ApplicationHelper::generateSiteClassName($addon->name);
            $addonInstance = new $addonClassName($addon);

            $addonCss = $layouts->addon_css->render(array('addon' => $addon));

            if (method_exists($addonClassName, 'css'))
            {
                $css = $addonInstance->css();
                $addonSelector = static::getSelector($addonCss);
                $instanceSelector = static::getSelector($css);

                if (empty($addonSelector) || empty($instanceSelector)) {
                    return [];
                }

                return [
                    $addonSelector => $addonCss,
                    $instanceSelector => $css
                ];
            }
        }

        return $output;
    }

    /**
     * Get the detail page ID for the dynamic content collection.
     *
     * @param int $collectionId The collection ID.
     * @return int|null The detail page ID or null if not found.
     *
     * @since 5.5.0
     */
    protected static function getDetailPageId($collectionId)
    {
        if (empty($collectionId)) {
            return null;
        }

        if (array_key_exists($collectionId, static::$detailPageIdCache)) {
            return static::$detailPageIdCache[$collectionId];
        }

        $page = Page::where('extension', 'com_sppagebuilder')
            ->where('extension_view', 'dynamic_content:detail')
            ->where('view_id', $collectionId)
            ->first(['id']);

        $pageId = $page->id ?? null;
        static::$detailPageIdCache[$collectionId] = $pageId;

        return $pageId;
    }

    /**
     * Create the route url for the dynamic content detail page.
     *
     * @param object $item The item to create the route url for.
     * @return string|null The route url or null if not found.
     *
     * @since 5.5.0
     */
    public static function createRouteUrl($item)
    {
        $collectionId = $item['collection_id'];
        $itemId = $item['id'];
        $pageId = static::getDetailPageId($collectionId);

        if (empty($collectionId) || empty($itemId) || empty($pageId)) {
            return null;
        }

        if ($collectionId === CollectionIds::ARTICLES_COLLECTION_ID) {
            $menuItemId = static::getArticlesMenuItemId();
            $routeUrl = 'index.php?option=com_sppagebuilder&view=dynamic&collection_item_id=' . $itemId;
            $routeUrl .= '&collection_type=articles';
        } else if ($collectionId === CollectionIds::TAGS_COLLECTION_ID) {
            $menuItemId = static::getTagsMenuItemId();
            $routeUrl = 'index.php?option=com_sppagebuilder&view=dynamic&collection_item_id=' . $itemId;
            $routeUrl .= '&collection_type=tags';
        } else {
            $menuItemId = static::getCurrentMenuItemId($collectionId);
            $routeUrl = 'index.php?option=com_sppagebuilder&view=dynamic&collection_item_id=' . $itemId;
            $routeUrl .= '&collection_type=normal-source';
        }

        if (!empty($menuItemId)) {
            $routeUrl .= '&Itemid=' . $menuItemId;
        }

        return Route::_($routeUrl, false);
    }

    /**
     * Get the dynamic content data from the item.
     *
     * @param object $attribute The attribute to get the data from.
     * @param array $item The item to get the data from.
     * @return array|null The dynamic content data or null if not found.
     *
     * @since 5.5.0
     */
    public static function getDynamicContentData($attribute, $item)
    {
        if (empty($attribute) || empty($item)) {
            return null;
        }

        $path = $attribute->path ?? '';
        $segments = explode('.', $path);

        if (empty($segments) || !is_array($segments)) {
            $segments = [];
        }

        $value = $item;

        foreach ($segments as $segment) {
            $key = in_array($segment, static::NON_PREFIXED_FIELDS)
                ? $segment
                : CollectionItemsService::createFieldKey((int) $segment);

            if (is_array($value)) {
                if (!array_key_exists($key, $value)) {
                    $value = static::getReferenceValueByPath($value['id'], $segment);
                } else {
                    $value = $value[$key] ?? null;
                }
            }

            $value = is_object($value) ? (array) $value : $value;
        }

        if (is_array($value)) {
            if (array_key_exists('value', $value)) {
                $value = $value['value'];
            }
        }

        if (is_array($value) || is_object($value)) {
            return $value;
        }

        // Pick the value from the option store for the option type field.
        $optionStore = $item['option_store'] ?? [];

        if (isset($optionStore[$value])) {
            return $optionStore[$value];
        }

        return $value;
    }

    /**
     * When creating links for nested dynamic content fields, we need to get the parent object
     * that contains the actual link target, rather than the final field value.
     * 
     * For example, if we have a blog post with an author reference field:
     * {
     *     "id": 1,
     *     "field_1": "My Blog Post",              // title field
     *     "field_2": {                            // author reference field
     *         "id": 123,
     *         "field_3": "John Smith",            // author name field
     *         "field_4": "john@example.com"       // author email field
     *     }
     * }
     * 
     * And we want to link to the author's email (field_2.field_4), we need to return
     * the entire author object (field_2) as we need the author id to create the link to navigate there.
     * 
     * This method extracts the parent object by traversing the attribute path
     * and stopping at the second-to-last segment.
     *
     * @param array $item The item to prepare.
     * @param object $attribute The attribute to prepare the item for.
     * @return array|null The prepared item or null if the item is empty.
     *
     * @since 5.5.0
     */
    public static function prepareItemForLink($item, $attribute)
    {
        if (empty($item) || empty($attribute)) {
            return null;
        }

        $path = $attribute->path ?? '';
        $segments = explode('.', $path);

        if (empty($segments) || !is_array($segments)) {
            $segments = [];
        }

        // For single segment paths (direct fields), return the item itself
        if (count($segments) === 1) {
            return $item;
        }

        if (isset($item['collection_id']) && ($item['collection_id'] === CollectionIds::ARTICLES_COLLECTION_ID || $item['collection_id'] === CollectionIds::TAGS_COLLECTION_ID)) {
            return $item;
        }

        $value = $item;
        $length = count($segments);
        $segmentsUntilLast = array_slice($segments, 0, $length - 1);

        foreach ($segmentsUntilLast as $segment) {
            $key = in_array($segment, static::NON_PREFIXED_FIELDS)
                ? $segment
                : CollectionItemsService::createFieldKey((int) $segment);

            if (is_array($value)) {
                if (!array_key_exists($key, $value)) {
                    $value = static::getReferenceValueByPath($value['id'], $segment);

                    if (!empty($value['reference_item_id'])) {
                        $value = (new CollectionItemsService)->getCollectionItem($value['reference_item_id']);
                    }
                } else {
                    $value = $value[$key] ?? null;
                }
            }

            $value = is_object($value) ? (array) $value : $value;   
        }

        return $value;
    }

    /**
     * Get the collection item ID from the URL.
     *
     * @return int|null The collection item ID or null if not found.
     *
     * @since 5.5.0
     */
    public static function getCollectionItemIdFromUrl()
    {
        $input = Factory::getApplication()->input;
        $itemIds = $input->get('collection_item_id', [], 'ARRAY');

        if (empty($itemIds)) {
            return null;
        }

        return (int) $itemIds[count($itemIds) - 1];
    }

    public static function getDetailPageDataFromArticles()
    {
        $itemId = static::getCollectionItemIdFromUrl();
        
        if (empty($itemId)) {
            return null;
        }

        try {
            $db = Factory::getDbo();
            $authorised = \Joomla\CMS\Access\Access::getAuthorisedViewLevels(Factory::getUser()->get('id'));
            $baseUrl = rtrim(Uri::root(), '/');
            
            $query = $db->getQuery(true);
            $query->select([
                'a.id', 'a.title', 'a.alias', 'a.introtext', 'a.fulltext',
                'a.catid', 'a.created', 'a.created_by', 'a.publish_up',
                'a.images', 'a.attribs', 'a.language', 'a.featured', 'a.hits',
                'b.title as category', 'b.alias as category_alias',
                'u.name as username', 'u.email as created_by_email',
                'CASE WHEN p.profile_value IS NOT NULL THEN CONCAT(' . $db->quote($baseUrl) . ', JSON_UNQUOTE(p.profile_value)) ELSE NULL END as profile_image'
            ])
            ->from($db->quoteName('#__content', 'a'))
            ->join('LEFT', $db->quoteName('#__categories', 'b') . ' ON a.catid = b.id')
            ->join('LEFT', $db->quoteName('#__users', 'u') . ' ON u.id = a.created_by')
            ->join('LEFT', $db->quoteName('#__user_profiles', 'p') . ' ON p.user_id = a.created_by AND p.profile_key = ' . $db->quote('profileimage.profile_image'))
            ->where('a.id = ' . $db->quote($itemId))
            ->where('a.access IN (' . implode(',', $authorised) . ')');

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

            if (!$article) {
                return null;
            }

            $version = new Version();
            $JoomlaVersion = $version->getShortVersion();
            if ((float) $JoomlaVersion >= 4) {
                JLoader::registerAlias('FieldsHelper', 'Joomla\Component\Fields\Administrator\Helper\FieldsHelper');
            } else {
                JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php');
            }

            $custom_fields = FieldsHelper::getFields('com_content.article', $article);

            $article->introtext = $article->introtext ?? '';
            $article->fulltext = $article->fulltext ?? '';
            $article->hits = $article->hits ?? 0;

            if (!\class_exists('SppagebuilderHelperArticles')) {
                require_once JPATH_ROOT . '/components/com_sppagebuilder/helpers/articles.php';
            }
            \SppagebuilderHelperArticles::applyComContentCustomFieldValuesToItem($article, $custom_fields);

            $articleData = (array) $article;
            $articleData['collection_id'] = CollectionIds::ARTICLES_COLLECTION_ID;
            $articleData['slug'] = $article->id . ':' . $article->alias;
            $articleData['catslug'] = $article->catid . ':' . $article->category_alias;
            
            if (version_compare($JoomlaVersion, '4.0.0', '>=')) {
                $articleData['link'] = \Joomla\CMS\Router\Route::_(\Joomla\Component\Content\Site\Helper\RouteHelper::getArticleRoute($articleData['slug'], $article->catid, $article->language));
            } else {
                if (!class_exists('ContentHelperRoute')) {
                    require_once JPATH_SITE . '/components/com_content/helpers/route.php';
                }
                $articleData['link'] = \Joomla\CMS\Router\Route::_(\ContentHelperRoute::getArticleRoute($articleData['slug'], $article->catid, $article->language));
            }

            $articleData['introtext'] = self::replaceFieldShortcodes($article->introtext, $custom_fields);
            $articleData['fulltext'] = self::replaceFieldShortcodes($article->fulltext, $custom_fields);

            $attribs = json_decode($article->attribs ?? '{}');
            $feature_img = '';
            if (isset($attribs->helix_ultimate_image) && $attribs->helix_ultimate_image) {
                $feature_img = $attribs->helix_ultimate_image;
            } elseif (isset($attribs->spfeatured_image) && $attribs->spfeatured_image) {
                $feature_img = $attribs->spfeatured_image;
            }

            if (!empty($feature_img)) {
                $articleData['featured_image'] = $feature_img;
                $img_baseurl = basename($feature_img);

                $small = JPATH_ROOT . '/' . dirname($feature_img) . '/' . \Joomla\CMS\Filesystem\File::stripExt($img_baseurl) . '_small.' . \Joomla\CMS\Filesystem\File::getExt($img_baseurl);
                if (file_exists($small)) {
                    $articleData['image_small'] = Uri::root(true) . '/' . dirname($feature_img) . '/' . \Joomla\CMS\Filesystem\File::stripExt($img_baseurl) . '_small.' . \Joomla\CMS\Filesystem\File::getExt($img_baseurl);
                }

                $thumbnail = JPATH_ROOT . '/' . dirname($feature_img) . '/' . \Joomla\CMS\Filesystem\File::stripExt($img_baseurl) . '_thumbnail.' . \Joomla\CMS\Filesystem\File::getExt($img_baseurl);
                if (file_exists($thumbnail)) {
                    $articleData['image_thumbnail'] = Uri::root(true) . '/' . dirname($feature_img) . '/' . \Joomla\CMS\Filesystem\File::stripExt($img_baseurl) . '_thumbnail.' . \Joomla\CMS\Filesystem\File::getExt($img_baseurl);
                } else {
                    $articleData['image_thumbnail'] = Uri::root(true) . '/' . $articleData['featured_image'];
                }

                $medium = JPATH_ROOT . '/' . dirname($feature_img) . '/' . \Joomla\CMS\Filesystem\File::stripExt($img_baseurl) . '_medium.' . \Joomla\CMS\Filesystem\File::getExt($img_baseurl);
                if (file_exists($medium)) {
                    $articleData['image_medium'] = Uri::root(true) . '/' . dirname($feature_img) . '/' . \Joomla\CMS\Filesystem\File::stripExt($img_baseurl) . '_medium.' . \Joomla\CMS\Filesystem\File::getExt($img_baseurl);
                }

                $large = JPATH_ROOT . '/' . dirname($feature_img) . '/' . \Joomla\CMS\Filesystem\File::stripExt($img_baseurl) . '_large.' . \Joomla\CMS\Filesystem\File::getExt($img_baseurl);
                if (file_exists($large)) {
                    $articleData['image_large'] = Uri::root(true) . '/' . dirname($feature_img) . '/' . \Joomla\CMS\Filesystem\File::stripExt($img_baseurl) . '_large.' . \Joomla\CMS\Filesystem\File::getExt($img_baseurl);
                }
            } else {
                $articleData['featured_image'] = '';
                $articleData['image_thumbnail'] = '';
                $images = json_decode($article->images ?? '{}');
                if (isset($images->image_intro) && $images->image_intro) {
                    if (strpos($images->image_intro, 'http://') !== false || strpos($images->image_intro, 'https://') !== false) {
                        $articleData['image_thumbnail'] = $images->image_intro;
                    } else {
                        $articleData['image_thumbnail'] = Uri::root(true) . '/' . $images->image_intro;
                    }
                } elseif (isset($images->image_fulltext) && $images->image_fulltext) {
                    if (strpos($images->image_fulltext, 'http://') !== false || strpos($images->image_fulltext, 'https://') !== false) {
                        $articleData['image_thumbnail'] = $images->image_fulltext;
                    } else {
                        $articleData['image_thumbnail'] = Uri::root(true) . '/' . $images->image_fulltext;
                    }
                } else {
                    $articleData['image_thumbnail'] = false;
                }
            }

            $keysToAdd = [
                'image_small',
                'image_medium',
                'image_large',
                'image_intro',
                'image_intro_alt',
                'float_intro',
                'image_intro_caption',
                'image_fulltext',
                'image_fulltext_alt',
                'float_fulltext',
                'image_fulltext_caption'
            ];
            
            foreach ($keysToAdd as $key) {
                if (!isset($articleData[$key])) {
                    $articleData[$key] = '';
                }
            }

            if (isset($article->images)) {
                $images = json_decode($article->images);
                if (isset($images)) {
                    foreach ($images as $key => $value) {
                        $articleData[$key] = $value;
                    }
                }
            }

            $articleData['profile_image'] = $article->profile_image ?? '';
            if (empty($articleData['profile_image'])) {
                $enableGravatar = \Joomla\CMS\Component\ComponentHelper::getParams('com_sppagebuilder')->get('enable_gravatar', 1);
                if ($enableGravatar && !empty($article->created_by_email)) {
                    $hash = md5(strtolower(trim($article->created_by_email)));
                    $gravatarUrl = "https://www.gravatar.com/avatar/{$hash}?s=45&d=404";
                    $articleData['profile_image'] = $gravatarUrl;
                }
            }

            $layoutQuery = $db->getQuery(true);
            $layoutQuery->select($db->quoteName(['content', 'text', 'css']));
            $layoutQuery->from($db->quoteName('#__sppagebuilder'));
            $layoutQuery->where($db->quoteName('extension') . ' = ' . $db->quote('com_content'));
            $layoutQuery->where($db->quoteName('extension_view') . ' = ' . $db->quote('article'));
            $layoutQuery->where($db->quoteName('view_id') . ' = ' . (int) $article->id);
            $layoutQuery->where($db->quoteName('active') . ' = 1');
            $db->setQuery($layoutQuery);
            $layoutData = $db->loadObject();
            
            $articleData['layout'] = !empty($layoutData) ? $layoutData : null;
            $articleData['username'] = $article->username ?? '';
            $articleData['category'] = $article->category ?? '';
            
            return $articleData;
        } catch (\Exception $e) {
            Factory::getApplication()->enqueueMessage('Error fetching article detail: ' . $e->getMessage(), 'error');
            return null;
        }
    }

    private static function replaceFieldShortcodes($text, $custom_fields) {
		$fieldMap = [];
		foreach ($custom_fields as $field) {
			if (isset($field->id)) {
				$fieldMap[$field->id] = (isset($field->value) && $field->value) ? $field->value : '';
			}
		}
        
		return preg_replace_callback('/\{field\s+(\d+)\}/', function($matches) use ($fieldMap) {
			$fieldId = $matches[1];
            $value = $fieldMap[$fieldId] ?? '';
			return isset($fieldMap[$fieldId]) ? $fieldMap[$fieldId] : '';
		}, $text);
	}

    /**
     * Get the detail page data
     * 
     * @return array|null The data or null if not found
     * @since 6.0.0
     */
    public static function getDetailPageDataFromTags()
    {
        $itemId = static::getCollectionItemIdFromUrl();
        

        $db = \Joomla\CMS\Factory::getDbo();
        $query = $db->getQuery(true)
            ->select('*')
            ->from('#__tags')
            ->where('id = ' . (int) $itemId)
            ->where('published = 1');
        $db->setQuery($query);
        $tag = $db->loadObject();
        
        if ($tag) {

            $tagData = (array) $tag;
            $tagData['collection_id'] = CollectionIds::TAGS_COLLECTION_ID;
            

            $tagData['title'] = $tagData['title'] ?? '';
            $tagData['alias'] = $tagData['alias'] ?? '';
            $tagData['description'] = $tagData['description'] ?? '';
            
            return $tagData;
        }
        
        return null;
    }

    /**
     * Get the dynamic content item data from the database.
     *
     * @return array|null The dynamic content item data or null if not found.
     *
     * @since 5.5.0
     */
    public static function getDetailPageData()
    {
        $itemId = static::getCollectionItemIdFromUrl();

        if (empty($itemId)) {
            return null;
        }

        $service = new CollectionDataService();
        $item = $service->fetchCollectionItemById($itemId);

        return $item ?? null;
    }

    public static function getJoomlaSingleArticleRoute($item)
    {
        if (empty($item)) {
            return null;
        }
        return !empty($item['link']) ? $item['link'] : null;
    }

    /**
     * Create a dynamic content link.
     *
     * @param object $link The link object.
     * @param array $item The item to create the link for.
     * @return string|null The link or null if not found.
     *
     * @since 5.5.0
     */
    public static function createDynamicContentLink($link, $item)
    {
        if (empty($link) || empty($item)) {
            return null;
        }

        $linkType = $link->type ?? null;

        if (empty($linkType)) {
            return null;
        }

        switch ($linkType) {
            case 'page':
                $pageId = $link->page ?? null;
                if ($pageId === CollectionIds::ARTICLES_COLLECTION_ID) {
                    return static::getJoomlaSingleArticleRoute($item);
                }
                $page = !empty($pageId)
                    ? Page::where('id', $pageId)->first(['extension_view', 'view_id', 'id'])
                    : null;

                if (empty($page) || $page->isEmpty()) {
                    return null;
                }

                if ($page->extension_view === 'dynamic_content:detail') {
                    $routeUrl = 'index.php?option=com_sppagebuilder&view=dynamic';

                    if ($page->view_id === CollectionIds::ARTICLES_COLLECTION_ID) {
                        $routeUrl = static::getJoomlaSingleArticleRoute($item);
                        return Route::_($routeUrl, false);
                    } else if ($page->view_id === CollectionIds::TAGS_COLLECTION_ID) {
                        $menuItemId = static::getTagsMenuItemId();
                        if (!empty($menuItemId)) {
                            $routeUrl .= '&Itemid=' . $menuItemId;
                        }
                        return Route::_(static::buildRouteWithTagItemId($routeUrl, $item['id']), false);
                    } else {
                        $menuItemId = static::getCurrentMenuItemId($page->view_id);
                        if (!empty($menuItemId)) {
                            $routeUrl .= '&Itemid=' . $menuItemId;
                        }
                        return Route::_(static::buildRouteWithCollectionItemId($routeUrl, $item['id']), false);
                    }
                }

                $routeUrl = 'index.php?option=com_sppagebuilder&view=page&id=' . $page->id;
                return Route::_($routeUrl, false);
            case 'url':
                return $link->url ?? null;
            case 'menu':
                return Route::_($link->menu, false);
            case 'popup': // Implement popup link later
            default:
                return null;
        }
    }

    /**
     * Generate the link attributes.
     *
     * @param array $linkOptions The link options.
     * @return array The link attributes.
     *
     * @since 5.5.0
     */
    public static function generateLinkAttributes($linkOptions)
    {
        $url = $linkOptions['url'] ?? null;
        $target = $linkOptions['target'] ?? null;
        $nofollow = $linkOptions['nofollow'] ?? null;
        $noreferrer = $linkOptions['noreferrer'] ?? null;
        $noopener = $linkOptions['noopener'] ?? null;

        $attributes = [
            'href' => '',
            'target' => '',
            'rel' => '',
            'has_link' => false,
        ];

        if (!empty($url)) {
            $attributes['href'] = $url;
            $attributes['has_link'] = true;
        }

        if (!empty($target)) {
            $attributes['target'] = $target;
        }

        $rel = [];

        if (!empty($nofollow)) {
            $rel[] = 'nofollow';
        }

        if (!empty($noreferrer)) {
            $rel[] = 'noreferrer'; 
        }

        if (!empty($noopener)) {
            $rel[] = 'noopener';
        }

        if (!empty($rel)) {
            $attributes['rel'] = implode(' ', $rel);
        }

        return $attributes;
    }

    private static function phpToIcu(string $php): string
{
    $map = [
        'd' => 'dd', 'j' => 'd', 'D' => 'EEE', 'l' => 'EEEE',
        'm' => 'MM', 'n' => 'M', 'M' => 'MMM', 'F' => 'MMMM',
        'y' => 'yy', 'Y' => 'yyyy',
        'H' => 'HH', 'G' => 'H', 'h' => 'hh', 'g' => 'h',
        'i' => 'mm', 's' => 'ss',
        'A' => 'a', 'a' => 'a',
    ];
    return preg_replace_callback('/[djDlmnMFyYHGhgisAa]/', function($m) use ($map) {
        return $map[$m[0]] ?? $m[0];
    }, $php);
}

    /**
     * Format the date.
     *
     * @param string $date The date to format.
     * @param object $attribute The attribute to format the date for.
     * @return string|null The formatted date or null if not found.
     *
     * @since 5.5.0
     */
    public static function formatDate($date, $attribute)
    {
        $format = $attribute->date_format ?? 'd M, Y H:i A';
        if (empty($date) || empty($format)) {
            return $date;
        }

        if ($format === 'n-time-ago') {
            $date = Date::create($date);
            $now = Date::create('now');
            $interval = $now->diff($date);
            $minutes = $interval->days * 24 * 60 + $interval->h * 60 + $interval->i;

            if ($minutes === 0) {
                return Text::_('COM_SPPAGEBUILDER_DYNAMIC_CONTENT_JUST_NOW');
            } elseif ($minutes < 60) {
                return Text::plural('COM_SPPAGEBUILDER_DYNAMIC_CONTENT_MINUTES_AGO', $minutes);
            } elseif ($minutes < 1440) {
                $hours = floor($minutes / 60);
                return Text::plural('COM_SPPAGEBUILDER_DYNAMIC_CONTENT_HOURS_AGO', $hours);
            } elseif ($minutes < 10080) {
                $days = floor($minutes / 1440);
                return Text::plural('COM_SPPAGEBUILDER_DYNAMIC_CONTENT_DAYS_AGO', $days);
            } elseif ($minutes < 43200) {
                $weeks = floor($minutes / 10080);
                return Text::plural('COM_SPPAGEBUILDER_DYNAMIC_CONTENT_WEEKS_AGO', $weeks);
            } elseif ($minutes < 525600) {
                $months = floor($minutes / 43200);
                return Text::plural('COM_SPPAGEBUILDER_DYNAMIC_CONTENT_MONTHS_AGO', $months);
            } else {
                $years = floor($minutes / 525600);
                return Text::plural('COM_SPPAGEBUILDER_DYNAMIC_CONTENT_YEARS_AGO', $years);
            }
        }

        $format = $format === 'custom' ? $attribute->date_format_custom : $format;
        
        $lang = Factory::getLanguage()->getTag();
        $lang = str_replace('-', '_', $lang);
        $lang .= '@numbers=native';
        
        $dateObj = new DateTime($date);
        
        if(!class_exists('IntlDateFormatter')) {
            return $dateObj->format($format);
        }
        
        $format = self::phpToIcu($format);
        $formatter = new \IntlDateFormatter(
            $lang,         
            \IntlDateFormatter::NONE,      
            \IntlDateFormatter::NONE,
            $dateObj->getTimezone()->getName(),
            \IntlDateFormatter::GREGORIAN,
            $format
        );
    
        return $formatter->format($dateObj);
    }

    /**
     * Check if the field has a circular reference.
     * Circular reference means that the field is referencing itself.
     *
     * @param int $fieldId The field ID.
     * @return bool True if the field has a circular reference, false otherwise.
     *
     * @since 5.5.0
     */
    public static function hasCircularReference($fieldId)
    {
        $field = CollectionField::where('id', $fieldId)->first(['collection_id', 'reference_collection_id']);

        if ($field->isEmpty()) {
            return false;
        }

        return $field->collection_id === $field->reference_collection_id;
    }

    /**
     * Get the first collection item ID.
     *
     * @param integer $collectionId The collection ID.
     * @return int|null The first collection item ID or null if not found.
     *
     * @since 5.5.0
     */
    public static function getFirstCollectionItemId(int $collectionId)
    {

        if ($collectionId === CollectionIds::ARTICLES_COLLECTION_ID) {
            if (!\class_exists('SppagebuilderHelperArticles')) {
                require_once JPATH_ROOT . '/components/com_sppagebuilder/helpers/articles.php';
            }

            try {
                $articles = \SppagebuilderHelperArticles::getArticles(1, 'oldest');
                if (!empty($articles) && isset($articles[0])) {
                    return $articles[0]->id;
                }
            } catch (\Exception $e) {
                return null;
            }

            return null;
        }


        if ($collectionId === CollectionIds::TAGS_COLLECTION_ID) {
            try {
                $db = \Joomla\CMS\Factory::getDbo();
                $query = $db->getQuery(true)
                    ->select('id')
                    ->from('#__tags')
                    ->where('published = 1')
                    ->order('id ASC');
                $db->setQuery($query, 0, 1);
                $tagId = $db->loadResult();
                
                if ($tagId) {
                    return (int) $tagId;
                }
            } catch (\Exception $e) {
                return null;
            }

            return null;
        }

        // Handle regular collections
        $item = CollectionItem::where('collection_id', $collectionId)
            ->orderBy('id', 'ASC')
            ->first(['id']);

        if ($item->isEmpty()) {
            return null;
        }

        return $item->id;
    }

    /**
     * Prepare the image url for displaying.
     *
     * @param string $src The image source.
     * @return string|null The image URL or null if not found.
     *
     * @since 5.5.0
     */
    public static function getImageUrl($src)
    {
        if (empty($src)) {
            return null;
        }

        if (strpos($src, 'http') === 0) {
            return $src;
        }

        return Uri::root(true) . '/' . $src;
    }

    /**
     * Get the value by the path.
     *
     * @param int $itemId The item ID.
     * @param int $fieldId The field ID.
     * @return array|null The value or null if not found.
     *
     * @since 5.5.0
     */
    protected static function getReferenceValueByPath($itemId, $fieldId)
    {
        $item = CollectionItemValue::where('item_id', $itemId)
            ->where('field_id', $fieldId)
            ->first(['value', 'reference_item_id']);

        if ($item->isEmpty()) {
            return null;
        }

        $item->id = $item->reference_item_id ?? null;
        return $item->toArray();
    }

    /**
     * Build the route with the item ID.
     *
     * @param string $url The URL to build the route for.
     * @param int $itemId The item ID to build the route for.
     * @return string|null The built route or null if not found.
     *
     * @since 6.0.0
     */
    protected static function buildRouteWithArticleItemId($url, $articleId)
    {
        $currentRoute = Uri::getInstance($url);
        

        $currentRoute->setVar('collection_item_id', [$articleId]);
        $currentRoute->setVar('collection_type', 'articles');
        
        return $currentRoute->toString();
    }

    /**
     * Build the route with the tag item ID.
     *
     * @param string $url The URL to build the route for.
     * @param int $tagId The tag ID to build the route for.
     * @return string|null The built route or null if not found.
     *
     * @since 6.0.0
     */
    public static function buildRouteWithTagItemId($url, $tagId)
    {
        $currentRoute = Uri::getInstance($url);
        $currentRoute->setVar('collection_item_id', [$tagId]);
        $currentRoute->setVar('collection_type', 'tags');
        return $currentRoute->toString();
    }

    /**
     * Build the route with the collection item ID.
     *
     * @param string $url The URL to build the route for.
     * @param int $itemId The item ID to build the route for.
     * @return string|null The built route or null if not found.
     *
     * @since 5.5.0
     */
    public static function buildRouteWithCollectionItemId($url, $itemId)
    {
        $currentRoute = Uri::getInstance($url);
        $app = Factory::getApplication();
        $input = $app->input;
        $itemIds = $input->get('collection_item_id', [], 'ARRAY');
        

        $collectionIdOfItemToPush = static::getCollectionIdOfCollectionItem($itemId);
        $collectionType = 'normal-source';
        
        if (empty($itemIds)) {
            $itemIds = [];
            $itemIds[] = $itemId;
            $currentRoute->setVar('collection_item_id', $itemIds);
            $currentRoute->setVar('collection_type', $collectionType);
            return $currentRoute->toString();
        }

        $lastItemId = $itemIds[count($itemIds) - 1];
        $itemIdToPush = $itemId;

        $collectionIdOfLastItem = static::getCollectionIdOfCollectionItem($lastItemId);

        if ($collectionIdOfLastItem !== $collectionIdOfItemToPush) {
            $itemIds[] = $itemIdToPush;
        } else {
            $itemIds[count($itemIds) - 1] = $itemIdToPush;
        }

        $currentRoute->setVar('collection_item_id', $itemIds);
        $currentRoute->setVar('collection_type', $collectionType);
        return $currentRoute->toString();
    }

    /**
     * Get the collection ID of the collection item.
     *
     * @param int $itemId The item ID.
     * @return int|null The collection ID or null if not found.
     *
     * @since 5.5.0
     */
    protected static function getCollectionIdOfCollectionItem($itemId)
    {

        $item = CollectionItem::where('id', $itemId)->first(['collection_id']);

        if (!$item->isEmpty()) {
            return $item->collection_id;
        }

        return null;
    }

    /**
     * Get the menu item ID.
     *
     * @return int|null The menu item ID or null if not found.
     *
     * @since 6.0.0
     */
    protected static function getArticlesMenuItemId()
    {
        $pageId = Page::where(['extension_view' => 'dynamic_content:index', 'view_id' => CollectionIds::ARTICLES_COLLECTION_ID])->first(['id']);

        $pageId = !empty($pageId->id) ? $pageId->id : null;

        if (empty($pageId)) {
            return null;
        }

        $menuItems = Menu::whereLike('link', '%option=com_sppagebuilder&view=page&id=' . $pageId . '%')
            ->where('client_id', 0)
            ->where('published', 1)
            ->get(['link', 'id']);

        if (empty($menuItems)) {
            return null;
        }

        $app = Factory::getApplication();
        $menu = $app->getMenu();
        $activeMenuItem = $menu->getActive();
        $activeMenuItemId = $activeMenuItem->id ?? null;

        $menuItems = Arr::make($menuItems);
        $menuItem = $menuItems->find(function ($item) use ($activeMenuItemId) {
            $query = Uri::getInstance($item->link);
            if ($query->getVar('view') !== 'page') {
                return false;
            }
            $pageId = intval($query->getVar('id') ?? 0);
            $pageCollectionId = static::getCollectionIdFromPageId($pageId);

            return ($pageCollectionId === CollectionIds::ARTICLES_COLLECTION_ID) && ($item->id === $activeMenuItemId) ? $item->id : null;
        });

        return !empty($menuItem->id) ? $menuItem->id : null;
    }

    /**
     * Get the menu item ID.
     *
     * @return int|null The menu item ID or null if not found.
     *
     * @since 6.0.0
     */
    public static function getTagsMenuItemId()
    {
        $menuItems = Menu::whereLike('link', '%option=com_sppagebuilder%')
            ->where('client_id', 0)
            ->where('published', 1)
            ->get(['link', 'id']);

        if (empty($menuItems)) {
            return null;
        }

        $menuItems = Arr::make($menuItems);
        $menuItem = $menuItems->find(function ($item) {
            $query = Uri::getInstance($item->link);
            if ($query->getVar('view') !== 'page') {
                return false;
            }
            $pageId = intval($query->getVar('id') ?? 0);
            $pageCollectionId = static::getCollectionIdFromPageId($pageId);


            return $pageCollectionId === CollectionIds::TAGS_COLLECTION_ID;
        });

        if (empty($menuItem)) {
            /** @var CMSApplication */
            $app = Factory::getApplication();
            $input = $app->input;
            $itemId = $input->getInt('Itemid', 0);

            return $itemId ?: null;
        }

        return $menuItem->id;
    }

   /**
     * Get the current item ID from the URL.
     *
     * @param int $collectionId The collection ID.
     * @return int|null The current item ID or null if not found.
     *
     * @since 5.5.0
     */
    public static function getCurrentMenuItemId($collectionId)
    {
        if (array_key_exists($collectionId, static::$menuItemIdCache)) {
            return static::$menuItemIdCache[$collectionId];
        }

        $menuItems = Menu::whereLike('link', '%option=com_sppagebuilder%')
            ->where('client_id', 0)
            ->where('published', 1)
            ->get(['link', 'id']);

        if (empty($menuItems)) {
            static::$menuItemIdCache[$collectionId] = null;
            return null;
        }

        $menuItems = Arr::make($menuItems);
        $menuItem = $menuItems->find(function ($item) use ($collectionId) {
            $query = Uri::getInstance($item->link);
            if ($query->getVar('view') !== 'page') {
                return false;
            }
            $pageId = intval($query->getVar('id') ?? 0);
            $pageCollectionId = static::getCollectionIdFromPageId($pageId);

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

            return $pageCollectionId === (int) $collectionId;
        });

        if (empty($menuItem)) {
            /** @var CMSApplication */
            $app = Factory::getApplication();
            $input = $app->input;
            $itemId = $input->getInt('Itemid', 0);

            $result = $itemId ?: null;
            static::$menuItemIdCache[$collectionId] = $result;
            return $result;
        }

        static::$menuItemIdCache[$collectionId] = $menuItem->id;
        return $menuItem->id;
    }

    /**
     * Get the collection ID from the page ID.
     *
     * @param int $pageId The page ID.
     * @return int|null The collection ID or null if not found.
     *
     * @since 5.5.0
     */
    protected static function getCollectionIdFromPageId($pageId)
    {
        $page = Page::where('id', $pageId)->first(['extension_view', 'view_id', 'id']);

        if ($page->isEmpty()) {
            return null;
        }

        return $page->view_id ?? null;
    }
}
