<?php
namespace DiviBooster\DiviBooster\Modules\CircleCounter\PreviewText;

// Bail if loaded outside WordPress.
if (!defined('ABSPATH')) {
    return;
}

const DIVI5_MODULE_SLUG             = 'divi/circle-counter';
const DIVI5_SETTING_SLUG_PREVIEW    = 'diviboosterPreviewText';
const DIVI5_SETTING_SLUG_FONT       = 'diviboosterPreviewTextFont';

const PREVIEW_TEXT_DATA_ATTR = 'data-divibooster-prefix';

function translatable_text($text) {
    if (!function_exists('__')) {
        return $text;
    }

    return __($text, 'divi-booster');
}

// === Small compatibility helpers ===

// Escape a string if WordPress escaping functions are available.
function maybe_esc_html($text) {
    if (!function_exists('esc_html')) {
        return null;
    }

    return esc_html((string) $text);
}

// Register a filter if the hooks API is available.
function maybe_add_filter($hook, $callback, $priority = 10, $accepted_args = 1) {
    if (!function_exists('add_filter')) {
        return;
    }

    add_filter($hook, $callback, $priority, $accepted_args);
}

// Register an action if the hooks API is available.
function maybe_add_action($hook, $callback, $priority = 10, $accepted_args = 1) {
    if (!function_exists('add_action')) {
        return;
    }

    add_action($hook, $callback, $priority, $accepted_args);
}

// Extract the Number Prefix value from a block attributes array.
function get_divi5_number_prefix_from_attrs($attrs) {
    if (!is_array($attrs)) {
        return '';
    }

    $setting = $attrs[DIVI5_SETTING_SLUG_PREVIEW] ?? null;
    if (!is_array($setting)) {
        return '';
    }

    if (isset($setting['desktop']['value'])) {
        return $setting['desktop']['value'];
    }

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

    return '';
}

// === Rendering: prefix markup ===

// Add the prefix as a data attribute on the percent value so CSS can render it via ::before.
function inject_preview_text_markup($output, $preview_text) {
    if (!is_string($output) || $preview_text === null) {
        return $output;
    }

    $preview_text = (string) $preview_text;
    if ($preview_text === '') {
        return $output;
    }

    // Escape for placement inside an HTML attribute.
    $preview_escaped = maybe_esc_attr($preview_text);
    if ($preview_escaped === null) {
        return $output;
    }

    $callback = function($m) use ($preview_escaped) {
    // Only touch the first paragraph that contains the percent value.
        $inner = $m[1];
        if (strpos($inner, 'percent-value') === false) {
            return $m[0];
        }
    // Add the prefix data attribute to the percent-value span.
        $inner = preg_replace(
            '/(<span[^>]*class="[^"]*percent-value[^"]*")/',
            '$1 ' . PREVIEW_TEXT_DATA_ATTR . '="' . $preview_escaped . '"',
            $inner,
            1
        );
        return '<p>' . $inner . '</p>';
    };

    $output = preg_replace_callback('#<p>(.*?)</p>#s', $callback, $output, 1);

    return $output;
}

// Return a value stored at a dotted path within a nested array (or return the default).
function array_get_path($array, array $path, $default = null) {
    $current = $array;
    foreach ($path as $key) {
        if (!is_array($current) || !array_key_exists($key, $current)) {
            return $default;
        }
        $current = $current[$key];
    }
    return $current;
}

// Escape wrapper to ensure a string is safe for embedding inside an HTML attribute.
function maybe_esc_attr($text) {
    if (!function_exists('esc_attr')) {
        return null;
    }

    return esc_attr((string) $text);
}

// Escape wrapper for a previously-escaped HTML fragment (when that helper exists).
function maybe_et_core_esc_previously($html) {
    if (!function_exists('et_core_esc_previously')) {
        return $html;
    }

    return et_core_esc_previously($html);
}

