<?php
/**
 * Plugins Management Feature Class
 *
 * Displays all installed plugins with update status and provides management UI.
 *
 * @package     DiveWP
 * @author      Oleg Petrov
 * @version     1.0.0
 * @license     GPL-2.0-or-later
 */

// Exit if accessed directly
if (!defined('ABSPATH')) {
    die(esc_html__('Direct access not permitted.', 'divewp-boost-site-performance'));
}

/**
 * Plugins Management Feature Class
 */
class DiveWP_Plugins_Management {

    /**
     * Constructor
     */
    public function __construct() {
        add_action('admin_enqueue_scripts', array($this, 'enqueue_assets'));
        add_action('wp_ajax_divewp_toggle_plugin_status', array($this, 'handle_toggle_plugin_status'));
        add_action('wp_ajax_divewp_get_plugin_details', array($this, 'handle_get_plugin_details'));
        add_action('wp_ajax_divewp_get_plugin_versions', array($this, 'handle_get_plugin_versions'));
        add_action('wp_ajax_divewp_rollback_plugin_version', array($this, 'handle_rollback_plugin_version'));
        add_action('wp_ajax_divewp_update_plugin', array($this, 'handle_update_plugin'));
        add_action('wp_ajax_divewp_get_plugin_ratings_batch', array($this, 'handle_get_plugin_ratings_batch'));
        add_action('wp_ajax_divewp_get_plugin_icons_batch', array($this, 'handle_get_plugin_icons_batch'));
    }

    /**
     * Enqueue feature assets
     *
     * @param string $hook Current admin page hook
     * @return void
     */
    public function enqueue_assets($hook) {
        if ('toplevel_page_divewp' !== $hook) {
            return;
        }

        // Enqueue video hero CSS (reusable template)
        wp_enqueue_style(
            'divewp-video-hero',
            DIVEWP_PLUGIN_URL . 'assets/css/video-hero.css',
            array('divewp-style'),
            DIVEWP_VERSION
        );

        // Enqueue filter bar CSS (reusable template)
        wp_enqueue_style(
            'divewp-filter-bar',
            DIVEWP_PLUGIN_URL . 'assets/css/filter-bar.css',
            array('divewp-style'),
            DIVEWP_VERSION
        );

        // Enqueue plugins management CSS
        wp_enqueue_style(
            'divewp-plugins-management',
            DIVEWP_PLUGIN_URL . 'assets/css/features/plugins-management.css',
            array('divewp-style', 'divewp-global', 'divewp-video-hero', 'divewp-filter-bar'),
            DIVEWP_VERSION
        );

        // Enqueue filter bar JS (reusable template)
        wp_enqueue_script(
            'divewp-filter-bar',
            DIVEWP_PLUGIN_URL . 'assets/js/divewp-filter-bar.js',
            array('jquery'),
            DIVEWP_VERSION,
            true
        );

        // Enqueue plugins management JS
        wp_enqueue_script(
            'divewp-plugins-management',
            DIVEWP_PLUGIN_URL . 'assets/js/divewp-plugins-management.js',
            array('jquery', 'divewp-admin-js', 'divewp-filter-bar'),
            DIVEWP_VERSION,
            true
        );

        // Localize script
        wp_localize_script('divewp-plugins-management', 'divewpPluginsData', array(
            'ajaxurl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('divewp_plugins_nonce'),
            'strings' => array(
                'activate' => __('Activate', 'divewp-boost-site-performance'),
                'deactivate' => __('Deactivate', 'divewp-boost-site-performance'),
                'update' => __('Update', 'divewp-boost-site-performance'),
                'active' => __('Active', 'divewp-boost-site-performance'),
                'inactive' => __('Inactive', 'divewp-boost-site-performance'),
                'updateAvailable' => __('Update Available', 'divewp-boost-site-performance'),
                'upToDate' => __('Up to date', 'divewp-boost-site-performance'),
                'details' => __('Details', 'divewp-boost-site-performance'),
                'error' => __('Error occurred. Please try again.', 'divewp-boost-site-performance'),
                'confirmDeactivate' => __('Are you sure you want to deactivate this plugin?', 'divewp-boost-site-performance'),
                'overview' => __('Overview', 'divewp-boost-site-performance'),
                'description' => __('Description', 'divewp-boost-site-performance'),
                'changelog' => __('Changelog', 'divewp-boost-site-performance'),
                'loadingDetails' => __('Loading details...', 'divewp-boost-site-performance'),
                'notAvailable' => __('Not available', 'divewp-boost-site-performance'),
                'versions' => __('Versions', 'divewp-boost-site-performance'),
                'loadingVersions' => __('Loading versions…', 'divewp-boost-site-performance'),
                'versionsNotAvailable' => __('Version history is only available for WordPress.org plugins.', 'divewp-boost-site-performance'),
                'installedVersion' => __('Installed Version', 'divewp-boost-site-performance'),
                'installed' => __('Installed', 'divewp-boost-site-performance'),
                'installThisVersion' => __('Install this version', 'divewp-boost-site-performance'),
                'loadMore' => __('Load more versions', 'divewp-boost-site-performance'),
                'rollbackNotice' => __('Rollback replaces plugin files with an older version; database schema and options are not reverted.', 'divewp-boost-site-performance'),
                /* translators: %s: Version number of the plugin to roll back to. */
                'rollbackWarning' => __('This will replace plugin files with version %s. Database changes made by newer versions will NOT be rolled back. Proceed with caution.', 'divewp-boost-site-performance'),
                'rollbackReactivateLabel' => __('Re-activate plugin after rollback', 'divewp-boost-site-performance'),
                'rollbackReactivateHint' => __('If the older version is incompatible, the plugin will remain deactivated.', 'divewp-boost-site-performance'),
                'confirmRollback' => __('Confirm Rollback', 'divewp-boost-site-performance'),
                'cancel' => __('Cancel', 'divewp-boost-site-performance'),
                'rollingBack' => __('Rolling back…', 'divewp-boost-site-performance'),
                /* translators: 1: Previous version number, 2: Target version number after rollback. */
                'rollbackSuccess' => __('Successfully rolled back from v%1$s to v%2$s.', 'divewp-boost-site-performance'),
                'rollbackError' => __('Rollback failed. Please try again.', 'divewp-boost-site-performance'),
                'noVersionsFound' => __('No version history found for this plugin.', 'divewp-boost-site-performance'),
                'reviews' => __('Reviews', 'divewp-boost-site-performance'),
                'loadingReviews' => __('Loading reviews…', 'divewp-boost-site-performance'),
                'reviewsNotAvailable' => __('Reviews are only available for WordPress.org plugins.', 'divewp-boost-site-performance'),
                'noReviewsFound' => __('No reviews found for this plugin.', 'divewp-boost-site-performance'),
                'viewAllReviews' => __('View all reviews on WordPress.org', 'divewp-boost-site-performance'),
                'updating' => __('Updating…', 'divewp-boost-site-performance'),
                'updatePlugin' => __('Update Plugin', 'divewp-boost-site-performance'),
                /* translators: 1: Previous version number, 2: New version number after update. */
                'updateSuccess' => __('Successfully updated from v%1$s to v%2$s.', 'divewp-boost-site-performance'),
                'updateError' => __('Update failed. Please try again.', 'divewp-boost-site-performance'),
                /* translators: %s: number of ratings */
                'ratingsCount' => __('%s ratings', 'divewp-boost-site-performance'),
            ),
        ));
    }

