<?php
namespace Grav\Plugin;

use Composer\Autoload\ClassLoader;
use Grav\Common\Assets;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Plugin;
use Grav\Common\Utils;
use Grav\Plugin\ShortcodeCore\ShortcodeManager;
use Grav\Plugin\ShortcodeCore\ShortcodeTwigVar;
use RocketTheme\Toolbox\Event\Event;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
use Twig\TwigFilter;


class ShortcodeCorePlugin extends Plugin
{
    /** @var  ShortcodeManager $shortcodes */
    protected $shortcodes;

    /**
     * @return array
     */
    public static function getSubscribedEvents()
    {
        return [
            'onPluginsInitialized' => [
                ['autoload', 100001],
                ['onPluginsInitialized', 10]
            ],
            'registerNextGenEditorPlugin' => [
                ['registerNextGenEditorPlugin', 0],
                ['registerNextGenEditorPluginShortcodes', 0],
            ],
            'registerEditorProPlugin' => [
                ['registerEditorProPlugin', 0],
            ],
            'onEditorProShortcodeRegister' => [
                ['onEditorProShortcodeRegister', 0],
            ]
        ];
    }

    /**
     * [onPluginsInitialized:100000] Composer autoload.
     *
     * @return ClassLoader
     */
    public function autoload()
    {
        return require __DIR__ . '/vendor/autoload.php';
    }

    /**
     * Initialize configuration
     */
    public function onPluginsInitialized()
    {
        $this->config = $this->grav['config'];

        $forceFrontend = (bool)($this->grav['shortcode_force_frontend'] ?? false);

        // don't continue if this is admin and plugin is disabled for admin
        if (!$forceFrontend && !$this->config->get('plugins.shortcode-core.active_admin') && $this->isAdmin()) {
            return;
        }

        $this->enable([
            'onThemeInitialized'        => ['onThemeInitialized', 0],
            'onMarkdownInitialized'     => ['onMarkdownInitialized', 0],
            'onShortcodeHandlers'       => ['onShortcodeHandlers', 0],
            'onPageContentRaw'          => ['onPageContentRaw', 0],
            'onPageContentProcessed'    => ['onPageContentProcessed', -10],
            'onPageContent'             => ['onPageContent', 0],
            'onTwigInitialized'         => ['onTwigInitialized', 0],
            'onTwigTemplatePaths'       => ['onTwigTemplatePaths', 0],
        ]);

        $this->grav['shortcode'] = $this->shortcodes = new ShortcodeManager();
    }

    /**
     * Theme initialization is best place to fire onShortcodeHandler event
     * in order to support both plugins and themes
     */
    public function onThemeInitialized()
    {
        $this->grav->fireEvent('onShortcodeHandlers');
    }

    /**
     * Handle the markdown Initialized event by setting up shortcode block tags
     *
     * @param  Event  $event the event containing the markdown parser
     */
    public function onMarkdownInitialized(Event $event)
    {
        $this->shortcodes->setupMarkdown($event['markdown']);
    }

    /**
     * Process shortcodes before Grav's processing
     *
     * @param Event $e
     */
    public function onPageContentRaw(Event $e)
    {
        $this->processShortcodes($e['page'], 'processRawContent');
    }

    /**
     * Process shortcodes after Grav's processing, but before caching
     *
     * @param Event $e
     */
    public function onPageContentProcessed(Event $e)
    {
        $this->processShortcodes($e['page'], 'processContent');
    }