function ensure_divi5_attribute_exists(&$settings, $attr_slug, array $definition) {
    if (!isset($settings['attributes'][$attr_slug])) {
        $settings['attributes'][$attr_slug] = $definition;
    }
}

// === Divi 5: block metadata (PHP) ===
maybe_add_filter('block_type_metadata_settings', __NAMESPACE__ . '\register_divi5_attrs');

// Ensure our additional attributes are present in the Circle Counter block metadata.
function register_divi5_attrs($settings) {
    if (!is_array($settings)) {
        return $settings;
    }

    $module_name       = isset($settings['name']) ? $settings['name'] : '';
    $module_attributes = isset($settings['attributes']) ? $settings['attributes'] : false;

    if ($module_name !== DIVI5_MODULE_SLUG || !$module_attributes || !is_array($module_attributes)) {
        return $settings;
    }

    ensure_divi5_attribute_exists(
        $settings,
        DIVI5_SETTING_SLUG_PREVIEW,
        [
            'type'     => 'object',
            'settings' => [
                'innerContent' => [
                    'groupType' => 'group-items',
                    'items'     => [
                        'diviboosterPreviewTextField' => [
                            'groupSlug'   => 'contentElements',
                            'attrName'    => DIVI5_SETTING_SLUG_PREVIEW,
                            'label'       => translatable_text('Number Prefix'),
                            'description' => translatable_text('Text to display before the number (e.g. a currency symbol).'),
                            'features'    => [
                                'hover'      => false,
                                'sticky'     => false,
                                'responsive' => false,
                                'preset'     => 'content',
                            ],
                            'render'      => true,
                            'priority'    => 30,
                            'component'   => [
                                'type' => 'field',
                                'name' => 'divi/text',
                            ],
                            'defaultAttr' => [
                                'desktop' => [
                                    'value' => '',
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ]
    );

    ensure_divi5_attribute_exists(
        $settings,
        DIVI5_SETTING_SLUG_FONT,
        [
            'type'     => 'object',
            'settings' => [
                'decoration' => [
                    'font' => [
                        'groupType' => 'group-item',
                        'item'      => [
                            'groupSlug' => 'designPreviewText',
                            'render'    => true,
                            'component' => [
                                'name' => 'divi/font',
                                'type' => 'group',
                                'props'=> [
                                    'grouped'        => false,
                                    'fieldLabel'     => translatable_text('Number Prefix'),
                                    'fields'         => [
                                        'textAlign' => [
                                            'render' => false,
                                        ],
                                        'lineHeight' => [
                                            'render' => false,
                                        ],
                                    ],
                                    'defaultGroupAttr' => [
                                        'font' => [
                                            'desktop' => [
                                                'value' => [
                                                    'size'       => '',
                                                    'lineHeight' => '',
                                                ],
                                            ],
                                        ],
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ]
    );

    return $settings;
}

// === Divi 5: builder wiring (JS) ===
maybe_add_action('wp_enqueue_scripts', __NAMESPACE__ . '\enqueue_divi5_mapping_js');

// Enqueue a small inline script that wires the extra settings into the builder UI.
function enqueue_divi5_mapping_js() {
    if (!function_exists('et_builder_d5_enabled') || !\et_builder_d5_enabled()) {
        return;
    }

    if (!function_exists('et_core_is_fb_enabled') || !\et_core_is_fb_enabled()) {
        return;
    }

    if (!function_exists('sanitize_title') ||
        !function_exists('wp_register_script') ||
        !function_exists('wp_enqueue_script') ||
        !function_exists('wp_add_inline_script')) {
        return;
    }

    $handle = sanitize_title('divi-booster-' . DIVI5_MODULE_SLUG . '-' . DIVI5_SETTING_SLUG_PREVIEW . '-mapping');
    \dbdb_enqueue_inline_script_if_dependencies_available($handle, get_divi5_mapping_inline_js(), array('lodash', array('divi-vendor-wp-hooks', 'wp-hooks')));
}

function get_divi5_mapping_inline_js() {
    if (!function_exists('wp_json_encode')) {
        return '';
    }

    $divi5_preview_slug    = wp_json_encode(DIVI5_SETTING_SLUG_PREVIEW);
    $divi5_font_slug       = wp_json_encode(DIVI5_SETTING_SLUG_FONT);

    if (!is_string($divi5_preview_slug) || $divi5_preview_slug === '' ||
        !is_string($divi5_font_slug) || $divi5_font_slug === '') {
        return '';
    }

    // Build the inline JS using a heredoc so PHP can safely interpolate JSON-encoded keys.
    return <<<END
// Add a Design group for Number Prefix in the Circle Counter module.
window.vendor.wp.hooks.addFilter('divi.moduleLibrary.moduleSettings.groups.divi.circle-counter', 'divi', (groups, metadata) => {
    if (!groups.designPreviewText) {
        groups.designPreviewText = {
            groupName: 'designPreviewText',
            panel: 'design',
            priority: 50,
            multiElements: true,
            component: {
                name: 'divi/composite',
                props: {
                    groupLabel: 'Number Prefix',
                },
            },
        };
    }
    return groups;
});

// Ensure Number Prefix-related attributes exist on the module.
window.vendor.wp.hooks.addFilter('divi.moduleLibrary.moduleAttributes.divi.circle-counter', 'divi', (attributes, metadata) => {
    if (!attributes[{$divi5_preview_slug}]) {
        attributes[{$divi5_preview_slug}] = {
            type: 'object',
            settings: {
                innerContent: {
                    groupType: 'group-items',
                    items: {
                        diviboosterPreviewTextField: {
                            groupSlug: 'contentElements',
                            attrName: {$divi5_preview_slug},
                            label: 'Number Prefix',
                            description: 'Text to display before the number (e.g. a currency symbol).',
                            features: {
                                hover: false,
                                sticky: false,
                                responsive: false,
                                preset: 'content',
                            },
                            render: true,
                            priority: 30,
                            component: {
                                type: 'field',
                                name: 'divi/text',
                            },
                            defaultAttr: {
                                desktop: { value: '' },
                            },
                        },
                    },
                },
            },
        };
    }

    // Keep the font attribute present in the builder.
    if (!attributes[{$divi5_font_slug}]) {
        attributes[{$divi5_font_slug}] = {
            type: 'object',
            settings: {
                decoration: {
                    font: {
                        groupType: 'group-item',
                        item: {
                            groupSlug: 'designPreviewText',
                            render: true,
                            component: {
                                name: 'divi/font',
                                type: 'group',
                                props: {
                                    grouped: false,
                                    fieldLabel: 'Number Prefix',
                                    fields: {
                                        textAlign: {
                                            render: false,
                                        },
                                        lineHeight: {
                                            render: false,
                                        },
                                    },
                                    defaultGroupAttr: {
                                        font: {
                                            desktop: {
                                                value: {
                                                    size: '',
                                                    lineHeight: '',
                                                },
                                            },
                                        },
                                    },
                                },
                            },
                        },
                    },
                },
            },
        };
    }

    return attributes;
});

// Track the prefix for each rendered module instance, keyed by its data-id.
var diviBoosterPrefixMap = window._diviBoosterPrefixMap || {};
window._diviBoosterPrefixMap = diviBoosterPrefixMap;

window.vendor.wp.hooks.addFilter('divi.module.wrapper.render', 'divi-booster', function(moduleWrapper, param) {
    var moduleName = param && param.name || '';
    var moduleAttrs = param && param.attrs || {};
    var moduleId = param && param.id || '';
    var moduleState = param && param.state || '';
    var moduleElements = param && param.elements || null;

    if ('divi/circle-counter' !== moduleName) {
        return moduleWrapper;
    }

    // Read the prefix value from either the direct or innerContent attribute shape.
    var rawAttr = moduleAttrs[{$divi5_preview_slug}];
    var valueObj = rawAttr && rawAttr.desktop ? rawAttr : (rawAttr && rawAttr.innerContent ? rawAttr.innerContent : null);
    var prefixText = (valueObj && valueObj.desktop && valueObj.desktop.value) || (valueObj && valueObj.value) || '';

    // Store the prefix so the DOM updater can apply it.
    diviBoosterPrefixMap[moduleId] = prefixText;

    // Update the DOM shortly after render so the preview reflects the latest prefix.
    if (window._diviBoosterApplyPrefixes) {
        requestAnimationFrame(function() { window._diviBoosterApplyPrefixes(); });
    }

    // Emit font styles for the Number Prefix ::before pseudo-element in the builder.
    var fontAttr = moduleAttrs[{$divi5_font_slug}] && moduleAttrs[{$divi5_font_slug}].decoration && moduleAttrs[{$divi5_font_slug}].decoration.font || {};
    var el = window.vendor && window.vendor.wp && window.vendor.wp.element;
    var StyleContainer = window.divi && window.divi.module && window.divi.module.StyleContainer;
    var orderClass = moduleElements && moduleElements.orderClass || '';

    if (!el || !StyleContainer || !orderClass) {
        return moduleWrapper;
    }

    var isEdited = moduleWrapper.props && moduleWrapper.props.isEdited;

    var additionalStyles = el.createElement(
        StyleContainer,
        {
            key: 'divibooster-prefix-styles',
            mode: 'builder',
            state: isEdited ? moduleState : '',
            noStyleTag: false,
        },
        moduleElements.style({
            attrName: 'module',
            styleProps: {
                advancedStyles: [
                    {
                        componentName: 'divi/font',
                        props: {
                            selector: orderClass + ' .percent p .percent-value::before',
                            attr: fontAttr,
                        },
                    },
                ],
            },
        })
    );

    // Append styles as an extra child of the wrapper.
    return el.cloneElement(
        moduleWrapper,
        {},
        moduleWrapper.props.children,
        additionalStyles
    );
});

// Keep the DOM in sync with the latest prefix values.
(function(){
    var styleId = 'divibooster-circle-counter-prefix-vb-style';
    if (!document.getElementById(styleId)) {
        var css = '.percent-value[data-divibooster-prefix]::before { content: attr(data-divibooster-prefix); }';
        var style = document.createElement('style');
        style.id = styleId;
        style.type = 'text/css';
        style.appendChild(document.createTextNode(css));
        (document.head || document.documentElement).appendChild(style);
    }

    var rafId = 0;
    function scheduleApplyPrefixes() {
        if (rafId) return;
        rafId = requestAnimationFrame(function() {
            rafId = 0;
            applyPrefixes();
        });
    }

    function applyPrefixes() {
        var map = window._diviBoosterPrefixMap || {};
        var modules = document.querySelectorAll('.et_pb_circle_counter[data-id]');
        for (var i = 0; i < modules.length; i++) {
            var mod = modules[i];
            var id = mod.getAttribute('data-id');
            var prefix = map[id] || '';
            var pv = mod.querySelector('.percent-value');
            if (!pv) continue;
            var current = pv.getAttribute('data-divibooster-prefix') || '';
            if (prefix && current !== prefix) {
                pv.setAttribute('data-divibooster-prefix', prefix);
            } else if (!prefix && current) {
                pv.removeAttribute('data-divibooster-prefix');
            }
        }
    }

    // Apply once initially.
    scheduleApplyPrefixes();

    // Expose a trigger so the render hook can request an update.
    window._diviBoosterApplyPrefixes = scheduleApplyPrefixes;

    // Observe mutations and debounce updates to once per animation frame.
    var observer = new MutationObserver(function() {
        scheduleApplyPrefixes();
    });
    observer.observe(document.body || document.documentElement, {
        childList: true,
        subtree: true,
        attributes: true,
        attributeFilter: ['data-id', 'class']
    });
})();

END;
}

// === Divi 5: server render integration ===

// Inject the prefix data attribute into the rendered Circle Counter markup.
maybe_add_filter('render_block_' . DIVI5_MODULE_SLUG, function($block_content, $parsed_block, $block) {
    $attrs = isset($parsed_block['attrs']) ? $parsed_block['attrs'] : null;
    $preview = get_divi5_number_prefix_from_attrs($attrs);

    return inject_preview_text_markup($block_content, $preview);
}, 10, 3);

// === Divi 5: front-end CSS ===
maybe_add_action('wp_head', function() {
    // Only output on the front-end.
    if (function_exists('et_core_is_fb_enabled') && et_core_is_fb_enabled()) {
        return;
    }
    // Skip emitting CSS if there are no Circle Counter modules on the page.
    if (!function_exists('has_block') || !has_block(DIVI5_MODULE_SLUG)) {
        return;
    }
    echo '<style>.percent-value[data-divibooster-prefix]::before{content:attr(data-divibooster-prefix)}</style>';
});

// === Divi 5: font styles ===

// Ask Divi's style system to emit font-related styles for the prefix pseudo-element.
maybe_add_filter('divi_module_wrapper_render', __NAMESPACE__ . '\divi5_wrapper_render_preview_text_styles', 10, 2);

// Add font styles for the prefix pseudo-element when rendering the Circle Counter wrapper.
function divi5_wrapper_render_preview_text_styles($module_wrapper, $args) {
    if (!is_array($args)) {
        return maybe_et_core_esc_previously($module_wrapper);
    }

    $module_name = isset($args['name']) ? (string) $args['name'] : '';
    
    if ($module_name !== DIVI5_MODULE_SLUG) {
        return maybe_et_core_esc_previously($module_wrapper);
    }

    $module_attrs    = is_array($args['attrs'] ?? null) ? $args['attrs'] : [];
    $module_elements = $args['elements'] ?? null;
    if (!$module_elements) {
        return maybe_et_core_esc_previously($module_wrapper);
    }

    if (!is_object($module_elements) || !method_exists($module_elements, 'style') || !property_exists($module_elements, 'order_class')) {
        return maybe_et_core_esc_previously($module_wrapper);
    }

    // Build the font attribute payload.
    $font_attr = array_get_path($module_attrs, [DIVI5_SETTING_SLUG_FONT, 'decoration', 'font'], []);
    if (!is_array($font_attr)) {
        $font_attr = [];
    }

    $text_shadow_attr = array_get_path($module_attrs, [DIVI5_SETTING_SLUG_FONT, 'decoration', 'font', 'textShadow'], []);
    if (!is_array($text_shadow_attr)) {
        $text_shadow_attr = [];
    }

    $style_payload = [
        'id'            => $args['id'] ?? '',
        'name'          => $module_name,
        'orderIndex'    => $args['orderIndex'] ?? 0,
        'storeInstance' => $args['storeInstance'] ?? 0,
        'styles'        => [
            $module_elements->style(
                [
                    'attrName'   => 'module',
                    'styleProps' => [
                        'advancedStyles' => [
                            [
                                'componentName' => 'divi/font',
                                'props'         => [
                    // Target the prefix pseudo-element.
                                    'selector' => "{$module_elements->order_class} .percent p .percent-value::before",
                                    'attr'     => $font_attr,
                                ],
                            ],
                // Apply text shadow if set.
                            [
                                'componentName' => 'divi/text-shadow',
                                'props'         => [
                                    'selector' => "{$module_elements->order_class} .percent p .percent-value::before",
                                    'attr'     => $text_shadow_attr,
                                ],
                            ],
                        ],
                    ],
                ]
            ),
        ],
    ];


    if (class_exists('ET\\Builder\\FrontEnd\\Module\\Style') && method_exists('ET\\Builder\\FrontEnd\\Module\\Style', 'add')) {
        try {
            \ET\Builder\FrontEnd\Module\Style::add($style_payload);
        } catch (\Throwable $e) {
            // Fail silently if Divi's style system changes or is unavailable.
        }
    }

    return maybe_et_core_esc_previously($module_wrapper);
}