    /**
     * Get all installed plugins with update info
     *
     * @return array Array of plugin data
     */
    private function get_all_plugins() {
        // Require WordPress plugin functions
        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $all_plugins = get_plugins();
        $active_plugins = get_option('active_plugins', array());
        $update_plugins = get_site_transient('update_plugins');

        $plugins_data = array();

        foreach ($all_plugins as $plugin_file => $plugin_data) {
            $is_active = in_array($plugin_file, $active_plugins, true);
            $has_update = !empty($update_plugins->response) && isset($update_plugins->response[$plugin_file]);
            $wporg_slug = $this->resolve_wporg_slug($plugin_file, $plugin_data, $update_plugins);

            $plugins_data[] = array(
                'file' => $plugin_file,
                'name' => $plugin_data['Name'],
                'version' => $plugin_data['Version'],
                'description' => $plugin_data['Description'],
                'author' => $plugin_data['Author'],
                'is_active' => $is_active,
                'has_update' => $has_update,
                'update_version' => $has_update ? $update_plugins->response[$plugin_file]->new_version : '',
                'plugin_url' => isset($plugin_data['PluginURI']) ? $plugin_data['PluginURI'] : '',
                'author_url' => isset($plugin_data['AuthorURI']) ? $plugin_data['AuthorURI'] : '',
                'wporg_slug' => $wporg_slug,
            );
        }

        // Sort by 4-tier priority: active+update, inactive+update, active+current, inactive+current
        usort($plugins_data, function($a, $b) {
            $priority_a = $this->get_plugin_sort_priority($a);
            $priority_b = $this->get_plugin_sort_priority($b);

            if ($priority_a !== $priority_b) {
                return $priority_a - $priority_b;
            }
            return strcasecmp($a['name'], $b['name']);
        });

        return $plugins_data;
    }

    /**
     * Get sort priority for a plugin (lower = higher priority)
     *
     * 1 = Active + has update (needs immediate attention)
     * 2 = Inactive + has update (pending updates)
     * 3 = Active + no update (healthy, running fine)
     * 4 = Inactive + no update (lowest priority)
     *
     * @param array $plugin Plugin data array
     * @return int Priority value 1-4
     */
    private function get_plugin_sort_priority($plugin) {
        if ($plugin['is_active'] && $plugin['has_update']) {
            return 1;
        }
        if (!$plugin['is_active'] && $plugin['has_update']) {
            return 2;
        }
        if ($plugin['is_active'] && !$plugin['has_update']) {
            return 3;
        }
        return 4;
    }

    /**
     * Resolve wp.org slug from plugin data
     */
    private function resolve_wporg_slug($plugin_file, $plugin_data, $update_plugins) {
        // Try update transient - plugins with available updates
        if (!empty($update_plugins->response) && isset($update_plugins->response[$plugin_file]->slug)) {
            return $update_plugins->response[$plugin_file]->slug;
        }

        // Try update transient - plugins that are up-to-date (still tracked by wp.org)
        if (!empty($update_plugins->no_update) && isset($update_plugins->no_update[$plugin_file]->slug)) {
            return $update_plugins->no_update[$plugin_file]->slug;
        }

        // Try to extract from PluginURI
        if (!empty($plugin_data['PluginURI'])) {
            if (preg_match('#wordpress\.org/plugins/([a-z0-9\-]+)/?$#i', $plugin_data['PluginURI'], $matches)) {
                return $matches[1];
            }
        }

        return null;
    }

    /**
     * Count plugins with updates
     *
     * @return int Number of plugins with updates
     */
    private function count_plugins_with_updates() {
        $plugins = $this->get_all_plugins();
        return count(array_filter($plugins, function($p) {
            return $p['has_update'];
        }));
    }

    /**
     * Render the plugins management page
     */
    public function render() {
        // Verify user capabilities
        if (!current_user_can('manage_options')) {
            wp_die(esc_html__('You do not have sufficient permissions to access this page.', 'divewp-boost-site-performance'));
        }

        $plugins = $this->get_all_plugins();
        $update_count = $this->count_plugins_with_updates();
        $total_count = count($plugins);
        ?>
        <div class="divewp-plugins-management-dashboard">
            <h3><?php esc_html_e('Plugins Management', 'divewp-boost-site-performance'); ?></h3>
            
            <p class="divewp-plugins-intro">
                <?php esc_html_e('Monitor and manage all installed plugins on your WordPress site. View updates, activate or deactivate plugins, and keep your site secure and optimized.', 'divewp-boost-site-performance'); ?>
            </p>

            <?php $this->render_video_hero(); ?>

            <?php $this->render_hero_bar($total_count, $update_count); ?>

            <?php $this->render_plugins_grid($plugins); ?>

            <?php $this->render_plugins_drawer(); ?>
        </div>
        <?php
    }

    /**
     * Render video hero section using reusable template
     *
     * @return void
     */
    private function render_video_hero() {
        // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Template variables passed to included template
        $title       = __('WordPress Plugins: How to Keep Your Site Secure and Fast', 'divewp-boost-site-performance');
        $description = __('Learn best practices for managing WordPress plugins—choosing reliable plugins, keeping them updated, and removing ones that slow down or compromise your site.', 'divewp-boost-site-performance');
        $video_id    = 'YBlJtqacXPI';
        $badge_text  = __('Video Guide', 'divewp-boost-site-performance');
        $features    = array(
            __('Keep plugins updated', 'divewp-boost-site-performance'),
            __('Identify security risks', 'divewp-boost-site-performance'),
        );
        // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound

        include DIVEWP_PLUGIN_DIR . 'includes/templates/video-hero-template.php';
    }