    /**
     * @param PageInterface $page
     * @param string $type
     */
    protected function processShortcodes(PageInterface $page, $type = 'processContent') {
        $meta = [];
        $this->shortcodes->resetObjects(); // clear shortcodes that may have been processed in this execution thread before
        $config = $this->mergeConfig($page);

        // Don't run in admin pages other than content
        $admin_pages_only = $config['admin_pages_only'] ?? true;
        if ($admin_pages_only && $this->isAdmin() && !Utils::startsWith($page->filePath(), $this->grav['locator']->findResource('page://'))) {
            return;
        }

        $this->active = $config->get('active', true);

        // if the plugin is not active (either global or on page) exit
        if (!$this->active) {
            return;
        }

        // process the content for shortcodes
        $page->setRawContent($this->shortcodes->$type($page, $config));

        // if objects found set them as page content meta
        $shortcode_objects = $this->shortcodes->getObjects();
        if (!empty($shortcode_objects)) {
            $meta['shortcode'] = $shortcode_objects;
        }

        // if assets founds set them as page content meta
        $shortcode_assets = $this->shortcodes->getAssets();
        if (!empty($shortcode_assets)) {
            $meta['shortcodeAssets'] = $shortcode_assets;
        }

        // if we have meta set, let's add it to the content meta
        if (!empty($meta)) {
            $page->addContentMeta('shortcodeMeta', $meta);
        }
    }

    /**
     * @param PageInterface $page
     * @return \Grav\Common\Data\Data
     */
    protected function getConfig(PageInterface $page)
    {
        $config = $this->mergeConfig($page);
        $this->active = false;

        // Don't run in admin pages other than content
        $admin_pages_only = isset($config['admin_pages_only']) ? $config['admin_pages_only'] : true;
        if ($admin_pages_only &&
            $this->isAdmin() &&
            !Utils::startsWith($page->filePath(), $this->grav['locator']->findResource('page://'))) {

        } else {
            $this->active = $config->get('active', true);
        }

        return $config;
    }

    /**
     * Handle the assets that might be associated with this page
     */
    public function onPageContent(Event $event)
    {
        if (!$this->active) {
            return;
        }

        $page = $event['page'];

        // get the meta and check for assets
        $page_meta = $page->getContentMeta('shortcodeMeta');

        if (is_array($page_meta)) {
            if (isset($page_meta['shortcodeAssets'])) {

                $page_assets = (array) $page_meta['shortcodeAssets'];

                /** @var Assets $assets */
                $assets = $this->grav['assets'];
                // if we actually have data now, add it to asset manager
                foreach ($page_assets as $type => $asset) {
                    foreach ($asset as $item) {
                        $method = 'add'.ucfirst($type);
                        if (is_array($item)) {
                            $assets->$method($item[0], $item[1]);
                        } else {
                            $assets->$method($item);
                        }
                    }
                }
            }
        }
    }

    /**
     * Event that handles registering handler for shortcodes
     */
    public function onShortcodeHandlers()
    {
        $include_default_shortcodes = $this->config->get('plugins.shortcode-core.include_default_shortcodes', true);
        if ($include_default_shortcodes) {
            $this->shortcodes->registerAllShortcodes(__DIR__ . '/classes/shortcodes', ['ignore' => ['Shortcode', 'ShortcodeObject']]);
            $this->unregisterExcludedShortcodes();
        }

        // Add custom shortcodes directory if provided
        $custom_shortcodes = $this->config->get('plugins.shortcode-core.custom_shortcodes');
        if (isset($custom_shortcodes)) {
            $this->shortcodes->registerAllShortcodes(GRAV_ROOT . $custom_shortcodes);
        }

        // Register config-defined shortcodes (the "shortcode builder" — no PHP class needed)
        $this->registerConfigShortcodes();
    }

    /**
     * Drop individual tags from the built-in set.
     *
     * `include_default_shortcodes` is all or nothing, and the `ignore` list
     * registerAllShortcodes() takes is by PHP class rather than by tag: one
     * class can register six tags ([h1] to [h6]), and a class name does not
     * always match the tag it registers. So the tags come off after
     * registration, named the way a site actually writes them in its config.
     *
     * An excluded tag stops being parsed and its brackets render as literal
     * text, which is what already happens with the whole set switched off.
     * Requested in getgrav/grav#4288.
     *
     * @return void
     */
    protected function unregisterExcludedShortcodes()
    {
        $excluded = $this->config->get('plugins.shortcode-core.exclude_default_shortcodes');
        if (!is_array($excluded)) {
            return;
        }

        $containers = [$this->shortcodes->getHandlers(), $this->shortcodes->getRawHandlers()];

        foreach ($excluded as $name) {
            $name = trim((string) $name);
            if ($name === '') {
                continue;
            }

            foreach ($containers as $handlers) {
                if ($handlers->has($name)) {
                    $handlers->remove($name);
                }
            }
        }
    }

