<?php

/**
 * @package         Regular Labs Library
 * @version         25.9.25453
 * 
 * @author          Peter van Westen <info@regularlabs.com>
 * @link            https://regularlabs.com
 * @copyright       Copyright © 2025 Regular Labs All Rights Reserved
 * @license         GNU General Public License version 2 or later
 */
namespace RegularLabs\Library;

defined('_JEXEC') or die;
use Joomla\CMS\Cache\CacheControllerFactoryInterface;
use Joomla\CMS\Cache\Controller\CallbackController;
use Joomla\CMS\Cache\Exception\CacheExceptionInterface;
use Joomla\CMS\Component\ComponentHelper as JComponentHelper;
use Joomla\CMS\Factory as JFactory;
use Joomla\CMS\Helper\ModuleHelper as JModuleHelper;
use Joomla\CMS\Installer\Installer as JInstaller;
use Joomla\CMS\Language\Text as JText;
use Joomla\CMS\Plugin\PluginHelper as JPluginHelper;
class Extension
{
    /**
     * Check if all extension types of a given extension are installed
     */
    public static function areInstalled(string $extension, array $types = ['plugin']): bool
    {
        foreach ($types as $type) {
            $folder = 'system';
            if (is_array($type)) {
                [$type, $folder] = $type;
            }
            if (!self::isInstalled($extension, $type, $folder)) {
                return \false;
            }
        }
        return \true;
    }
    public static function cleanCache($group = null)
    {
        $options = ['defaultgroup' => $group ?: JFactory::getApplication()->getInput()->get('option'), 'cachebase' => JFactory::getApplication()->get('cache_path', JPATH_CACHE)];
        try {
            /** @var CallbackController $cache */
            $cache = JFactory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController('callback', $options);
            $cache->clean();
        } catch (CacheExceptionInterface) {
            //
        }
    }
    public static function disable(string $alias, string $type = 'plugin', string $folder = 'system'): void
    {
        $element = self::getElementByAlias($alias);
        $element = match ($element) {
            'module' => 'mod_' . $element,
            'component' => 'com_' . $element,
            default => $element,
        };
        $db = \RegularLabs\Library\DB::get();
        $query = \RegularLabs\Library\DB::getQuery()->update(\RegularLabs\Library\DB::quoteName('#__extensions'))->set(\RegularLabs\Library\DB::quoteName('enabled') . ' = 0')->where(\RegularLabs\Library\DB::is('element', $element))->where(\RegularLabs\Library\DB::is('type', $type));
        if ($type == 'plugin') {
            $query->where(\RegularLabs\Library\DB::is('folder', $folder));
        }
        $db->setQuery($query);
        $db->execute();
    }
    /**
     * Return an alias and element name based on the given extension name
     */
    public static function getAliasAndElement(string &$name): array
    {
        $name = self::getNameByAlias($name);
        $alias = self::getAliasByName($name);
        $element = self::getElementByAlias($alias);
        return [$alias, $element];
    }
    public static function getAliasByName(string $name): string
    {
        $alias = \RegularLabs\Library\RegEx::replace('[^a-z0-9]', '', strtolower($name));
        return match ($alias) {
            'advancedmodules' => 'advancedmodulemanager',
            'what-nothing' => 'whatnothing',
            default => $alias,
        };
    }
    public static function getById(int|string $id): ?object
    {
        $db = \RegularLabs\Library\DB::get();
        $query = \RegularLabs\Library\DB::getQuery()->select(\RegularLabs\Library\DB::quoteName(['extension_id', 'manifest_cache']))->from(\RegularLabs\Library\DB::quoteName('#__extensions'))->where(\RegularLabs\Library\DB::is('extension_id', (int) $id));
        $db->setQuery($query);
        return $db->loadObject();
    }
    /**
     * Return an element name based on the given extension alias
     */
    public static function getElementByAlias(string $alias): string
    {
        $alias = self::getAliasByName($alias);
        return match ($alias) {
            'advancedmodulemanager' => 'advancedmodules',
            default => $alias,
        };
    }
    public static function getNameByAlias(string $alias): string
    {
        // Alias is a language string
        if (!str_contains($alias, ' ') && strtoupper($alias) == $alias) {
            return JText::_($alias);
        }
        // Alias has a space and/or capitals, so is already a name
        if (str_contains($alias, ' ') || $alias !== strtolower($alias)) {
            return $alias;
        }
        return JText::_(self::getXMLValue('name', $alias));
    }
    /**
     * Get the full path to the extension folder
     */
    public static function getPath(string $extension = 'plg_system_regularlabs', string $basePath = JPATH_ADMINISTRATOR, string $check_folder = ''): string
    {
        $basePath = $basePath ?: JPATH_SITE;
        if (!in_array($basePath, [JPATH_ADMINISTRATOR, JPATH_SITE], \true)) {
            return $basePath;
        }
        $extension = str_replace('.sys', '', $extension);
        switch (\true) {
            case str_starts_with($extension, 'mod_'):
                $path = 'modules/' . $extension;
                break;
            case str_starts_with($extension, 'plg_'):
                [$prefix, $folder, $name] = explode('_', $extension, 3);
                $path = 'plugins/' . $folder . '/' . $name;
                break;
            case str_starts_with($extension, 'com_'):
            default:
                $path = 'components/' . $extension;
                break;
        }
        $check_folder = $check_folder ? '/' . $check_folder : '';
        if (is_dir($basePath . '/' . $path . $check_folder)) {
            return $basePath . '/' . $path;
        }
        if (is_dir(JPATH_ADMINISTRATOR . '/' . $path . $check_folder)) {
            return JPATH_ADMINISTRATOR . '/' . $path;
        }
        if (is_dir(JPATH_SITE . '/' . $path . $check_folder)) {
            return JPATH_SITE . '/' . $path;
        }
        return $basePath;
    }
    /**
     * Return an extensions main xml array
     */
    public static function getXML(string $alias, string $type = '', string $folder = ''): array|false
    {
        $file = self::getXMLFile($alias, $type, $folder);
        if (!$file) {
            return \false;
        }
        return JInstaller::parseXMLInstallFile($file);
    }
    /**
     * Return an extensions main xml file name (including path)
     */
    public static function getXMLFile(string $alias, string $type = '', string $folder = '', bool $get_params = \false): string
    {
        $element = self::getElementByAlias($alias);
        $files = [];
        // Components
        if ($type == '' || $type == 'component') {
            $file = $get_params ? 'config' : $element;
            $files[] = JPATH_ADMINISTRATOR . '/components/com_' . $element . '/' . $file . '.xml';
            $files[] = JPATH_SITE . '/components/com_' . $element . '/' . $file . '.xml';
            if (!$get_params) {
                $files[] = JPATH_ADMINISTRATOR . '/components/com_' . $element . '/com_' . $element . '.xml';
                $files[] = JPATH_SITE . '/components/com_' . $element . '/com_' . $element . '.xml';
            }
        }
        // Plugins
        if ($type == '' || $type == 'plugin') {
            if ($folder != '') {
                $files[] = JPATH_PLUGINS . '/' . $folder . '/' . $element . '/' . $element . '.xml';
            }
            // System Plugins
            $files[] = JPATH_PLUGINS . '/system/' . $element . '/' . $element . '.xml';
            // Editor Button Plugins
            $files[] = JPATH_PLUGINS . '/editors-xtd/' . $element . '/' . $element . '.xml';
            // Field Plugins
            $field_name = \RegularLabs\Library\RegEx::replace('field$', '', $element);
            $files[] = JPATH_PLUGINS . '/fields/' . $field_name . '/' . $field_name . '.xml';
        }
        // Modules
        if ($type == '' || $type == 'module') {
            $files[] = JPATH_ADMINISTRATOR . '/modules/mod_' . $element . '/' . $element . '.xml';
            $files[] = JPATH_SITE . '/modules/mod_' . $element . '/' . $element . '.xml';
            $files[] = JPATH_ADMINISTRATOR . '/modules/mod_' . $element . '/mod_' . $element . '.xml';
            $files[] = JPATH_SITE . '/modules/mod_' . $element . '/mod_' . $element . '.xml';
        }
        foreach ($files as $file) {
            if (!file_exists($file)) {
                continue;
            }
            return $file;
        }
        return '';
    }
    /**
     * Return a value from an extensions main xml file based on the given key
     */
    public static function getXMLValue(string $key, string $alias, string $type = '', string $folder = ''): string
    {
        $xml = self::getXML($alias, $type, $folder);
        if (!$xml) {
            return '';
        }
        if (!isset($xml[$key])) {
            return '';
        }
        return $xml[$key] ?? '';
    }
    public static function isAuthorised(bool $require_core_auth = \true): bool
    {
        if (\RegularLabs\Library\User::isGuest()) {
            return \false;
        }
        if (!$require_core_auth) {
            return \true;
        }
        if (!\RegularLabs\Library\User::authorise('core.edit', 'com_content') && !\RegularLabs\Library\User::authorise('core.edit.own', 'com_content') && !\RegularLabs\Library\User::authorise('core.create', 'com_content')) {
            return \false;
        }
        return \true;
    }
    /**
     * Check if the Regular Labs Library is enabled
     */
    public static function isEnabled(string $extension, string $type = 'component', string $folder = 'system'): bool
    {
        $extension = strtolower($extension);
        if (!self::isInstalled($extension, $type, $folder)) {
            return \false;
        }
        switch ($type) {
            case 'component':
                $extension = str_replace('com_', '', $extension);
                return JComponentHelper::isEnabled('com_' . $extension);
            case 'module':
                $extension = str_replace('mod_', '', $extension);
                return JModuleHelper::isEnabled('mod_' . $extension);
            case 'plugin':
                return JPluginHelper::isEnabled($folder, $extension);
            default:
                return \false;
        }
    }
    public static function isEnabledInArea(object $params): bool
    {
        if (!isset($params->enable_frontend)) {
            return \true;
        }
        // Only allow in frontend
        if ($params->enable_frontend == 2 && \RegularLabs\Library\Document::isClient('administrator')) {
            return \false;
        }
        // Do not allow in frontend
        if (!$params->enable_frontend && \RegularLabs\Library\Document::isClient('site')) {
            return \false;
        }
        return \true;
    }
    public static function isEnabledInComponent(object $params): bool
    {
        if (!isset($params->disabled_components)) {
            return \true;
        }
        return !\RegularLabs\Library\Protect::isRestrictedComponent($params->disabled_components);
    }
    /**
     * Check if the Regular Labs Library is enabled
     */
    public static function isFrameworkEnabled(): bool
    {
        return JPluginHelper::isEnabled('system', 'regularlabs');
    }
    public static function isInstalled(string $extension, string $type = 'component', string $folder = 'system'): bool
    {
        $extension = strtolower($extension);
        switch ($type) {
            case 'component':
                $extension = str_replace('com_', '', $extension);
                return file_exists(JPATH_ADMINISTRATOR . '/components/com_' . $extension . '/' . $extension . '.xml') || file_exists(JPATH_SITE . '/components/com_' . $extension . '/' . $extension . '.xml');
            case 'plugin':
                return file_exists(JPATH_PLUGINS . '/' . $folder . '/' . $extension . '/' . $extension . '.xml');
            case 'module':
                $extension = str_replace('mod_', '', $extension);
                return file_exists(JPATH_ADMINISTRATOR . '/modules/mod_' . $extension . '/' . $extension . '.xml') || file_exists(JPATH_ADMINISTRATOR . '/modules/mod_' . $extension . '/mod_' . $extension . '.xml') || file_exists(JPATH_SITE . '/modules/mod_' . $extension . '/' . $extension . '.xml') || file_exists(JPATH_SITE . '/modules/mod_' . $extension . '/mod_' . $extension . '.xml');
            case 'library':
                $extension = str_replace('lib_', '', $extension);
                return is_dir(JPATH_LIBRARIES . '/' . $extension);
            default:
                return \false;
        }
    }
    public static function orderPluginFirst(string $name, string $folder = 'system'): void
    {
        $db = \RegularLabs\Library\DB::get();
        $query = \RegularLabs\Library\DB::getQuery()->select(['e.ordering'])->from(\RegularLabs\Library\DB::quoteName('#__extensions', 'e'))->where(\RegularLabs\Library\DB::is('e.type', 'plugin'))->where(\RegularLabs\Library\DB::is('e.folder', $folder))->where(\RegularLabs\Library\DB::is('e.element', $name));
        $db->setQuery($query);
        $current_ordering = $db->loadResult();
        if ($current_ordering == '') {
            return;
        }
        $query = \RegularLabs\Library\DB::getQuery()->select('e.ordering')->from(\RegularLabs\Library\DB::quoteName('#__extensions', 'e'))->where(\RegularLabs\Library\DB::is('e.type', 'plugin'))->where(\RegularLabs\Library\DB::is('e.folder', $folder))->where(\RegularLabs\Library\DB::like(\RegularLabs\Library\DB::quoteName('e.manifest_cache'), '%"author":"Regular Labs%'))->where(\RegularLabs\Library\DB::isNot('e.element', $name))->order('e.ordering ASC');
        $db->setQuery($query);
        $min_ordering = $db->loadResult();
        if ($min_ordering == '') {
            return;
        }
        if ($current_ordering < $min_ordering) {
            return;
        }
        if ($min_ordering < 1 || $current_ordering == $min_ordering) {
            $new_ordering = max($min_ordering, 1);
            $query = \RegularLabs\Library\DB::getQuery()->update(\RegularLabs\Library\DB::quoteName('#__extensions'))->set(\RegularLabs\Library\DB::quoteName('ordering') . ' = ' . $new_ordering)->where(\RegularLabs\Library\DB::is('ordering', $min_ordering))->where(\RegularLabs\Library\DB::is('type', 'plugin'))->where(\RegularLabs\Library\DB::is('folder', $folder))->where(\RegularLabs\Library\DB::isNot('element', $name))->where(\RegularLabs\Library\DB::like(\RegularLabs\Library\DB::quoteName('manifest_cache'), '%"author":"Regular Labs%'));
            $db->setQuery($query);
            $db->execute();
            $min_ordering = $new_ordering;
        }
        if ($current_ordering == $min_ordering) {
            return;
        }
        $new_ordering = $min_ordering - 1;
        $query = $db->getQuery(\true)->update(\RegularLabs\Library\DB::quoteName('#__extensions'))->set(\RegularLabs\Library\DB::quoteName('ordering') . ' = ' . $new_ordering)->where(\RegularLabs\Library\DB::is('type', 'plugin'))->where(\RegularLabs\Library\DB::is('folder', $folder))->where(\RegularLabs\Library\DB::is('element', $name));
        $db->setQuery($query);
        $db->execute();
    }
}