    /**
     * Render hero context bar with filter buttons and search using reusable template
     *
     * @param int $total_count Total number of plugins
     * @param int $update_count Number of plugins with updates
     * @return void
     */
    private function render_hero_bar($total_count, $update_count) {
        // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- Template variables passed to included template
        $plugins = $this->get_all_plugins();
        $active_count = count(array_filter($plugins, function($p) {
            return $p['is_active'];
        }));
        $inactive_count = $total_count - $active_count;

        $filter_bar_id      = 'divewp-plugins-filter-bar';
        $default_filter     = 'all';
        $search_placeholder = __('Search by name, author, or description...', 'divewp-boost-site-performance');
        $show_search        = true;
        $filters            = array(
            array(
                'key'   => 'all',
                'label' => __('All', 'divewp-boost-site-performance'),
                'count' => $total_count,
            ),
            array(
                'key'   => 'active',
                'label' => __('Active', 'divewp-boost-site-performance'),
                'count' => $active_count,
            ),
            array(
                'key'   => 'inactive',
                'label' => __('Inactive', 'divewp-boost-site-performance'),
                'count' => $inactive_count,
            ),
        );

        if ($update_count > 0) {
            $filters[] = array(
                'key'     => 'updates',
                'label'   => __('Updates', 'divewp-boost-site-performance'),
                'count'   => $update_count,
                'variant' => 'warning',
            );
        }
        // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound

        include DIVEWP_PLUGIN_DIR . 'includes/templates/filter-bar-template.php';
    }

    /**
     * Render plugins grid
     *
     * @param array $plugins Array of plugin data
     * @return void
     */
    private function render_plugins_grid($plugins) {
        if (empty($plugins)) {
            ?>
            <div class="divewp-plugins-empty">
                <span class="dashicons dashicons-admin-plugins"></span>
                <p><?php esc_html_e('No plugins found.', 'divewp-boost-site-performance'); ?></p>
            </div>
            <?php
            return;
        }

        ?>
        <div class="divewp-plugins-grid">
            <?php foreach ($plugins as $plugin) : ?>
                <div class="recommendation-card divewp-plugin-card" 
                    data-plugin-file="<?php echo esc_attr($plugin['file']); ?>"
                    data-plugin-name="<?php echo esc_attr($plugin['name']); ?>"
                    data-plugin-version="<?php echo esc_attr($plugin['version']); ?>"
                    data-plugin-description="<?php echo esc_attr($plugin['description']); ?>"
                    data-plugin-author="<?php echo esc_attr($plugin['author']); ?>"
                    data-plugin-active="<?php echo $plugin['is_active'] ? '1' : '0'; ?>"
                    data-plugin-update="<?php echo $plugin['has_update'] ? '1' : '0'; ?>"
                    data-plugin-update-version="<?php echo esc_attr($plugin['update_version']); ?>"
                    data-plugin-wporg-slug="<?php echo esc_attr($plugin['wporg_slug'] ?: ''); ?>"
                    data-plugin-url="<?php echo esc_attr($plugin['plugin_url']); ?>"
                    data-plugin-author-url="<?php echo esc_attr($plugin['author_url']); ?>"
                >
                    
                    <div class="recommendation-top">
                        <div class="recommendation-header">
                            <div class="recommendation-icon">
                                <span class="dashicons dashicons-admin-plugins"></span>
                            </div>
                            <h4 class="recommendation-title"><?php echo esc_html($plugin['name']); ?></h4>
                        </div>
                    </div>

                    <div class="recommendation-middle">
                        <div class="recommendation-content">
                            <p><?php echo esc_html(wp_trim_words($plugin['description'], 15)); ?></p>
                            <div class="divewp-plugin-meta">
                                <?php /* translators: %s: Plugin version number */ ?>
                                <span class="divewp-plugin-version"><?php printf(esc_html__('v%s', 'divewp-boost-site-performance'), esc_html($plugin['version'])); ?></span>
                                <?php if (!empty($plugin['author'])) : ?>
                                    <span class="divewp-plugin-author">
                                        <?php /* translators: %s: Plugin author name */ ?>
                                        <?php printf(esc_html__('By %s', 'divewp-boost-site-performance'), esc_html($plugin['author'])); ?>
                                    </span>
                                <?php endif; ?>
                            </div>
                            <?php if (!empty($plugin['wporg_slug'])) : ?>
                                <div class="divewp-rating-summary divewp-rating-summary--loading" data-rating-slug="<?php echo esc_attr($plugin['wporg_slug']); ?>">
                                    <span class="divewp-rating-summary__placeholder">&nbsp;</span>
                                </div>
                            <?php endif; ?>
                        </div>
                    </div>

                    <div class="recommendation-bottom">
                        <div class="divewp-plugin-badges">
                            <?php if ($plugin['is_active']) : ?>
                                <span class="status-pill status-pill-info"><?php esc_html_e('Active', 'divewp-boost-site-performance'); ?></span>
                            <?php else : ?>
                                <span class="status-pill status-pill-warning"><?php esc_html_e('Inactive', 'divewp-boost-site-performance'); ?></span>
                            <?php endif; ?>

                            <?php // Show update badge for ALL plugins (active or inactive) ?>
                            <?php if ($plugin['has_update']) : ?>
                                <span class="status-pill status-pill-danger"><?php esc_html_e('Update Available', 'divewp-boost-site-performance'); ?></span>
                            <?php elseif ($plugin['is_active']) : ?>
                                <?php // Only show "Up to date" for active plugins ?>
                                <span class="status-pill status-pill-success"><?php esc_html_e('Up to date', 'divewp-boost-site-performance'); ?></span>
                            <?php endif; ?>
                        </div>
                    </div>
                </div>
            <?php endforeach; ?>
        </div>
        <?php
    }