    /**
     * Register shortcodes defined in config, each backed by a Twig template or
     * an inline Twig output string — the "shortcode builder". Lets a site add
     * its own shortcodes without writing a PHP class or a plugin.
     *
     * Config (`plugins.shortcode-core.shortcodes`) is a list of definitions,
     * each `{ name, template? | output? }`. A keyed map (tag => definition) is
     * also accepted. The template/output is trusted code (it lives on disk or
     * in admin-authored config, not in page content), and the author-supplied
     * `params`/`content` it receives are auto-escaped by Twig on output — so
     * this stays inside the Grav 2 content-security model.
     */
    protected function registerConfigShortcodes(): void
    {
        $defined = $this->config->get('plugins.shortcode-core.shortcodes');
        if (!is_array($defined)) {
            return;
        }

        $twig = $this->grav['twig'];

        foreach ($defined as $key => $def) {
            if (!is_array($def)) {
                continue;
            }

            // `name` field (list form) or the map key (keyed form).
            $name = trim((string) ($def['name'] ?? (is_string($key) ? $key : '')));
            $template = trim((string) ($def['template'] ?? ''));
            $output = (string) ($def['output'] ?? '');

            if ($name === '' || ($template === '' && $output === '')) {
                continue;
            }

            $this->shortcodes->getHandlers()->add($name, static function (ShortcodeInterface $sc) use ($twig, $template, $output) {
                $vars = [
                    'params'    => $sc->getParameters(),
                    'content'   => $sc->getContent(),
                    'shortcode' => $sc,
                ];

                // A template file wins over inline output when both are set.
                if ($template !== '') {
                    return $twig->processTemplate($template, $vars);
                }

                return $twig->processString($output, $vars);
            });
        }
    }

    /**
     * Add a twig filter for processing shortcodes in templates
     */
    public function onTwigInitialized()
    {
        $this->grav['twig']->twig()->addFilter(new TwigFilter('shortcodes', [$this->shortcodes, 'processShortcodes']));
        $this->grav['twig']->twig_vars['shortcode'] = new ShortcodeTwigVar();
    }

    public function onTwigTemplatePaths()
    {
        $this->grav['twig']->twig_paths[] = __DIR__ . '/templates';
    }

    public function registerNextGenEditorPlugin($event) {
        $config = $this->config->get('plugins.shortcode-core.nextgen-editor');
        $plugins = $event['plugins'];

        if ($config['env'] !== 'development') {
            $plugins['css'][] = 'plugin://shortcode-core/nextgen-editor/dist/css/app.css';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/dist/js/app.js';
        } else {
            $plugins['js'][]  = 'http://' . $config['dev_host'] . ':' . $config['dev_port'] . '/js/app.js';
        }

        $event['plugins']  = $plugins;
        return $event;
    }

