<?php
/**
 * Name: Gutenberg Block Control
 * Description: Enable or disable specific Gutenberg blocks from block inserter to clean up editor interface and prevent users from using problematic or unnecessary blocks
 * Category: Admin
 * Icon: -
 * UseCases: [
 *   {
 *     "title": "Simplified Editor for Clients",
 *     "description": "Disable 40+ advanced blocks (code, columns, pullquotes) leaving only paragraph, image, and heading for non-technical blog authors."
 *   },
 *   {
 *     "title": "Enforce Brand Consistency",
 *     "description": "Remove blocks like 'Custom HTML' and 'Shortcode' that allow users to break site design or inject unauthorized code."
 *   },
 *   {
 *     "title": "WooCommerce Focus",
 *     "description": "Disable content blocks not needed for product descriptions, keeping only essential blocks to simplify product editors' experience."
 *   }
 * ]
 * Added: 2025-08-08
 * Tags: gutenberg, blocks, editor, disable, control
 * WhereToFind: {
 *   "location": "Block Editor",
 *   "url": "post-new.php",
 *   "instructions": "Open block inserter (+) - disabled blocks won't appear in the list",
 *   "newTab": false
 * }
 */

// Prevent direct access
if (!defined('ABSPATH')) exit;

class SwitchboardGutenbergBlockControl {
    
    public function __construct() {
        add_filter('allowed_block_types_all', [$this, 'filter_blocks'], 999, 2);
        add_action('enqueue_block_editor_assets', [$this, 'enqueue_block_script']);
        add_action('enqueue_block_editor_assets', [$this, 'cache_available_blocks'], 1);
        add_action('wp_ajax_get_gutenberg_blocks', [$this, 'ajax_get_blocks']);
    }
    
    private function get_settings() {
        // Get settings from the correct option name
        $settings = get_option('switchboard_gutenberg-block-control_settings', []);
        
        return $settings;
    }
    
    public function filter_blocks($allowed_blocks, $context) {
        $settings = $this->get_settings();
        $disabled_blocks = isset($settings['disabled_blocks']) ? $settings['disabled_blocks'] : '';

        if (empty($disabled_blocks) || !is_string($disabled_blocks)) {
            return $allowed_blocks;
        }

        $disabled = array_filter(array_map('trim', explode(',', $disabled_blocks)));
        
        // If $allowed_blocks is true/null, get all registered blocks
        if ($allowed_blocks === true || $allowed_blocks === null || !is_array($allowed_blocks)) {
            $registry = WP_Block_Type_Registry::get_instance();
            $all_blocks = $registry->get_all_registered();
            $allowed_blocks = array_keys($all_blocks);
        }
        
        // Remove disabled blocks
        $allowed = array_diff($allowed_blocks, $disabled);
        
        return array_values($allowed);
    }
    