    /**
     * Render plugins drawer modal
     *
     * @return void
     */
    private function render_plugins_drawer() {
        ?>
        <div class="divewp-plugins-drawer" aria-hidden="true">
            <div class="divewp-plugins-drawer__overlay"></div>
            <div class="divewp-plugins-drawer__panel">
                <div class="divewp-plugins-drawer__header">
                    <h4 class="divewp-plugins-drawer__title"><?php esc_html_e('Plugin Details', 'divewp-boost-site-performance'); ?></h4>
                    <button type="button" class="divewp-plugins-drawer__close">
                        <span class="dashicons dashicons-no-alt"></span>
                    </button>
                </div>
                <div class="divewp-plugins-drawer__tabs">
                    <button type="button" class="divewp-plugins-drawer__tab divewp-plugins-drawer__tab--active" data-tab="overview">
                        <?php esc_html_e('Overview', 'divewp-boost-site-performance'); ?>
                    </button>
                    <button type="button" class="divewp-plugins-drawer__tab" data-tab="description">
                        <?php esc_html_e('Description', 'divewp-boost-site-performance'); ?>
                    </button>
                    <button type="button" class="divewp-plugins-drawer__tab" data-tab="changelog">
                        <?php esc_html_e('Changelog', 'divewp-boost-site-performance'); ?>
                    </button>
                    <button type="button" class="divewp-plugins-drawer__tab" data-tab="reviews">
                        <?php esc_html_e('Reviews', 'divewp-boost-site-performance'); ?>
                    </button>
                    <button type="button" class="divewp-plugins-drawer__tab" data-tab="versions">
                        <?php esc_html_e('Versions', 'divewp-boost-site-performance'); ?>
                    </button>
                </div>
                <div class="divewp-plugins-drawer__content">
                    <!-- Content loaded dynamically -->
                </div>
                <div class="divewp-plugins-drawer__footer">
                    <button type="button" class="button button-secondary divewp-plugins-drawer__action" data-action="toggle">
                        <span class="dashicons dashicons-controls-play"></span>
                        <span class="action-label"><?php esc_html_e('Toggle', 'divewp-boost-site-performance'); ?></span>
                    </button>
                    <button type="button" class="button button-primary divewp-plugins-drawer__action divewp-plugins-drawer__update" data-action="update" style="display:none;">
                        <span class="dashicons dashicons-update"></span>
                        <span class="update-label"><?php esc_html_e('Update Plugin', 'divewp-boost-site-performance'); ?></span>
                    </button>
                </div>
            </div>
        </div>
        <?php
    }