    public function registerNextGenEditorPluginShortcodes($event) {
        $include_default_shortcodes = $this->config->get('plugins.shortcode-core.include_default_shortcodes', true);
        if ($include_default_shortcodes) {
            $plugins = $event['plugins'];

            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/shortcode-core.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/align/align.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/color/color.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/columns/columns.js';
            $plugins['css'][] = 'plugin://shortcode-core/nextgen-editor/shortcodes/details/details.css';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/details/details.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/div/div.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/figure/figure.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/fontawesome/fontawesome.js';
            $plugins['css'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/headers/headers.css';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/headers/headers.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/language/language.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/lorem/lorem.js';
            $plugins['css'][] = 'plugin://shortcode-core/nextgen-editor/shortcodes/mark/mark.css';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/mark/mark.js';
            $plugins['css'][] = 'plugin://shortcode-core/nextgen-editor/shortcodes/notice/notice.css';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/notice/notice.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/raw/raw.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/safe-email/safe-email.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/section/section.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/size/size.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/span/span.js';
            $plugins['js'][]  = 'plugin://shortcode-core/nextgen-editor/shortcodes/u/u.js';

            $event['plugins']  = $plugins;
        }
        return $event;
    }

    public function registerEditorProPlugin($event) {
        $plugins = $event['plugins'];
        
        // Add Editor Pro shortcode integration JavaScript
        $plugins['js'][] = 'plugin://shortcode-core/editor-pro/shortcode-integration.js';
        
        $event['plugins'] = $plugins;
        return $event;
    }

    public function onEditorProShortcodeRegister($event) {
        // Only offer tags that will actually parse. With the built-in set
        // switched off, or a tag excluded, inserting one from the picker just
        // leaves raw bracket text on the page. The older NextGen editor path
        // has always checked this; this one never did (getgrav/grav#4288).
        if (!$this->config->get('plugins.shortcode-core.include_default_shortcodes', true)) {
            return $event;
        }

        $excluded = $this->config->get('plugins.shortcode-core.exclude_default_shortcodes');
        $excluded = is_array($excluded) ? array_map('trim', $excluded) : [];

        $shortcodes = $event['shortcodes'];
        
        // Register core shortcodes for Editor Pro
        $coreShortcodes = [
            [
                'name' => 'center',
                'title' => 'Center Align',
                'description' => 'Center align content',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'formatting',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="10" x2="3" y2="10"></line><line x1="17" y1="6" x2="7" y2="6"></line><line x1="17" y1="14" x2="7" y2="14"></line><line x1="21" y1="18" x2="3" y2="18"></line></svg>',
                'attributes' => [],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => 'text-align: center;'
            ],
            [
                'name' => 'left',
                'title' => 'Left Align',
                'description' => 'Left align content',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'formatting',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="21" y1="18" x2="3" y2="18"></line></svg>',
                'attributes' => [],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => 'text-align: left;'
            ],
            [
                'name' => 'right',
                'title' => 'Right Align',
                'description' => 'Right align content',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'formatting',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="10" x2="7" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="21" y1="18" x2="7" y2="18"></line></svg>',
                'attributes' => [],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => 'text-align: right;'
            ],
            [
                'name' => 'justify',
                'title' => 'Justify Align',
                'description' => 'Justify align content',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'formatting',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="21" y1="18" x2="3" y2="18"></line></svg>',
                'attributes' => [],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => 'text-align: justify;'
            ],
            [
                'name' => 'columns',
                'title' => 'Columns Layout',
                'description' => 'Create multi-column layout with customizable spacing',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'layout',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="18" rx="1"></rect><rect x="14" y="3" width="7" height="18" rx="1"></rect></svg>',
                'attributes' => [
                    'count' => [
                        'type' => 'number',
                        'title' => 'Column Count',
                        'min' => 2,
                        'max' => 6,
                        'default' => 2,
                        'required' => true
                    ],
                    'width' => [
                        'type' => 'text',
                        'title' => 'Column Width',
                        'default' => '200px',
                        'placeholder' => 'e.g., 200px or auto'
                    ],
                    'gap' => [
                        'type' => 'text',
                        'title' => 'Gap',
                        'default' => '30px',
                        'placeholder' => 'e.g., 30px or 1rem'
                    ],
                    'rule' => [
                        'type' => 'text',
                        'title' => 'Column Rule',
                        'default' => '',
                        'placeholder' => 'e.g., 1px solid #ccc'
                    ]
                ],
                'titleBarAttributes' => ['count', 'width'],
                'hasContent' => true,
                'cssTemplate' => 'columns: {{count}} {{width}}; column-gap: {{gap}}; column-rule: {{rule}};'
            ],
            [
                'name' => 'div',
                'title' => 'Div element',
                'description' => 'Create a custom Div element',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'layout',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect></svg>',
                'attributes' => [
                    'id' => [
                        'type' => 'text',
                        'title' => 'ID',
                        'default' => null,
                        'required' => false
                    ],
                    'class' => [
                        'type' => 'text',
                        'title' => 'Class',
                        'default' => null,
                        'placeholder' => 'e.g., font-bold text-blue-500'
                    ],
                    'style' => [
                        'type' => 'text',
                        'title' => 'Style',
                        'default' => null,
                        'placeholder' => 'e.g., color: red; padding: 10px;'
                    ]
                ],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'span',
                'title' => 'Span element',
                'description' => 'Create a custom Span element',
                'type' => 'inline',
                'plugin' => 'shortcode-core',
                'category' => 'layout',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7V4a2 2 0 0 1 2-2h8"></path><path d="M4 17v3a2 2 0 0 0 2 2h8"></path><path d="M20 7V4a2 2 0 0 0-2-2h-8"></path><path d="M20 17v3a2 2 0 0 1-2 2h-8"></path></svg>',
                'attributes' => [
                    'id' => [
                        'type' => 'text',
                        'title' => 'ID',
                        'default' => null,
                        'required' => false
                    ],
                    'class' => [
                        'type' => 'text',
                        'title' => 'Class',
                        'default' => null,
                        'placeholder' => 'e.g., font-bold text-blue-500'
                    ],
                    'style' => [
                        'type' => 'text',
                        'title' => 'Style',
                        'default' => null,
                        'placeholder' => 'e.g., color: red; font-weight: bold;'
                    ]
                ],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'section',
                'title' => 'Section Container',
                'description' => 'Semantic section with optional styling',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'layout',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 22h2a2 2 0 0 0 2-2V7.5L14.5 2H6a2 2 0 0 0-2 2v3"></path><polyline points="14 2 14 8 20 8"></polyline><path d="M3 15h6"></path><path d="M6 12v6"></path></svg>',
                'attributes' => [
                    'name' => [
                        'type' => 'text',
                        'title' => 'Section Name',
                        'default' => '',
                        'placeholder' => 'Optional section identifier'
                    ],
                    'page' => [
                        'type' => 'text',
                        'title' => 'Page of Content',
                        'default' => '',
                        'placeholder' => '/content/my-page',
                        'required' => false
                    ],
                ],
                'titleBarAttributes' => ['name'],
                'hasContent' => true,
                'cssTemplate' => null
            ],
            [
                'name' => 'notice',
                'title' => 'Notice Box',
                'description' => 'Create styled notice/alert boxes',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>',
                'attributes' => [
                    'type' => [
                        'type' => 'select',
                        'title' => 'Notice Type',
                        'options' => ['note', 'info', 'warning', 'error'],
                        'default' => 'note',
                        'required' => true
                    ]
                ],
                'titleBarAttributes' => ['type'],
                'hasContent' => true,
                'cssTemplate' => 'padding: 12px 16px; border-radius: 4px; margin: 16px 0; border-left: 4px solid #0ea5e9; background: #f0f9ff; color: #0c4a6e;'
            ],
            [
                'name' => 'mark',
                'title' => 'Highlight Text',
                'description' => 'Highlight text with background color',
                'type' => 'inline',
                'plugin' => 'shortcode-core',
                'category' => 'formatting',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 11-6 6v3h9l3-3"></path><path d="m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4"></path></svg>',
                'attributes' => [
                    'color' => [
                        'type' => 'color',
                        'title' => 'Highlight Color',
                        'default' => '#ffff00'
                    ]
                ],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => 'background-color: {{color}}; padding: 1px 2px; border-radius: 2px;'
            ],
            [
                'name' => 'fa',
                'title' => 'Font Awesome Icon',
                'description' => 'Insert Font Awesome icon',
                'type' => 'inline',
                'plugin' => 'shortcode-core',
                'category' => 'media',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"></path><circle cx="12" cy="12" r="3"></circle></svg>',
                'attributes' => [
                    'icon' => [
                        'type' => 'text',
                        'title' => 'Icon Name',
                        'default' => 'heart',
                        'required' => true,
                        'placeholder' => 'e.g., heart, star, user'
                    ],
                    'size' => [
                        'type' => 'select',
                        'title' => 'Size',
                        'options' => ['', 'xs', 'sm', 'lg', 'xl', '2x', '3x'],
                        'default' => ''
                    ]
                ],
                'titleBarAttributes' => ['icon'],
                'hasContent' => false,
                'cssTemplate' => '',
                'customRenderer' => 'function(blockData, config) {
                    // Extract icon name from params or content
                    let iconName = "";
                    
                    // Try to get icon from attributes first
                    if (blockData.attributes && blockData.attributes.icon) {
                        iconName = blockData.attributes.icon;
                    } else if (blockData.params) {
                        const iconMatch = blockData.params.match(/icon\\s*=\\s*["\']([^"\']+)["\']|icon\\s*=\\s*([^\\s\\]]+)/);
                        iconName = iconMatch ? (iconMatch[1] || iconMatch[2]) : "";
                    }
                    
                    // If no icon in params, check if content is the icon name
                    if (!iconName && blockData.content && !blockData.content.includes(" ") && !blockData.content.includes("<")) {
                        iconName = blockData.content;
                    }
                    
                    if (iconName) {
                        // Create FontAwesome icon HTML
                        const iconClass = iconName.startsWith("fa-") ? iconName : "fa-" + iconName;
                        let sizeClass = "";
                        
                        // Add size class if specified
                        if (blockData.attributes && blockData.attributes.size) {
                            sizeClass = " fa-" + blockData.attributes.size;
                        }
                        
                        let displayText = "<i class=\"fa " + iconClass + sizeClass + "\" style=\"margin: 0 4px;\"></i>";
                        
                        // Add any additional content after the icon if it is not the icon name
                        if (blockData.content && blockData.content !== iconName) {
                            displayText += " " + blockData.content;
                        }
                        return displayText;
                    } else {
                        // Fallback to showing the content or tagName
                        return blockData.content || blockData.tagName;
                    }
                }'
            ],
            [
                'name' => 'color',
                'title' => 'Text Color',
                'description' => 'Apply color to text',
                'type' => 'inline',
                'plugin' => 'shortcode-core',
                'category' => 'formatting',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="13.5" cy="6.5" r=".5"></circle><circle cx="17.5" cy="10.5" r=".5"></circle><circle cx="8.5" cy="7.5" r=".5"></circle><circle cx="6.5" cy="12.5" r=".5"></circle><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z"></path></svg>',
                'attributes' => [
                    'color' => [
                        'type' => 'color',
                        'title' => 'Text Color',
                        'default' => '#000000',
                        'required' => true
                    ]
                ],
                'titleBarAttributes' => ['color'],
                'hasContent' => true,
                'cssTemplate' => 'color: {{color}};'
            ],
            [
                'name' => 'size',
                'title' => 'Text Size',
                'description' => 'Change text size',
                'type' => 'inline',
                'plugin' => 'shortcode-core',
                'category' => 'formatting',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7V4h16v3"></path><path d="M12 4v16"></path><path d="M6 20h12"></path></svg>',
                'attributes' => [
                    'size' => [
                        'type' => 'text',
                        'title' => 'Font Size',
                        'default' => '16px',
                        'required' => true,
                        'placeholder' => 'e.g., 16px, 1.2em, 120%'
                    ]
                ],
                'titleBarAttributes' => ['size'],
                'hasContent' => true,
                'cssTemplate' => 'font-size: {{size}};'
            ],
            [
                'name' => 'u',
                'title' => 'Underline',
                'description' => 'Underline text',
                'type' => 'inline',
                'plugin' => 'shortcode-core',
                'category' => 'formatting',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 4v7a6 6 0 0 0 12 0V4"></path><line x1="4" y1="20" x2="20" y2="20"></line></svg>',
                'attributes' => [],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => 'text-decoration: underline;'
            ],
            [
                'name' => 'figure',
                'title' => 'Figure',
                'description' => 'Figure with optional caption',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'media',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg>',
                'attributes' => [
                    'id' => [
                        'type' => 'text',
                        'title' => 'ID',
                        'default' => '',
                        'required' => false
                    ],
                    'class' => [
                        'type' => 'text',
                        'title' => 'Class',
                        'default' => '',
                        'placeholder' => 'e.g., full-width centered'
                    ],
                    'caption' => [
                        'type' => 'text',
                        'title' => 'Caption',
                        'default' => '',
                        'placeholder' => 'Figure caption text'
                    ]
                ],
                'titleBarAttributes' => ['caption'],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'details',
                'title' => 'Details/Summary',
                'description' => 'Collapsible content section',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"></path></svg>',
                'attributes' => [
                    'summary' => [
                        'type' => 'text',
                        'title' => 'Summary Text',
                        'default' => 'Click to expand',
                        'required' => true
                    ],
                    'class' => [
                        'type' => 'text',
                        'title' => 'Class',
                        'default' => '',
                        'placeholder' => 'e.g., accordion-item'
                    ]
                ],
                'titleBarAttributes' => ['summary'],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'h1',
                'title' => 'Heading 1',
                'description' => 'Level 1 heading',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h8"></path><path d="M4 18V6"></path><path d="M12 18V6"></path><path d="m17 12 3-2v8"></path></svg>',
                'attributes' => [
                    'id' => [
                        'type' => 'text',
                        'title' => 'ID',
                        'default' => '',
                        'required' => false
                    ],
                    'class' => [
                        'type' => 'text',
                        'title' => 'Class',
                        'default' => '',
                        'placeholder' => 'e.g., section-title'
                    ]
                ],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'h2',
                'title' => 'Heading 2',
                'description' => 'Level 2 heading',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h8"></path><path d="M4 18V6"></path><path d="M12 18V6"></path><path d="M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-3.5-1.5-.5.5-.5 1.5-.5 1.5"></path><path d="M17 18v-2"></path></svg>',
                'attributes' => [
                    'id' => [
                        'type' => 'text',
                        'title' => 'ID',
                        'default' => '',
                        'required' => false
                    ],
                    'class' => [
                        'type' => 'text',
                        'title' => 'Class',
                        'default' => '',
                        'placeholder' => 'e.g., subsection-title'
                    ]
                ],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'h3',
                'title' => 'Heading 3',
                'description' => 'Level 3 heading',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h8"></path><path d="M4 18V6"></path><path d="M12 18V6"></path><path d="M17.5 10.5c1.7-1 3.5 0 3.5 1.5a2 2 0 0 1-2 2"></path><path d="M17 17.5c2 1.5 4 .3 4-1.5a2 2 0 0 0-2-2"></path></svg>',
                'attributes' => [
                    'id' => [
                        'type' => 'text',
                        'title' => 'ID',
                        'default' => '',
                        'required' => false
                    ],
                    'class' => [
                        'type' => 'text',
                        'title' => 'Class',
                        'default' => '',
                        'placeholder' => 'e.g., minor-heading'
                    ]
                ],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'h4',
                'title' => 'Heading 4',
                'description' => 'Level 4 heading',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h8"></path><path d="M4 18V6"></path><path d="M12 18V6"></path><path d="M17 10v4h4"></path><path d="M21 10v8"></path></svg>',
                'attributes' => [
                    'id' => [
                        'type' => 'text',
                        'title' => 'ID',
                        'default' => '',
                        'required' => false
                    ],
                    'class' => [
                        'type' => 'text',
                        'title' => 'Class',
                        'default' => '',
                        'placeholder' => 'e.g., minor-heading'
                    ]
                ],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'h5',
                'title' => 'Heading 5',
                'description' => 'Level 5 heading',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h8"></path><path d="M4 18V6"></path><path d="M12 18V6"></path><path d="M17 13v-3h4"></path><path d="M17 17.7c.4.2.8.3 1.3.3 1.5 0 2.7-1.1 2.7-2.5S19.8 13 18.3 13c-.5 0-1 .2-1.3.5"></path></svg>',
                'attributes' => [
                    'id' => [
                        'type' => 'text',
                        'title' => 'ID',
                        'default' => '',
                        'required' => false
                    ],
                    'class' => [
                        'type' => 'text',
                        'title' => 'Class',
                        'default' => '',
                        'placeholder' => 'e.g., minor-heading'
                    ]
                ],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'h6',
                'title' => 'Heading 6',
                'description' => 'Level 6 heading',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h8"></path><path d="M4 18V6"></path><path d="M12 18V6"></path><circle cx="19" cy="16" r="2"></circle><path d="M20 10c-2 2-3 3.5-3 6"></path></svg>',
                'attributes' => [
                    'id' => [
                        'type' => 'text',
                        'title' => 'ID',
                        'default' => '',
                        'required' => false
                    ],
                    'class' => [
                        'type' => 'text',
                        'title' => 'Class',
                        'default' => '',
                        'placeholder' => 'e.g., minor-heading'
                    ]
                ],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'raw',
                'title' => 'Raw Content',
                'description' => 'Prevent shortcode processing in content',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>',
                'attributes' => [],
                'titleBarAttributes' => [],
                'hasContent' => true,
                'cssTemplate' => '',
                'customRenderer' => 'function(blockData, config) {
                    // Raw content should be displayed as-is without processing
                    return blockData.content || "";
                }'
            ],
            [
                'name' => 'lang',
                'title' => 'Language Filter',
                'description' => 'Show content only for specific languages',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>',
                'attributes' => [
                    'lang' => [
                        'type' => 'text',
                        'title' => 'Language Codes',
                        'default' => 'en',
                        'required' => true,
                        'placeholder' => 'e.g., en, fr, de (comma-separated)'
                    ]
                ],
                'titleBarAttributes' => ['lang'],
                'hasContent' => true,
                'cssTemplate' => ''
            ],
            [
                'name' => 'lorem',
                'title' => 'Lorem Ipsum',
                'description' => 'Generate placeholder text',
                'type' => 'block',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>',
                'attributes' => [
                    'p' => [
                        'type' => 'number',
                        'title' => 'Paragraphs',
                        'default' => 1,
                        'min' => 1,
                        'max' => 10
                    ],
                    's' => [
                        'type' => 'number',
                        'title' => 'Sentences',
                        'default' => 0,
                        'min' => 0,
                        'max' => 20
                    ],
                    'w' => [
                        'type' => 'number',
                        'title' => 'Words',
                        'default' => 0,
                        'min' => 0,
                        'max' => 100
                    ]
                ],
                'titleBarAttributes' => ['p', 's', 'w'],
                'hasContent' => false,
                'cssTemplate' => '',
                'customRenderer' => 'function(blockData, config) {
                    const p = blockData.attributes?.p || 1;
                    const s = blockData.attributes?.s || 0;
                    const w = blockData.attributes?.w || 0;
                    
                    let display = "Lorem ipsum";
                    if (p > 0) display += " - " + p + " paragraph" + (p > 1 ? "s" : "");
                    if (s > 0) display += ", " + s + " sentence" + (s > 1 ? "s" : "");
                    if (w > 0) display += ", " + w + " word" + (w > 1 ? "s" : "");
                    
                    return display;
                }'
            ],
            [
                'name' => 'safe-email',
                'title' => 'Safe Email',
                'description' => 'Obfuscated email address',
                'type' => 'inline',
                'plugin' => 'shortcode-core',
                'category' => 'content',
                'group' => 'Core Shortcodes',
                'icon' => '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"></circle><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"></path></svg>',
                'attributes' => [
                    'email' => [
                        'type' => 'text',
                        'title' => 'Email Address',
                        'default' => '',
                        'required' => true,
                        'placeholder' => 'user@example.com'
                    ],
                    'autolink' => [
                        'type' => 'checkbox',
                        'title' => 'Auto Link',
                        'default' => true
                    ]
                ],
                'titleBarAttributes' => ['email'],
                'hasContent' => false,
                'cssTemplate' => '',
                'customRenderer' => 'function(blockData, config) {
                    const email = blockData.attributes?.email || blockData.content || "";
                    if (email) {
                        // Display obfuscated version in editor
                        const parts = email.split("@");
                        if (parts.length === 2) {
                            return parts[0] + "[at]" + parts[1].replace(/\\./g, "[dot]");
                        }
                    }
                    return email || "safe-email";
                }'
            ]
        ];
        
        // Add all core shortcodes to the registry
        foreach ($coreShortcodes as $shortcode) {
            if (in_array($shortcode['name'], $excluded, true)) {
                continue;
            }
            $shortcodes[] = $shortcode;
        }

        $event['shortcodes'] = $shortcodes;
        return $event;
    }

}