    public function enqueue_block_script() {
        $settings = $this->get_settings();
        $disabled_blocks = isset($settings['disabled_blocks']) ? $settings['disabled_blocks'] : '';

        if (empty($disabled_blocks) || !is_string($disabled_blocks)) {
            return;
        }

        $disabled = array_filter(array_map('trim', explode(',', $disabled_blocks)));

        // Also unregister blocks via JavaScript as backup
        wp_add_inline_script('wp-blocks', '
            wp.domReady(function() {
                var disabledBlocks = ' . wp_json_encode($disabled) . ';
                disabledBlocks.forEach(function(blockName) {
                    if (wp.blocks.getBlockType(blockName)) {
                        wp.blocks.unregisterBlockType(blockName);
                    }
                });
            });
        ', 'after');
    }
    
    public function cache_available_blocks() {
        // Cache all available blocks when editor loads
        $all_blocks = WP_Block_Type_Registry::get_instance()->get_all_registered();
        
        $blocks = [];
        foreach ($all_blocks as $name => $block) {
            // Skip internal/hidden blocks
            if (strpos($name, 'core/') === false && strpos($name, '/') === false) {
                continue;
            }
            
            // Skip blocks without proper titles
            if (!isset($block->title) || empty($block->title)) {
                continue;
            }
            
            $blocks[] = [
                'name' => $name,
                'title' => $block->title,
                'category' => isset($block->category) ? $block->category : 'common'
            ];
        }
        
        // Sort by category then title
        usort($blocks, function($a, $b) {
            if ($a['category'] === $b['category']) {
                return strcmp($a['title'], $b['title']);
            }
            return strcmp($a['category'], $b['category']);
        });
        
        // Cache for 1 hour
        set_transient('switchboard_available_blocks', $blocks, HOUR_IN_SECONDS);
    }
    
    public function ajax_get_blocks() {
        check_ajax_referer('switchboard-nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(esc_html__('Unauthorized', 'wpswitchboard'));
        }

        $blocks = get_transient('switchboard_available_blocks');

        if (false === $blocks) {
            // Fallback to current registry if cache is empty
            $blocks = $this->get_all_blocks();
        }

        wp_send_json_success($blocks);
    }
    
    public function get_all_blocks() {
        // Try to get cached blocks first
        $cached = get_transient('switchboard_available_blocks');
        if (false !== $cached) {
            return $cached;
        }
        
        // Fallback to current registry
        $all_blocks = WP_Block_Type_Registry::get_instance()->get_all_registered();
        
        $blocks = [];
        foreach ($all_blocks as $name => $block) {
            // Skip internal/hidden blocks
            if (strpos($name, 'core/') === false && strpos($name, '/') === false) {
                continue;
            }
            
            // Skip blocks without proper titles
            if (!isset($block->title) || empty($block->title)) {
                continue;
            }
            
            $blocks[] = [
                'name' => $name,
                'title' => $block->title,
                'category' => isset($block->category) ? $block->category : 'common'
            ];
        }
        
        // Sort by category then title
        usort($blocks, function($a, $b) {
            if ($a['category'] === $b['category']) {
                return strcmp($a['title'], $b['title']);
            }
            return strcmp($a['category'], $b['category']);
        });
        
        return $blocks;
    }

    /**
     * Render settings panel for Gutenberg Block Control
     * 
     * Displays interactive block management interface with:
     * - Searchable list of all available blocks
     * - Category-based organization
     * - Click-to-toggle disabled/enabled state
     * - Visual feedback for disabled blocks
     * - Refresh button to reload block list
     * 
     * @param array $settings Current module settings
     * @return void Outputs HTML directly
     */
    public function render_settings($settings = []) {
        $all_blocks = $this->get_all_blocks();
        $disabled_blocks = isset($settings['disabled_blocks']) ? $settings['disabled_blocks'] : '';
        $disabled_array = !empty($disabled_blocks) ? array_map('trim', explode(',', $disabled_blocks)) : [];

        // Localize strings for JavaScript
        $i18n = [
            'refreshing' => __('Refreshing...', 'wpswitchboard'),
            'refreshed' => __('Refreshed! Reloading...', 'wpswitchboard'),
            'failedRefresh' => __('Failed to refresh blocks. Please try again.', 'wpswitchboard'),
            'errorRefresh' => __('Error refreshing blocks. Please try again.', 'wpswitchboard'),
        ];
        ?>
        <input type="hidden" name="module" value="gutenberg-block-control">
        <input type="hidden" name="setting_disabled_blocks" id="setting_disabled_blocks" value="<?php echo esc_attr($disabled_blocks); ?>">

        <?php
        $cached_blocks = get_transient('switchboard_available_blocks');
        if (false === $cached_blocks || empty($all_blocks)): ?>
            <div class="sb-sp-alert-info">
                <p><strong><?php esc_html_e('First time setup:', 'wpswitchboard'); ?></strong> <?php esc_html_e('To see all available blocks (including those from plugins), please:', 'wpswitchboard'); ?></p>
                <ol class="sb-sp-my">
                    <li><?php esc_html_e('Open any post or page in the block editor', 'wpswitchboard'); ?></li>
                    <li><?php esc_html_e('Return to this page and click "Refresh Block List"', 'wpswitchboard'); ?></li>
                </ol>
            </div>
        <?php endif; ?>

        <div class="sb-sp-group sb-sp-flex sb-sp-items-center sb-sp-gap-2">
            <input type="text" id="block-search" placeholder="<?php echo esc_attr__('Search blocks...', 'wpswitchboard'); ?>" class="sb-sp-input sb-sp-col">
            <button type="button" id="refresh-blocks" class="button sb-sp-whitespace-nowrap"><?php esc_html_e('Refresh Block List', 'wpswitchboard'); ?></button>
        </div>

        <div class="sb-sp-group">
            <p class="sb-sp-description"><?php esc_html_e('Click blocks to disable them. Disabled blocks won\'t appear in the editor.', 'wpswitchboard'); ?> <strong><?php esc_html_e('Tip:', 'wpswitchboard'); ?></strong> <?php esc_html_e('If you don\'t see all your blocks, click "Refresh Block List" after opening the post editor. Disable "Buttons" to remove button container, and "Button" to remove individual buttons.', 'wpswitchboard'); ?></p>
        </div>
        
        <?php
        $current_category = '';
        foreach ($all_blocks as $block):
            if ($current_category !== $block['category']):
                if ($current_category !== ''):
                    echo '</div></div>';
                endif;
                $current_category = $block['category'];
                ?>
                <div class="sb-sp-group">
                    <h4 class="sb-sp-heading-sm"><?php echo esc_html(ucfirst($current_category)); ?></h4>
                    <div class="block-list">
            <?php endif;

            $is_disabled = in_array($block['name'], $disabled_array);
            $class = $is_disabled ? 'disabled' : 'enabled';
            ?>
            <div class="block-item <?php echo esc_attr($class); ?> sb-sp-inline-block" data-block="<?php echo esc_attr($block['name']); ?>">
                <?php echo esc_html($block['title']); ?>
                <?php if ($is_disabled): ?>
                    <span class="sb-sp-text-red-600">(disabled)</span>
                <?php endif; ?>
            </div>
        <?php endforeach; ?>
        </div></div>

        <script>
        jQuery(document).ready(function($) {
            const i18n = <?php echo json_encode($i18n); ?>;
            let disabledBlocks = <?php echo json_encode($disabled_array); ?>;
            
            // Toggle block
            $(".block-item").on("click", function() {
                const blockName = $(this).data("block");
                const index = disabledBlocks.indexOf(blockName);

                if (index > -1) {
                    disabledBlocks.splice(index, 1);
                    $(this).removeClass("disabled").addClass("enabled").find("span").remove();
                } else {
                    disabledBlocks.push(blockName);
                    $(this).removeClass("enabled").addClass("disabled").append(' <span class="sb-sp-text-red-600">(disabled)</span>');
                }

                $("#setting_disabled_blocks").val(disabledBlocks.join(","));
            });
            
            // Search functionality
            $("#block-search").on("input", function() {
                const query = $(this).val().toLowerCase();
                
                $(".block-item").each(function() {
                    const blockName = $(this).data("block").toLowerCase();
                    const blockText = $(this).text().toLowerCase();
                    
                    if (blockName.includes(query) || blockText.includes(query)) {
                        $(this).show();
                    } else {
                        $(this).hide();
                    }
                });
                
                // Hide empty categories
                $(".block-list").each(function() {
                    const visibleBlocks = $(this).find(".block-item:visible").length;
                    if (visibleBlocks === 0) {
                        $(this).prev("h4").hide();
                        $(this).hide();
                    } else {
                        $(this).prev("h4").show();
                        $(this).show();
                    }
                });
            });
            
            // Refresh blocks from cache
            $("#refresh-blocks").on("click", function() {
                const $btn = $(this);
                const originalText = $btn.text();
                $btn.prop("disabled", true).text(i18n.refreshing);

                $.ajax({
                    url: ajaxurl,
                    type: "POST",
                    data: {
                        action: "get_gutenberg_blocks",
                        nonce: "<?php echo esc_attr(wp_create_nonce('switchboard-nonce')); ?>"
                    },
                    success: function(response) {
                        if (response.success) {
                            // Store module name to reopen panel after reload
                            sessionStorage.setItem('switchboard_reopen_panel', 'gutenberg-block-control');
                            
                            // Show success message and reload
                            $btn.text(i18n.refreshed);
                            setTimeout(function() {
                                window.location.reload();
                            }, 500);
                        } else {
                            alert(i18n.failedRefresh);
                            $btn.prop("disabled", false).text(originalText);
                        }
                    },
                    error: function() {
                        alert(i18n.errorRefresh);
                        $btn.prop("disabled", false).text(originalText);
                    }
                });
            });
        });
        </script>
        <?php
    }

    /**
     * Register data locations for cleanup system
     */
    public function register_data_locations() {
        return [
            'options' => [
                'switchboard_gutenberg-block-control_settings',
            ],
            'option_patterns' => [
                '_transient_switchboard_gutenberg_blocks',
                '_transient_timeout_switchboard_gutenberg_blocks',
            ],
            'tables' => [],
            'post_meta' => [],
            'user_meta' => [],
        ];
    }
}

$switchboard_instance = new SwitchboardGutenbergBlockControl();
Switchboard_Data_Registry::get_instance()->auto_register($switchboard_instance, __FILE__);