    /**
     * AJAX handler for toggling plugin status (activate/deactivate)
     *
     * @return void
     */
    public function handle_toggle_plugin_status() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'divewp_plugins_nonce')) {
            wp_send_json_error(esc_html__('Invalid nonce.', 'divewp-boost-site-performance'));
            return;
        }

        // Check capability
        if (!current_user_can('activate_plugins')) {
            wp_send_json_error(esc_html__('You do not have permission to activate plugins.', 'divewp-boost-site-performance'));
            return;
        }

        // Get plugin file
        if (!isset($_POST['plugin_file'])) {
            wp_send_json_error(esc_html__('Plugin file not specified.', 'divewp-boost-site-performance'));
            return;
        }

        $plugin_file = sanitize_text_field(wp_unslash($_POST['plugin_file']));

        // Validate plugin file against actual installed plugins
        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $all_plugins = get_plugins();
        if (!isset($all_plugins[$plugin_file])) {
            wp_send_json_error(esc_html__('Invalid plugin file.', 'divewp-boost-site-performance'));
            return;
        }

        // Get current status
        $active_plugins = get_option('active_plugins', array());
        $is_active = in_array($plugin_file, $active_plugins, true);

        // Toggle status
        if ($is_active) {
            deactivate_plugins($plugin_file);
            $new_status = false;
        } else {
            activate_plugins($plugin_file);
            $new_status = true;
        }

        wp_send_json_success(array(
            'is_active' => $new_status,
            'message' => $new_status 
                ? esc_html__('Plugin activated.', 'divewp-boost-site-performance')
                : esc_html__('Plugin deactivated.', 'divewp-boost-site-performance'),
        ));
    }

    /**
     * AJAX handler for fetching plugin wp.org details
     *
     * @return void
     */
    public function handle_get_plugin_details() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'divewp_plugins_nonce')) {
            wp_send_json_error(esc_html__('Invalid nonce.', 'divewp-boost-site-performance'));
            return;
        }

        // Check capability
        if (!current_user_can('manage_options')) {
            wp_send_json_error(esc_html__('You do not have permission to access this.', 'divewp-boost-site-performance'));
            return;
        }

        if (!isset($_POST['plugin_file'])) {
            wp_send_json_error(esc_html__('Plugin file not specified.', 'divewp-boost-site-performance'));
            return;
        }

        $plugin_file = sanitize_text_field(wp_unslash($_POST['plugin_file']));

        // Validate plugin file against actual installed plugins
        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $all_plugins = get_plugins();
        if (!isset($all_plugins[$plugin_file])) {
            wp_send_json_error(esc_html__('Invalid plugin file.', 'divewp-boost-site-performance'));
            return;
        }

        $plugin_data = $all_plugins[$plugin_file];
        $update_plugins = get_site_transient('update_plugins');
        $wporg_slug = $this->resolve_wporg_slug($plugin_file, $plugin_data, $update_plugins);

        if (!$wporg_slug) {
            wp_send_json_success(array(
                'wporg_available' => false,
                'message' => esc_html__('Plugin is not from wp.org.', 'divewp-boost-site-performance'),
            ));
            return;
        }

        // Check transient cache first
        $cache_key = 'divewp_plugin_info_' . sanitize_key($wporg_slug);
        $cached = get_transient($cache_key);

        if (false !== $cached) {
            wp_send_json_success(array(
                'wporg_available'   => true,
                'description'       => isset($cached['description']) ? $cached['description'] : '',
                'changelog'         => isset($cached['changelog']) ? $cached['changelog'] : '',
                'reviews_html'      => isset($cached['reviews_html']) ? $cached['reviews_html'] : '',
                'rating_percent'    => isset($cached['rating_percent']) ? $cached['rating_percent'] : 0,
                'avg_rating'        => isset($cached['avg_rating']) ? $cached['avg_rating'] : 0,
                'num_ratings'       => isset($cached['num_ratings']) ? $cached['num_ratings'] : 0,
                'ratings_breakdown' => isset($cached['ratings_breakdown']) ? $cached['ratings_breakdown'] : array(),
                'wporg_slug'        => $wporg_slug,
            ));
            return;
        }

        // Fetch from wp.org API
        $api_result = $this->fetch_wporg_plugin_info($wporg_slug);

        if (is_wp_error($api_result)) {
            wp_send_json_error($api_result->get_error_message());
            return;
        }

        // Cache the result for 6 hours
        set_transient(
            $cache_key,
            array(
                'description'       => $api_result['description'],
                'changelog'         => $api_result['changelog'],
                'reviews_html'      => $api_result['reviews_html'],
                'rating_percent'    => $api_result['rating_percent'],
                'avg_rating'        => $api_result['avg_rating'],
                'num_ratings'       => $api_result['num_ratings'],
                'ratings_breakdown' => $api_result['ratings_breakdown'],
            ),
            6 * HOUR_IN_SECONDS
        );

        wp_send_json_success(array(
            'wporg_available'   => true,
            'description'       => $api_result['description'],
            'changelog'         => $api_result['changelog'],
            'reviews_html'      => $api_result['reviews_html'],
            'rating_percent'    => $api_result['rating_percent'],
            'avg_rating'        => $api_result['avg_rating'],
            'num_ratings'       => $api_result['num_ratings'],
            'ratings_breakdown' => $api_result['ratings_breakdown'],
            'wporg_slug'        => $wporg_slug,
        ));
    }

    /**
     * Fetch plugin info from wp.org API
     *
     * @param string $slug Plugin slug
     * @return array|WP_Error Plugin info array or error
     */
    private function fetch_wporg_plugin_info($slug) {
        // Ensure plugins_api is available
        if (!function_exists('plugins_api')) {
            require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
        }

        // Use plugins_api to fetch from wp.org — include ratings and reviews
        $plugin_info = plugins_api(
            'plugin_information',
            array(
                'slug' => $slug,
                'fields' => array(
                    'description' => true,
                    'sections'    => true,
                    'reviews'     => true,
                    'rating'      => true,
                    'ratings'     => true,
                    'num_ratings' => true,
                ),
            )
        );

        if (is_wp_error($plugin_info)) {
            return $plugin_info;
        }

        // Extract and sanitize description
        $description = isset($plugin_info->description) ? wp_kses_post($plugin_info->description) : '';

        // Extract and sanitize changelog
        $changelog = '';
        if (isset($plugin_info->sections['changelog'])) {
            $changelog = wp_kses_post($plugin_info->sections['changelog']);
        }

        // Extract and sanitize reviews HTML
        $reviews_html = '';
        if (isset($plugin_info->sections['reviews'])) {
            $reviews_html = wp_kses_post($plugin_info->sections['reviews']);
        }

        // Rating data (rating is 0-100 percentage, convert to 0-5 avg)
        $rating_percent = isset($plugin_info->rating) ? (int) $plugin_info->rating : 0;
        $num_ratings    = isset($plugin_info->num_ratings) ? (int) $plugin_info->num_ratings : 0;
        $avg_rating     = round($rating_percent / 20, 1); // 0-100 → 0.0-5.0

        // Per-star breakdown
        $ratings_breakdown = array();
        if (isset($plugin_info->ratings) && is_array($plugin_info->ratings)) {
            foreach ($plugin_info->ratings as $stars => $count) {
                $ratings_breakdown[ (string) $stars ] = (int) $count;
            }
        }

        return array(
            'description'       => $description,
            'changelog'         => $changelog,
            'reviews_html'      => $reviews_html,
            'rating_percent'    => $rating_percent,
            'avg_rating'        => $avg_rating,
            'num_ratings'       => $num_ratings,
            'ratings_breakdown' => $ratings_breakdown,
        );
    }

    // =========================================================================
    // Ratings — batch endpoint for card summaries
    // =========================================================================

    /**
     * AJAX handler for fetching wp.org rating summaries in batch
     *
     * Accepts a comma-separated list of wp.org slugs (max 100) and returns
     * rating data for each, using per-slug transient caching (24 hours).
     *
     * @return void
     */
    public function handle_get_plugin_ratings_batch() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'divewp_plugins_nonce')) {
            wp_send_json_error(esc_html__('Invalid nonce.', 'divewp-boost-site-performance'));
            return;
        }

        // Check capability
        if (!current_user_can('manage_options')) {
            wp_send_json_error(esc_html__('You do not have permission to access this.', 'divewp-boost-site-performance'));
            return;
        }

        if (!isset($_POST['slugs'])) {
            wp_send_json_error(esc_html__('No slugs provided.', 'divewp-boost-site-performance'));
            return;
        }

        $raw_slugs = sanitize_text_field(wp_unslash($_POST['slugs']));
        $slugs     = array_unique(array_filter(array_map('trim', explode(',', $raw_slugs))));

        if (empty($slugs) || count($slugs) > 100) {
            wp_send_json_error(esc_html__('Invalid number of slugs (1–100).', 'divewp-boost-site-performance'));
            return;
        }

        $results       = array();
        $slugs_to_fetch = array();

        // Check per-slug transient cache first
        foreach ($slugs as $slug) {
            $cache_key = 'divewp_plugin_rating_' . sanitize_key($slug);
            $cached    = get_transient($cache_key);

            if (false !== $cached && is_array($cached)) {
                $results[ $slug ] = $cached;
            } else {
                $slugs_to_fetch[] = $slug;
            }
        }

        // Fetch uncached slugs from wp.org API using batch endpoint
        if (!empty($slugs_to_fetch)) {
            $fetched = $this->fetch_wporg_ratings_batch($slugs_to_fetch);
            foreach ($fetched as $slug => $rating_data) {
                $results[ $slug ] = $rating_data;

                // Cache per slug for 24 hours
                $cache_key = 'divewp_plugin_rating_' . sanitize_key($slug);
                set_transient($cache_key, $rating_data, DAY_IN_SECONDS);
            }
        }

        wp_send_json_success($results);
    }

    /**
     * Fetch rating summaries for multiple wp.org plugins in one HTTP request
     *
     * Uses the wp.org API v1.2 batch endpoint (request[slugs]).
     *
     * @param array $slugs Array of plugin slugs.
     * @return array Keyed by slug, each value has avg_rating, rating_percent, num_ratings.
     */
    private function fetch_wporg_ratings_batch($slugs) {
        $url = add_query_arg(
            array(
                'action'                       => 'plugin_information',
                'request[slugs]'               => implode(',', $slugs),
                'request[fields][rating]'      => '1',
                'request[fields][ratings]'     => '1',
                'request[fields][num_ratings]' => '1',
                'request[fields][sections]'    => '0',
                'request[fields][description]' => '0',
                'request[fields][versions]'    => '0',
            ),
            'https://api.wordpress.org/plugins/info/1.2/'
        );

        $response = wp_remote_get($url, array(
            'timeout'    => 15,
            'user-agent' => 'WordPress/' . get_bloginfo('version') . '; ' . home_url('/'),
        ));

        $results = array();

        if (is_wp_error($response)) {
            return $results;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);

        if (!is_array($body)) {
            return $results;
        }

        foreach ($slugs as $slug) {
            if (isset($body[ $slug ]) && !isset($body[ $slug ]['error'])) {
                $plugin_data    = $body[ $slug ];
                $rating_percent = isset($plugin_data['rating']) ? (int) $plugin_data['rating'] : 0;
                $num_ratings    = isset($plugin_data['num_ratings']) ? (int) $plugin_data['num_ratings'] : 0;
                $avg_rating     = round($rating_percent / 20, 1);

                $results[ $slug ] = array(
                    'rating_percent' => $rating_percent,
                    'avg_rating'     => $avg_rating,
                    'num_ratings'    => $num_ratings,
                );
            }
        }

        return $results;
    }

    // =========================================================================
    // Icons — batch endpoint for plugin logos (wp.org CDN, no download)
    // =========================================================================

    /**
     * Allowed hosts for plugin icon URLs (WordPress.org CDN).
     */
    private static function get_icon_allowed_hosts() {
        return array('ps.w.org', 'wordpress.org', 's.w.org');
    }

    /**
     * Check if a URL is a valid icon URL (https + allowed host).
     *
     * @param string $url URL to validate.
     * @return bool
     */
    private function is_valid_icon_url($url) {
        if (!is_string($url) || '' === trim($url)) {
            return false;
        }
        $parsed = wp_parse_url($url);
        if (empty($parsed['host']) || empty($parsed['scheme'])) {
            return false;
        }
        if ('https' !== strtolower($parsed['scheme'])) {
            return false;
        }
        $host = strtolower($parsed['host']);
        foreach (self::get_icon_allowed_hosts() as $allowed) {
            if ($host === $allowed) {
                return true;
            }
            $suffix = '.' . $allowed;
            if (strlen($host) >= strlen($suffix) && substr($host, -strlen($suffix)) === $suffix) {
                return true;
            }
        }
        return false;
    }

    /**
     * Pick the best icon URL from wp.org icons array (priority: 256, 2x, 1x, default).
     *
     * @param array $icons Icons array from API (keys like default, 2x, 1x, icon-256x256, etc.).
     * @return string|null Valid icon URL or null.
     */
    private function pick_best_icon_url($icons) {
        if (!is_array($icons)) {
            return null;
        }
        $order = array('icon-256x256', 'icon-128x128', '2x', '1x', 'default');
        foreach ($order as $key) {
            if (!empty($icons[ $key ]) && $this->is_valid_icon_url($icons[ $key ])) {
                return $icons[ $key ];
            }
        }
        return null;
    }

    /**
     * AJAX handler for fetching wp.org plugin icon URLs in batch
     *
     * Accepts comma-separated slugs (max 100), returns slug => { icon_url } or null.
     * Uses per-slug transient cache (7 days).
     *
     * @return void
     */
    public function handle_get_plugin_icons_batch() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'divewp_plugins_nonce')) {
            wp_send_json_error(esc_html__('Invalid nonce.', 'divewp-boost-site-performance'));
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(esc_html__('You do not have permission to access this.', 'divewp-boost-site-performance'));
            return;
        }

        if (!isset($_POST['slugs'])) {
            wp_send_json_error(esc_html__('No slugs provided.', 'divewp-boost-site-performance'));
            return;
        }

        $raw_slugs = sanitize_text_field(wp_unslash($_POST['slugs']));
        $slugs     = array_unique(array_filter(array_map('trim', explode(',', $raw_slugs))));

        if (empty($slugs) || count($slugs) > 100) {
            wp_send_json_error(esc_html__('Invalid number of slugs (1–100).', 'divewp-boost-site-performance'));
            return;
        }

        $results       = array();
        $slugs_to_fetch = array();

        foreach ($slugs as $slug) {
            $cache_key = 'divewp_plugin_icon_' . sanitize_key($slug);
            $cached    = get_transient($cache_key);

            if (false !== $cached && is_string($cached) && $cached !== '') {
                $results[ $slug ] = array('icon_url' => $cached);
            } elseif (false !== $cached && $cached === '') {
                $results[ $slug ] = null;
            } else {
                $slugs_to_fetch[] = $slug;
            }
        }

        if (!empty($slugs_to_fetch)) {
            $fetched = $this->fetch_wporg_icons_batch($slugs_to_fetch);
            foreach ($fetched as $slug => $icon_url) {
                if ($icon_url !== null) {
                    $results[ $slug ] = array('icon_url' => $icon_url);
                    set_transient('divewp_plugin_icon_' . sanitize_key($slug), $icon_url, 7 * DAY_IN_SECONDS);
                } else {
                    $results[ $slug ] = null;
                    set_transient('divewp_plugin_icon_' . sanitize_key($slug), '', 7 * DAY_IN_SECONDS);
                }
            }
        }

        wp_send_json_success($results);
    }

    /**
     * Fetch icon URLs for multiple wp.org plugins in one HTTP request
     *
     * @param array $slugs Array of plugin slugs.
     * @return array Keyed by slug, value is icon URL string or null.
     */
    private function fetch_wporg_icons_batch($slugs) {
        $url = add_query_arg(
            array(
                'action'                 => 'plugin_information',
                'request[slugs]'         => implode(',', $slugs),
                'request[fields][icons]' => '1',
                'request[fields][sections]'    => '0',
                'request[fields][description]' => '0',
                'request[fields][versions]'   => '0',
            ),
            'https://api.wordpress.org/plugins/info/1.2/'
        );

        $response = wp_remote_get($url, array(
            'timeout'    => 15,
            'user-agent' => 'WordPress/' . get_bloginfo('version') . '; ' . home_url('/'),
        ));

        $results = array();

        if (is_wp_error($response)) {
            foreach ($slugs as $slug) {
                $results[ $slug ] = null;
            }
            return $results;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);

        if (!is_array($body)) {
            foreach ($slugs as $slug) {
                $results[ $slug ] = null;
            }
            return $results;
        }

        foreach ($slugs as $slug) {
            if (isset($body[ $slug ]['error'])) {
                $results[ $slug ] = null;
                continue;
            }
            $icons = isset($body[ $slug ]['icons']) ? $body[ $slug ]['icons'] : array();
            $icon_url = $this->pick_best_icon_url($icons);
            $results[ $slug ] = $icon_url;
        }

        return $results;
    }

    // =========================================================================
    // Versions / Rollback — shared helpers
    // =========================================================================

    /**
     * Get wp.org version history for a plugin
     *
     * Resolves the wp.org slug, calls plugins_api with versions=true,
     * caches the result in a transient (12 hours), filters out trunk,
     * and returns versions sorted newest-first.
     *
     * @param string $plugin_file Plugin file path (e.g. "akismet/akismet.php").
     * @return array|WP_Error Structured versions data or WP_Error on failure.
     */
    public function get_wporg_versions($plugin_file) {
        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $all_plugins = get_plugins();
        if (!isset($all_plugins[$plugin_file])) {
            return new \WP_Error('invalid_plugin', __('Plugin not found.', 'divewp-boost-site-performance'));
        }

        $plugin_data    = $all_plugins[$plugin_file];
        $update_plugins = get_site_transient('update_plugins');
        $wporg_slug     = $this->resolve_wporg_slug($plugin_file, $plugin_data, $update_plugins);

        if (!$wporg_slug) {
            return array(
                'wporg_available'   => false,
                'installed_version' => $plugin_data['Version'],
                'versions'          => array(),
            );
        }

        // Check transient cache (12 hours)
        $cache_key = 'divewp_plugin_versions_' . sanitize_key($wporg_slug);
        $cached    = get_transient($cache_key);

        if (false !== $cached && is_array($cached)) {
            // Always return fresh installed_version
            $cached['installed_version'] = $plugin_data['Version'];
            return $cached;
        }

        // Fetch from wp.org API
        if (!function_exists('plugins_api')) {
            require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
        }

        $plugin_info = plugins_api(
            'plugin_information',
            array(
                'slug'   => $wporg_slug,
                'fields' => array(
                    'versions' => true,
                ),
            )
        );

        if (is_wp_error($plugin_info)) {
            return $plugin_info;
        }

        $raw_versions = isset($plugin_info->versions) ? (array) $plugin_info->versions : array();

        // Filter out trunk, build normalized list
        $versions = array();
        foreach ($raw_versions as $version => $download_url) {
            if ('trunk' === $version || '' === $version) {
                continue;
            }
            $versions[] = array(
                'version'      => $version,
                'download_url' => $download_url,
            );
        }

        // Sort newest-first using version_compare
        usort($versions, function ($a, $b) {
            return version_compare($b['version'], $a['version']);
        });

        $result = array(
            'wporg_available'   => true,
            'installed_version' => $plugin_data['Version'],
            'versions'          => $versions,
        );

        // Cache for 12 hours (without installed_version so cache stays valid)
        set_transient($cache_key, $result, 12 * HOUR_IN_SECONDS);

        return $result;
    }

    /**
     * Perform a guarded rollback of a wp.org plugin to a specific version
     *
     * Uses Plugin_Upgrader with Automatic_Upgrader_Skin (silent) and
     * overwrite_package=true. The download URL is always resolved server-side
     * from the wp.org versions map — never accepted from the client.
     *
     * @param string $plugin_file    Plugin file path.
     * @param string $target_version Version string to rollback to.
     * @param bool   $reactivate     Whether to re-activate the plugin after rollback.
     * @return array Structured result with success, previous_version, target_version, is_active, message, warnings.
     */
    public function perform_rollback($plugin_file, $target_version, $reactivate = false) {
        // Capability check
        if (!current_user_can('update_plugins')) {
            return array(
                'success'  => false,
                'message'  => __('You do not have permission to update plugins.', 'divewp-boost-site-performance'),
                'warnings' => array(),
            );
        }

        // Get versions list (also validates plugin exists and has wporg slug)
        $versions_data = $this->get_wporg_versions($plugin_file);

        if (is_wp_error($versions_data)) {
            return array(
                'success'  => false,
                'message'  => $versions_data->get_error_message(),
                'warnings' => array(),
            );
        }

        if (empty($versions_data['wporg_available'])) {
            return array(
                'success'  => false,
                'message'  => __('Rollback is only available for WordPress.org plugins.', 'divewp-boost-site-performance'),
                'warnings' => array(),
            );
        }

        // Find the download URL for the target version (server-side resolution)
        $download_url = '';
        foreach ($versions_data['versions'] as $v) {
            if ($v['version'] === $target_version) {
                $download_url = $v['download_url'];
                break;
            }
        }

        if (empty($download_url)) {
            return array(
                'success'  => false,
                'message'  => sprintf(
                    /* translators: %s: requested version string */
                    __('Version %s not found in the WordPress.org versions list.', 'divewp-boost-site-performance'),
                    $target_version
                ),
                'warnings' => array(),
            );
        }

        $previous_version = $versions_data['installed_version'];

        // Check if plugin was active before rollback
        $active_plugins  = get_option('active_plugins', array());
        $was_active      = in_array($plugin_file, $active_plugins, true);

        // Require upgrader classes (admin-only files)
        require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
        require_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php';
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/misc.php';
        require_once ABSPATH . 'wp-admin/includes/plugin.php';

        // Use silent skin for AJAX context
        $skin     = new \Automatic_Upgrader_Skin();
        $upgrader = new \Plugin_Upgrader($skin);

        // Deactivate plugin before overwriting to avoid partial-load fatals
        if ($was_active) {
            deactivate_plugins($plugin_file, true);
        }

        // Install the target version, overwriting existing files
        $result = $upgrader->install($download_url, array(
            'overwrite_package' => true,
        ));

        // Force-refresh update_plugins transient so resolve_wporg_slug() works
        // after install() clears the plugin from the transient.
        if (!function_exists('wp_update_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/update.php';
        }
        wp_update_plugins();

        $warnings = array(
            __('Database changes are not rolled back.', 'divewp-boost-site-performance'),
        );

        // Handle upgrader failure
        if (is_wp_error($result)) {
            $error_msg = $this->map_upgrader_error($result);

            // Try to reactivate if it was active before (files unchanged on error)
            if ($was_active) {
                activate_plugins($plugin_file);
            }

            return array(
                'success'          => false,
                'previous_version' => $previous_version,
                'target_version'   => $target_version,
                'is_active'        => $was_active,
                'message'          => $error_msg,
                'warnings'         => $warnings,
            );
        }

        if (true !== $result) {
            // Upgrader returned false or unexpected value
            $feedback = $skin->get_upgrade_messages();
            $error_msg = !empty($feedback) ? implode(' ', $feedback) : __('Rollback failed for an unknown reason.', 'divewp-boost-site-performance');

            // Try to reactivate if it was active before
            if ($was_active) {
                activate_plugins($plugin_file);
            }

            return array(
                'success'          => false,
                'previous_version' => $previous_version,
                'target_version'   => $target_version,
                'is_active'        => $was_active,
                'message'          => $error_msg,
                'warnings'         => $warnings,
            );
        }

        // Rollback succeeded — optionally reactivate
        $is_active = false;
        if ($reactivate) {
            $activation_result = activate_plugins($plugin_file);
            if (is_wp_error($activation_result)) {
                $warnings[] = sprintf(
                    /* translators: %s: error message from activation */
                    __('Plugin rolled back but could not be reactivated: %s', 'divewp-boost-site-performance'),
                    $activation_result->get_error_message()
                );
            } else {
                $is_active = true;
            }
        }

        return array(
            'success'          => true,
            'previous_version' => $previous_version,
            'target_version'   => $target_version,
            'is_active'        => $is_active,
            'message'          => sprintf(
                /* translators: 1: previous version, 2: target version */
                __('Successfully rolled back from v%1$s to v%2$s.', 'divewp-boost-site-performance'),
                $previous_version,
                $target_version
            ),
            'warnings'         => $warnings,
        );
    }

    /**
     * Map common WP_Error codes from the upgrader to user-friendly messages
     *
     * @param WP_Error $error The upgrader error.
     * @return string User-friendly error message.
     */
    private function map_upgrader_error($error) {
        $code = $error->get_error_code();

        $messages = array(
            'fs_no_direct'     => __('The server filesystem is not directly accessible. Rollback requires direct filesystem access.', 'divewp-boost-site-performance'),
            'no_package'       => __('The download package could not be found. The version may no longer be available.', 'divewp-boost-site-performance'),
            'download_failed'  => __('Failed to download the plugin package. Please check your server connectivity and try again.', 'divewp-boost-site-performance'),
            'incompatible_archive' => __('The downloaded package is incompatible or corrupted.', 'divewp-boost-site-performance'),
            'mkdir_failed'     => __('Could not create directory. Please check filesystem permissions.', 'divewp-boost-site-performance'),
        );

        if (isset($messages[$code])) {
            return $messages[$code];
        }

        return $error->get_error_message();
    }

    // =========================================================================
    // Versions / Rollback — AJAX handlers
    // =========================================================================

    /**
     * AJAX handler for fetching plugin wp.org versions list
     *
     * @return void
     */
    public function handle_get_plugin_versions() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'divewp_plugins_nonce')) {
            wp_send_json_error(esc_html__('Invalid nonce.', 'divewp-boost-site-performance'));
            return;
        }

        // Check capability
        if (!current_user_can('manage_options')) {
            wp_send_json_error(esc_html__('You do not have permission to access this.', 'divewp-boost-site-performance'));
            return;
        }

        if (!isset($_POST['plugin_file'])) {
            wp_send_json_error(esc_html__('Plugin file not specified.', 'divewp-boost-site-performance'));
            return;
        }

        $plugin_file = sanitize_text_field(wp_unslash($_POST['plugin_file']));

        $result = $this->get_wporg_versions($plugin_file);

        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }

        wp_send_json_success($result);
    }

    /**
     * AJAX handler for rolling back a plugin to a specific version
     *
     * @return void
     */
    public function handle_rollback_plugin_version() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'divewp_plugins_nonce')) {
            wp_send_json_error(esc_html__('Invalid nonce.', 'divewp-boost-site-performance'));
            return;
        }

        // Check capability
        if (!current_user_can('update_plugins')) {
            wp_send_json_error(esc_html__('You do not have permission to update plugins.', 'divewp-boost-site-performance'));
            return;
        }

        if (!isset($_POST['plugin_file']) || !isset($_POST['target_version'])) {
            wp_send_json_error(esc_html__('Plugin file and target version are required.', 'divewp-boost-site-performance'));
            return;
        }

        $plugin_file    = sanitize_text_field(wp_unslash($_POST['plugin_file']));
        $target_version = sanitize_text_field(wp_unslash($_POST['target_version']));
        $reactivate     = !empty($_POST['reactivate']);

        $result = $this->perform_rollback($plugin_file, $target_version, $reactivate);

        if (!empty($result['success'])) {
            wp_send_json_success($result);
        } else {
            wp_send_json_error($result);
        }
    }

    /**
     * AJAX handler for updating a plugin to its latest version
     *
     * Uses Plugin_Upgrader with Automatic_Upgrader_Skin for silent in-modal update.
     *
     * @return void
     */
    public function handle_update_plugin() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'divewp_plugins_nonce')) {
            wp_send_json_error(array('message' => esc_html__('Invalid nonce.', 'divewp-boost-site-performance')));
            return;
        }

        if (!current_user_can('update_plugins')) {
            wp_send_json_error(array('message' => esc_html__('You do not have permission to update plugins.', 'divewp-boost-site-performance')));
            return;
        }

        if (!isset($_POST['plugin_file'])) {
            wp_send_json_error(array('message' => esc_html__('Plugin file not specified.', 'divewp-boost-site-performance')));
            return;
        }

        $plugin_file = sanitize_text_field(wp_unslash($_POST['plugin_file']));

        if (!function_exists('get_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $all_plugins = get_plugins();
        if (!isset($all_plugins[$plugin_file])) {
            wp_send_json_error(array('message' => esc_html__('Invalid plugin file.', 'divewp-boost-site-performance')));
            return;
        }

        $update_plugins = get_site_transient('update_plugins');
        if (empty($update_plugins->response) || !isset($update_plugins->response[$plugin_file])) {
            wp_send_json_error(array('message' => esc_html__('No update available for this plugin.', 'divewp-boost-site-performance')));
            return;
        }

        $previous_version = $all_plugins[$plugin_file]['Version'];
        $new_version      = $update_plugins->response[$plugin_file]->new_version;

        require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
        require_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php';
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/misc.php';
        require_once ABSPATH . 'wp-admin/includes/plugin.php';

        $skin     = new \Automatic_Upgrader_Skin();
        $upgrader = new \Plugin_Upgrader($skin);

        $result = $upgrader->upgrade($plugin_file);

        if (is_wp_error($result)) {
            wp_send_json_error(array(
                'message' => $this->map_upgrader_error($result),
            ));
            return;
        }

        if (true !== $result) {
            $feedback  = $skin->get_upgrade_messages();
            $error_msg = !empty($feedback) ? implode(' ', $feedback) : __('Update failed for an unknown reason.', 'divewp-boost-site-performance');
            wp_send_json_error(array('message' => $error_msg));
            return;
        }

        if (!function_exists('wp_update_plugins')) {
            require_once ABSPATH . 'wp-admin/includes/update.php';
        }
        wp_update_plugins();

        $active_plugins = get_option('active_plugins', array());
        $is_active      = in_array($plugin_file, $active_plugins, true);

        wp_send_json_success(array(
            'previous_version' => $previous_version,
            'new_version'      => $new_version,
            'is_active'        => $is_active,
            'message'          => sprintf(
                /* translators: 1: previous version, 2: new version */
                __('Successfully updated from v%1$s to v%2$s.', 'divewp-boost-site-performance'),
                $previous_version,
                $new_version
            ),
        ));
    }
}
