<?php
declare(strict_types=1);

if (!isset($GLOBALS['cms_shortcodes']) || !is_array($GLOBALS['cms_shortcodes'])) {
    $GLOBALS['cms_shortcodes'] = [];
}

if (!function_exists('add_shortcode')) {
    function add_shortcode(string $tag, callable $callback): void
    {
        $tag = strtolower(trim($tag));
        if ($tag === '') {
            return;
        }

        $GLOBALS['cms_shortcodes'][$tag] = $callback;
    }
}

if (!function_exists('shortcode_atts')) {
    function shortcode_atts(array $defaults, array $atts): array
    {
        $normalized = [];

        foreach ($defaults as $key => $value) {
            $normalized[$key] = array_key_exists($key, $atts) ? $atts[$key] : $value;
        }

        foreach ($atts as $key => $value) {
            if (!array_key_exists($key, $normalized)) {
                $normalized[$key] = $value;
            }
        }

        return $normalized;
    }
}

if (!function_exists('shortcode_parse_atts')) {
    function shortcode_parse_atts(string $text): array
    {
        $atts = [];

        if (preg_match_all('/([a-zA-Z0-9_-]+)\s*=\s*"([^"]*)"/', $text, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $atts[strtolower($match[1])] = $match[2];
            }
        }

        if (preg_match_all("/([a-zA-Z0-9_-]+)\s*=\s*'([^']*)'/", $text, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $atts[strtolower($match[1])] = $match[2];
            }
        }

        return $atts;
    }
}

if (!function_exists('do_shortcode')) {
    function do_shortcode(string $content, int $depth = 0): string
    {
        if ($content === '' || empty($GLOBALS['cms_shortcodes'])) {
            return $content;
        }

        if ($depth > 10) {
            return $content;
        }

        $pattern = '/\[([a-zA-Z0-9_-]+)([^\]]*)\]/';

        return (string) preg_replace_callback($pattern, static function (array $matches) use ($depth): string {
            $tag = strtolower(trim($matches[1]));
            $rawAttributes = trim($matches[2] ?? '');

            if (!isset($GLOBALS['cms_shortcodes'][$tag]) || !is_callable($GLOBALS['cms_shortcodes'][$tag])) {
                return $matches[0];
            }

            $attributes = shortcode_parse_atts($rawAttributes);
            $callback = $GLOBALS['cms_shortcodes'][$tag];

            try {
                $output = (string) call_user_func($callback, $attributes);
            } catch (Throwable $exception) {
                error_log('[CMS Shortcode] ' . $tag . ' failed: ' . $exception->getMessage());
                return '';
            }

            return do_shortcode($output, $depth + 1);
        }, $content) ?? $content;
    }
}

if (function_exists('add_filter')) {
    add_filter('render_output', static function ($output) {
        return is_string($output) ? do_shortcode($output) : $output;
    }, 5);
}
