<?php

/**
 * Bulk Edit Integration for Post List Tables
 *
 * @package ProRank\SEO\Admin
 */

declare(strict_types=1);

namespace ProRank\SEO\Admin;

use ProRank\SEO\Core\FocusKeywords;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}


/**
 * BulkEditIntegration class
 */


class BulkEditIntegration {

    /**
     * Valid SEO filter values
     */
    private const VALID_SEO_FILTERS = ['good', 'ok', 'poor', 'none'];

    /**
     * Valid keyword filter values
     */
    private const VALID_KW_FILTERS = ['set', 'none'];

    /**
     * Supported post types for filtering
     */
    private const SUPPORTED_POST_TYPES = ['post', 'page'];

    /**
     * Supported post-level robots directives for quick/bulk edit.
     */
    private const META_ROBOTS_DIRECTIVES = ['noindex', 'nofollow', 'noarchive', 'nosnippet', 'noimageindex'];

    /**
     * Constructor
     */
    public function __construct() {
        $this->init_hooks();
    }

    /**
     * Initialize hooks
     */
    private function init_hooks(): void {
        // Add bulk actions to post list
        add_filter('bulk_actions-edit-post', [$this, 'add_bulk_actions']);
        add_filter('bulk_actions-edit-page', [$this, 'add_bulk_actions']);

        // Handle bulk actions
        add_filter('handle_bulk_actions-edit-post', [$this, 'handle_bulk_action'], 10, 3);
        add_filter('handle_bulk_actions-edit-page', [$this, 'handle_bulk_action'], 10, 3);

        // Add columns to post list
        add_filter('manage_posts_columns', [$this, 'add_seo_columns']);
        add_filter('manage_pages_columns', [$this, 'add_seo_columns']);

        // Display column content
        add_action('manage_posts_custom_column', [$this, 'display_seo_column'], 10, 2);
        add_action('manage_pages_custom_column', [$this, 'display_seo_column'], 10, 2);

        // Make columns sortable
        add_filter('manage_edit-post_sortable_columns', [$this, 'make_columns_sortable']);
        add_filter('manage_edit-page_sortable_columns', [$this, 'make_columns_sortable']);

        // Add quick edit fields
        add_action('quick_edit_custom_box', [$this, 'add_quick_edit_fields'], 10, 2);
        add_action('bulk_edit_custom_box', [$this, 'add_bulk_edit_fields'], 10, 2);

        // Save quick edit data
        add_action('save_post', [$this, 'save_quick_edit_data']);

        // Add admin scripts
        add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);

        // Add admin notices
        add_action('admin_notices', [$this, 'display_bulk_action_notices']);

        // Add filter dropdowns
        add_action('restrict_manage_posts', [$this, 'add_filter_dropdowns']);

        // Filter posts by SEO status
        add_filter('pre_get_posts', [$this, 'filter_posts_by_seo']);

        // Ensure proper sorting for SEO score column
        add_filter('posts_clauses', [$this, 'orderby_seo_score'], 10, 2);
    }
    
    /**
     * Add bulk actions to dropdown
     *
     * @param array $bulk_actions Existing bulk actions.
     * @return array
     */
    public function add_bulk_actions(array $bulk_actions): array {
        // Check if bulk editing is enabled for this post type
        $screen = get_current_screen();
        $post_type = $screen->post_type ?? 'post';
        
        $settings = get_option('prorank_seo_titles_meta', []);
        if (!is_array($settings) || empty($settings)) {
            $settings = get_option('prorank_titles_meta_settings', []);
        }
        $post_type_settings = $settings["post_type_{$post_type}"] ?? [];
        
        // Always enable for posts and pages, check settings for others
        if ($post_type === 'post' || $post_type === 'page' || !empty($post_type_settings['bulk_editing'])) {
            $bulk_actions['prorank_bulk_edit_seo'] = __('Edit SEO Settings', 'prorank-seo');
            
            // Add AI optimization if user has Pro license
            if ($this->has_ai_access()) {
                $bulk_actions['prorank_ai_optimize_seo'] = __('AI Optimize SEO', 'prorank-seo');
            }
        }
        
        return $bulk_actions;
    }
    
    /**
     * Handle bulk action
     *
     * @param string $redirect_to Redirect URL.
     * @param string $doaction Action being performed.
     * @param array  $post_ids Post IDs.
     * @return string
     */
    public function handle_bulk_action(string $redirect_to, string $doaction, array $post_ids): string {
        if ($doaction === 'prorank_bulk_edit_seo') {
            // Use the dedicated hidden bulk-edit page so WordPress can resolve the
            // callback reliably even when the bulk editor is opened directly.
            $redirect_to = add_query_arg([
                'page' => 'prorank-bulk-edit',
                'post_ids' => implode(',', $post_ids),
                'post_type' => get_post_type($post_ids[0]),
            ], admin_url('admin.php'));
            
        } elseif ($doaction === 'prorank_ai_optimize_seo') {
            // Process AI optimization
            $optimized_count = $this->process_ai_optimization($post_ids);
            
            $redirect_to = add_query_arg([
                'prorank_ai_optimized' => $optimized_count,
                'prorank_ai_total' => count($post_ids),
            ], $redirect_to);
        }
        
        return $redirect_to;
    }
    
    /**
     * Add SEO columns to post list
     *
     * @param array $columns Existing columns.
     * @return array
     */
    public function add_seo_columns(array $columns): array {
        $new_columns = [];
        $seo_columns_added = false;

        foreach ($columns as $key => $label) {
            // Always preserve the column
            $new_columns[$key] = $label;

            // Add SEO columns after title
            if ($key === 'title' && !$seo_columns_added) {
                $new_columns['prorank_seo_score'] = '<span class="prorank-seo-column-header" title="' . esc_attr__('SEO Score', 'prorank-seo') . '">SEO</span>';
                $new_columns['prorank_focus_keyword'] = __('Keyword', 'prorank-seo');
                $new_columns['prorank_seo_title'] = __('SEO Title', 'prorank-seo');
                $new_columns['prorank_seo_desc'] = __('Meta Desc', 'prorank-seo');
                $seo_columns_added = true;
            }
        }

        // Fallback: if title column wasn't found, add Title and SEO columns after cb
        if (!$seo_columns_added) {
            $final_columns = [];
            $title_in_original = array_key_exists('title', $new_columns);
            $title_label = $columns['title'] ?? __('Title', 'prorank-seo');
            foreach ($new_columns as $key => $label) {
                $final_columns[$key] = $label;
                if ($key === 'cb' && !$title_in_original) {
                    $final_columns['title'] = $title_label;
                    $final_columns['prorank_seo_score'] = '<span class="prorank-seo-column-header" title="' . esc_attr__('SEO Status', 'prorank-seo') . '">' . esc_html__('Status', 'prorank-seo') . '</span>';
                    $final_columns['prorank_focus_keyword'] = __('Keyword', 'prorank-seo');
                    $final_columns['prorank_seo_title'] = __('SEO Title', 'prorank-seo');
                    $final_columns['prorank_seo_desc'] = __('Meta Desc', 'prorank-seo');
                }
            }
            return $final_columns;
        }

        return $new_columns;
    }
    
    /**
     * Display SEO column content
     *
     * @param string $column Column name.
     * @param int    $post_id Post ID.
     */
    public function display_seo_column(string $column, int $post_id): void {
        switch ($column) {
            case 'prorank_seo_score':
                $score = get_post_meta($post_id, '_prorank_seo_score', true);
                $this->display_seo_score_circle($score, $post_id);
                break;

            case 'prorank_focus_keyword':
                $keyword = FocusKeywords::get_primary($post_id);
                $this->display_focus_keyword($keyword);
                break;

            case 'prorank_seo_title':
                $title = get_post_meta($post_id, '_prorank_seo_title', true);
                $this->display_meta_with_progress($title, 60, 'title');
                $this->render_inline_seo_state($post_id);
                break;

            case 'prorank_seo_desc':
                $desc = get_post_meta($post_id, '_prorank_seo_description', true);
                $this->display_meta_with_progress($desc, 160, 'description');
                break;
        }
    }

    /**
     * Display SEO status as a compact badge while keeping numeric score for tooltips and sorting.
     *
     * @param mixed $score SEO score.
     * @param int   $post_id Post ID.
     */
    private function display_seo_score_circle($score, int $post_id): void {
        if ($score === '' || $score === null) {
            echo '<span class="prorank-status-badge empty" aria-label="' . esc_attr__('SEO status: Not analyzed', 'prorank-seo') . '" title="' . esc_attr__('SEO status: Not analyzed', 'prorank-seo') . '">' . esc_html__('Not analyzed', 'prorank-seo') . '</span>';
            return;
        }

        $score = intval($score);
        $class = $score >= 80 ? 'strong' : ($score >= 60 ? 'solid' : 'needs-work');
        $label = $score >= 80 ? __('Strong', 'prorank-seo') : ($score >= 60 ? __('Solid', 'prorank-seo') : __('Needs work', 'prorank-seo'));

        // Accessibility: meaningful aria-label
        $aria_label = sprintf(
            /* translators: 1: status label, 2: score number */
            __('SEO Status: %1$s (%2$d/100)', 'prorank-seo'),
            $label,
            $score
        );

        echo '<span class="prorank-status-badge ' . esc_attr($class) . '"';
        echo ' data-score="' . esc_attr($score) . '"';
        echo ' data-post-id="' . esc_attr($post_id) . '"';
        echo ' aria-label="' . esc_attr($aria_label) . '"';
/* translators: 1: status label, 2: score number */
        echo ' title="' . esc_attr(sprintf(__('SEO Status: %1$s (%2$d/100)', 'prorank-seo'), $label, $score)) . '">';
        echo esc_html($label);
        echo '</span>';
    }

    /**
     * Display focus keyword badge
     *
     * @param string $keyword Focus keyword.
     */
    private function display_focus_keyword(string $keyword): void {
        if ($keyword) {
            echo '<span class="prorank-keyword-badge set" title="' . esc_attr($keyword) . '">';
            $keyword_length = $this->safe_strlen($keyword);
            $keyword_display = $keyword_length > 15
                ? $this->safe_substr($keyword, 0, 15) . '...'
                : $keyword;
            echo esc_html($keyword_display);
            echo '</span>';
        } else {
            echo '<span class="prorank-keyword-badge empty" aria-label="' . esc_attr__('No focus keyword set', 'prorank-seo') . '">—</span>';
        }
    }

    /**
     * Display meta field with progress bar
     *
     * @param string $value     The meta value.
     * @param int    $max_length Maximum recommended length.
     * @param string $type      Type of meta (title or description).
     */
    private function display_meta_with_progress(string $value, int $max_length, string $type): void {
        if (!$value) {
            echo '<div class="prorank-meta-column empty">';
/* translators: %1$: value */
            echo '<span class="meta-text empty" aria-label="' . esc_attr(sprintf(__('No %1$s set', 'prorank-seo'), $type)) . '">—</span>';
            echo '</div>';
            return;
        }

        // Strip HTML and count multibyte characters correctly
        $clean_value = wp_strip_all_tags($value);
        $length = $this->safe_strlen($clean_value);
        $percent = min(100, ($length / $max_length) * 100);

        // Use SEO-focused length ranges instead of a generic percentage threshold.
        // Title: good 30-60, warning 20-29 or 61-70, over otherwise.
        // Description: good 120-160, warning 90-119 or 161-180, over otherwise.
        if ($type === 'title') {
            if ($length >= 30 && $length <= 60) {
                $class = 'good';
            } elseif (($length >= 20 && $length < 30) || ($length > 60 && $length <= 70)) {
                $class = 'warning';
            } else {
                $class = 'over';
            }
        } elseif ($type === 'description') {
            if ($length >= 120 && $length <= 160) {
                $class = 'good';
            } elseif (($length >= 90 && $length < 120) || ($length > 160 && $length <= 180)) {
                $class = 'warning';
            } else {
                $class = 'over';
            }
        } else {
            $class = $length > $max_length ? 'over' : ($percent > 80 ? 'warning' : 'good');
        }

        // Accessibility: aria-label for screen readers
        $type_label = $type === 'title' ? __('SEO Title', 'prorank-seo') : __('Meta Description', 'prorank-seo');
        $aria_label = sprintf(
            /* translators: 1: type (SEO Title/Meta Description), 2: current length, 3: max length */
            __('%1$s: %2$d of %3$d characters', 'prorank-seo'),
            $type_label,
            $length,
            $max_length
        );

        echo '<div class="prorank-meta-column" aria-label="' . esc_attr($aria_label) . '">';

        // Store full value in data attribute for quick edit prefill
        echo '<span class="meta-text" data-full-value="' . esc_attr($clean_value) . '">';
        echo esc_html($this->safe_substr($clean_value, 0, 40));
        if ($length > 40) {
            echo '...';
        }
        echo '</span>';

        // Progress bar
        echo '<div class="prorank-progress-bar ' . esc_attr($class) . '" role="progressbar" aria-valuenow="' . esc_attr($length) . '" aria-valuemin="0" aria-valuemax="' . esc_attr($max_length) . '">';
        echo '<div class="progress-fill" style="width: ' . esc_attr($percent) . '%"></div>';
        echo '</div>';

        // Character count
        echo '<span class="char-count">' . esc_html($length) . '/' . esc_html($max_length) . '</span>';

        echo '</div>';
    }
    
    /**
     * Make columns sortable
     *
     * @param array $columns Columns.
     * @return array
     */
    public function make_columns_sortable(array $columns): array {
        $columns['prorank_seo_score'] = 'prorank_seo_score';
        return $columns;
    }
    
    /**
     * Add quick edit fields
     *
     * @param string $column_name Column name.
     * @param string $post_type Post type.
     */
    public function add_quick_edit_fields(string $column_name, string $post_type): void {
        if ($column_name !== 'prorank_seo_title') {
            return;
        }
        ?>
        <fieldset class="inline-edit-col-right prorank-quick-edit-fields">
            <div class="inline-edit-col">
                <h4><?php esc_html_e('ProRank SEO', 'prorank-seo'); ?></h4>
                <?php wp_nonce_field('prorank_quick_edit', 'prorank_quick_edit_nonce'); ?>
                <label>
                    <span class="title"><?php esc_html_e('Focus Keyword', 'prorank-seo'); ?></span>
                    <span class="input-text-wrap">
                        <input type="text" name="prorank_focus_keyword" class="pkeyword" value="" />
                    </span>
                </label>
                <label>
                    <span class="title"><?php esc_html_e('SEO Title', 'prorank-seo'); ?></span>
                    <span class="input-text-wrap">
                        <input type="text" name="prorank_seo_title" class="ptitle" value="" maxlength="70" />
                        <span class="prorank-char-counter" data-max="60"></span>
                    </span>
                </label>
                <label>
                    <span class="title"><?php esc_html_e('Meta Description', 'prorank-seo'); ?></span>
                    <span class="input-text-wrap">
                        <textarea name="prorank_seo_description" class="pdesc" rows="2" maxlength="200"></textarea>
                        <span class="prorank-char-counter" data-max="160"></span>
                    </span>
                </label>
                <label>
                    <span class="title"><?php esc_html_e('Canonical URL', 'prorank-seo'); ?></span>
                    <span class="input-text-wrap">
                        <input type="url" name="prorank_seo_canonical_url" class="pcanonical" value="" placeholder="<?php esc_attr_e('Default permalink', 'prorank-seo'); ?>" />
                    </span>
                </label>
                <div class="prorank-quick-edit-robots">
                    <span class="title"><?php esc_html_e('Robots', 'prorank-seo'); ?></span>
                    <span class="input-text-wrap prorank-robot-options">
                        <?php foreach ($this->get_meta_robots_labels() as $directive => $label) : ?>
                            <label class="prorank-robot-option">
                                <input type="checkbox" name="prorank_meta_robots[]" value="<?php echo esc_attr($directive); ?>" />
                                <span><?php echo esc_html($label); ?></span>
                            </label>
                        <?php endforeach; ?>
                    </span>
                </div>
            </div>
        </fieldset>
        <?php
    }
    
    /**
     * Add bulk edit fields
     *
     * @param string $column_name Column name.
     * @param string $post_type Post type.
     */
    public function add_bulk_edit_fields(string $column_name, string $post_type): void {
        if ($column_name !== 'prorank_seo_title') {
            return;
        }
        ?>
        <fieldset class="inline-edit-col-right prorank-bulk-edit-fields">
            <div class="inline-edit-col">
                <h4><?php esc_html_e('ProRank SEO Settings', 'prorank-seo'); ?></h4>
                <?php wp_nonce_field('prorank_bulk_edit', 'prorank_bulk_edit_nonce'); ?>
                <p class="description">
                    <?php esc_html_e('Leave fields empty to keep existing values', 'prorank-seo'); ?>
                </p>
                <label>
                    <span class="title"><?php esc_html_e('SEO Title', 'prorank-seo'); ?></span>
                    <span class="input-text-wrap">
                        <input type="text" name="prorank_bulk_seo_title" value="" placeholder="<?php esc_attr_e('No change', 'prorank-seo'); ?>" />
                    </span>
                </label>
                <label>
                    <span class="title"><?php esc_html_e('Meta Description', 'prorank-seo'); ?></span>
                    <span class="input-text-wrap">
                        <textarea name="prorank_bulk_seo_description" rows="2" placeholder="<?php esc_attr_e('No change', 'prorank-seo'); ?>"></textarea>
                    </span>
                </label>
                <label>
                    <span class="title"><?php esc_html_e('Canonical URL', 'prorank-seo'); ?></span>
                    <span class="input-text-wrap">
                        <input type="url" name="prorank_bulk_seo_canonical_url" value="" placeholder="<?php esc_attr_e('No change', 'prorank-seo'); ?>" />
                    </span>
                </label>
                <label class="prorank-bulk-checkbox">
                    <input type="checkbox" name="prorank_bulk_clear_canonical_url" value="1" />
                    <span class="checkbox-title"><?php esc_html_e('Clear existing canonical URL', 'prorank-seo'); ?></span>
                </label>
                <div class="prorank-bulk-robots">
                    <span class="title"><?php esc_html_e('Robots', 'prorank-seo'); ?></span>
                    <span class="input-text-wrap prorank-robot-select-grid">
                        <?php foreach ($this->get_meta_robots_labels() as $directive => $label) : ?>
                            <label class="prorank-bulk-robot-field">
                                <span class="prorank-bulk-robot-label"><?php echo esc_html($label); ?></span>
                                <select name="<?php echo esc_attr('prorank_bulk_' . $directive); ?>">
                                    <option value=""><?php esc_html_e('No change', 'prorank-seo'); ?></option>
                                    <option value="enable"><?php esc_html_e('Enable', 'prorank-seo'); ?></option>
                                    <option value="disable"><?php esc_html_e('Disable', 'prorank-seo'); ?></option>
                                </select>
                            </label>
                        <?php endforeach; ?>
                    </span>
                </div>
                <?php if ($this->has_ai_access()): ?>
                <label>
                    <input type="checkbox" name="prorank_ai_optimize" value="1" />
                    <span class="checkbox-title"><?php esc_html_e('Use AI to optimize titles and descriptions', 'prorank-seo'); ?></span>
                </label>
                <?php endif; ?>
            </div>
        </fieldset>
        <?php
    }
    
    /**
     * Save quick edit data
     *
     * @param int $post_id Post ID.
     */
    public function save_quick_edit_data(int $post_id): void {
        if ( \ProRank\SEO\Plugin::is_elementor_builder_save_request() ) {
            return;
        }

        // Prevent autosave
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
            return;
        }

        // Check capability
        if (!current_user_can('edit_post', $post_id)) {
            return;
        }

        // Check for bulk edit
        if (prorank_get_request_var('bulk_edit') !== '') {
            // Verify bulk edit nonce
            $bulk_edit_nonce = (string) prorank_get_input_value($_REQUEST, 'prorank_bulk_edit_nonce', '');
            if ($bulk_edit_nonce === '' || !wp_verify_nonce($bulk_edit_nonce, 'prorank_bulk_edit')) {
                return;
            }

            $bulk_seo_title = prorank_get_request_var('prorank_bulk_seo_title');
            if ($bulk_seo_title !== '') {
                update_post_meta($post_id, '_prorank_seo_title', $bulk_seo_title);
            }
            $bulk_seo_description = (string) prorank_get_input_value($_REQUEST, 'prorank_bulk_seo_description', '');
            if ($bulk_seo_description !== '') {
                update_post_meta($post_id, '_prorank_seo_description', sanitize_textarea_field($bulk_seo_description));
            }

            $bulk_canonical_url = '';
            if (isset($_REQUEST['prorank_bulk_seo_canonical_url'])) {
                $bulk_canonical_url = esc_url_raw(trim(wp_unslash((string) $_REQUEST['prorank_bulk_seo_canonical_url'])));
            }
            if ($bulk_canonical_url !== '') {
                update_post_meta($post_id, '_prorank_seo_canonical_url', $bulk_canonical_url);
            } elseif (!empty($_REQUEST['prorank_bulk_clear_canonical_url'])) {
                delete_post_meta($post_id, '_prorank_seo_canonical_url');
            }

            $current_meta_robots = $this->normalize_meta_robots(get_post_meta($post_id, '_prorank_meta_robots', true));
            $updated_meta_robots = $this->apply_bulk_meta_robots($current_meta_robots, $_REQUEST);
            if ($updated_meta_robots !== $current_meta_robots) {
                update_post_meta($post_id, '_prorank_meta_robots', $updated_meta_robots);
            }

            // Handle AI optimization
            if (prorank_get_request_var('prorank_ai_optimize') !== '') {
                $this->queue_ai_optimization($post_id);
            }
            return;
        }

        // Verify quick edit nonce
        $quick_edit_nonce = (string) prorank_get_input_value($_REQUEST, 'prorank_quick_edit_nonce', '');
        if ($quick_edit_nonce === '' || !wp_verify_nonce($quick_edit_nonce, 'prorank_quick_edit')) {
            return;
        }

        // Save Focus Keyword (preserve existing secondary keywords)
        if (prorank_has_input($_REQUEST, 'prorank_focus_keyword')) {
            $kw_value = prorank_get_request_var('prorank_focus_keyword');
            if ($kw_value !== '') {
                $existing  = FocusKeywords::get($post_id);
                $secondary = array_filter($existing, function ($kw) { return empty($kw['primary']); });
                $input     = array_merge([['keyword' => $kw_value, 'primary' => true]], array_values($secondary));
            } else {
                $input = [];
            }
            FocusKeywords::save($post_id, $input);
        }

        // Save SEO Title
        if (prorank_has_input($_REQUEST, 'prorank_seo_title')) {
            update_post_meta($post_id, '_prorank_seo_title', prorank_get_request_var('prorank_seo_title'));
        }

        // Save Meta Description
        if (prorank_has_input($_REQUEST, 'prorank_seo_description')) {
            update_post_meta(
                $post_id,
                '_prorank_seo_description',
                sanitize_textarea_field((string) prorank_get_input_value($_REQUEST, 'prorank_seo_description', ''))
            );
        }

        if (isset($_REQUEST['prorank_seo_canonical_url'])) {
            $canonical_url = esc_url_raw(trim(wp_unslash((string) $_REQUEST['prorank_seo_canonical_url'])));
            if ($canonical_url === '') {
                delete_post_meta($post_id, '_prorank_seo_canonical_url');
            } else {
                update_post_meta($post_id, '_prorank_seo_canonical_url', $canonical_url);
            }
        }

        $meta_robots = $this->normalize_meta_robots($_REQUEST['prorank_meta_robots'] ?? []);
        update_post_meta($post_id, '_prorank_meta_robots', $meta_robots);
    }
    
    /**
     * Enqueue admin scripts
     *
     * @param string $hook Hook suffix.
     */
    public function enqueue_scripts(string $hook): void {
        if ($hook !== 'edit.php') {
            return;
        }

        $css_path = PRORANK_PLUGIN_DIR . 'assets/css/admin/bulk-edit.css';
        $js_path = PRORANK_PLUGIN_DIR . 'assets/js/admin/bulk-edit.js';
        $css_version = file_exists($css_path) ? filemtime($css_path) : PRORANK_VERSION;
        $js_version = file_exists($js_path) ? filemtime($js_path) : PRORANK_VERSION;
        
        wp_enqueue_style(
            'prorank-bulk-edit',
            PRORANK_PLUGIN_URL . 'assets/css/admin/bulk-edit.css',
            [],
            $css_version
        );
        
        wp_enqueue_script(
            'prorank-bulk-edit',
            PRORANK_PLUGIN_URL . 'assets/js/admin/bulk-edit.js',
            ['jquery'],
            $js_version,
            true
        );
        
        wp_localize_script('prorank-bulk-edit', 'prorankBulkEdit', [
            'nonce' => wp_create_nonce('prorank_bulk_edit'),
            'ajaxurl' => admin_url('admin-ajax.php'),
            'strings' => [
                'optimizing' => __('Optimizing with AI...', 'prorank-seo'),
                'error' => __('An error occurred', 'prorank-seo'),
            ],
        ]);
    }
    
    /**
     * Display bulk action notices
     */
    public function display_bulk_action_notices(): void {
        if (prorank_get_request_var('prorank_ai_optimized') !== '') {
            $optimized = intval(prorank_get_request_var('prorank_ai_optimized'));
            $total = intval(prorank_get_request_var('prorank_ai_total'));

            printf(
                '<div class="notice notice-success is-dismissible"><p>%s</p></div>',
                sprintf(
                    /* translators: 1: optimized posts, 2: total posts */
                    esc_html__( 'AI optimization complete: %1$s of %2$s posts optimized.', 'prorank-seo' ),
                    esc_html( number_format_i18n( (int) $optimized ) ),
                    esc_html( number_format_i18n( (int) $total ) )
                )
            );
        }
    }

    /**
     * Add filter dropdowns to posts list
     */
    public function add_filter_dropdowns(): void {
        global $typenow;

        // Only show for supported post types
        if (!in_array($typenow, self::SUPPORTED_POST_TYPES, true)) {
            return;
        }

        // SEO Score Filter - sanitize to known enum values
        $seo_filter_value = prorank_get_query_var('prorank_seo_filter');
        $seo_selected = in_array($seo_filter_value, self::VALID_SEO_FILTERS, true)
            ? $seo_filter_value : '';

        echo '<select name="prorank_seo_filter" class="prorank-seo-filter">';
        echo '<option value="">' . esc_html__('All SEO Scores', 'prorank-seo') . '</option>';
        echo '<option value="good"' . selected($seo_selected, 'good', false) . '>' . esc_html__('Good (80+)', 'prorank-seo') . '</option>';
        echo '<option value="ok"' . selected($seo_selected, 'ok', false) . '>' . esc_html__('OK (60-79)', 'prorank-seo') . '</option>';
        echo '<option value="poor"' . selected($seo_selected, 'poor', false) . '>' . esc_html__('Poor (<60)', 'prorank-seo') . '</option>';
        echo '<option value="none"' . selected($seo_selected, 'none', false) . '>' . esc_html__('Not Analyzed', 'prorank-seo') . '</option>';
        echo '</select>';

        // Focus Keyword Filter - sanitize to known enum values
        $kw_filter_value = prorank_get_query_var('prorank_keyword_filter');
        $kw_selected = in_array($kw_filter_value, self::VALID_KW_FILTERS, true)
            ? $kw_filter_value : '';

        echo '<select name="prorank_keyword_filter" class="prorank-keyword-filter">';
        echo '<option value="">' . esc_html__('All Keywords', 'prorank-seo') . '</option>';
        echo '<option value="set"' . selected($kw_selected, 'set', false) . '>' . esc_html__('Keyword Set', 'prorank-seo') . '</option>';
        echo '<option value="none"' . selected($kw_selected, 'none', false) . '>' . esc_html__('No Keyword', 'prorank-seo') . '</option>';
        echo '</select>';
    }

    /**
     * Filter posts by SEO status
     *
     * @param \WP_Query $query The query object.
     */
    public function filter_posts_by_seo($query): void {
        if (!is_admin() || !$query->is_main_query()) {
            return;
        }

        // Only filter supported post types
        $post_type = $query->get('post_type');
        if (!in_array($post_type, self::SUPPORTED_POST_TYPES, true)) {
            return;
        }

        // Sanitize filter values to known enums
        $seo_filter_value = prorank_get_query_var('prorank_seo_filter');
        $kw_filter_value = prorank_get_query_var('prorank_keyword_filter');
        $seo_filter = in_array($seo_filter_value, self::VALID_SEO_FILTERS, true)
            ? $seo_filter_value : '';
        $kw_filter = in_array($kw_filter_value, self::VALID_KW_FILTERS, true)
            ? $kw_filter_value : '';

        // Early return if no filters selected (performance)
        if (empty($seo_filter) && empty($kw_filter)) {
            return;
        }

        $meta_query = [];

        // SEO Status filters
        if ($seo_filter === 'good') {
            $meta_query[] = [
                'key'     => '_prorank_seo_score',
                'value'   => 80,
                'compare' => '>=',
                'type'    => 'NUMERIC',
            ];
        } elseif ($seo_filter === 'ok') {
            $meta_query[] = [
                'key'     => '_prorank_seo_score',
                'value'   => [60, 79],
                'compare' => 'BETWEEN',
                'type'    => 'NUMERIC',
            ];
        } elseif ($seo_filter === 'poor') {
            $meta_query[] = [
                'key'     => '_prorank_seo_score',
                'value'   => 60,
                'compare' => '<',
                'type'    => 'NUMERIC',
            ];
        } elseif ($seo_filter === 'none') {
            $meta_query[] = [
                'key'     => '_prorank_seo_score',
                'compare' => 'NOT EXISTS',
            ];
        }

        // Keyword filter - EXISTS includes empty values, so check for non-empty
        if ($kw_filter === 'set') {
            $meta_query[] = [
                'key'     => '_prorank_seo_focus_keyword',
                'value'   => '',
                'compare' => '!=',
            ];
        } elseif ($kw_filter === 'none') {
            $meta_query[] = [
                'relation' => 'OR',
                [
                    'key'     => '_prorank_seo_focus_keyword',
                    'compare' => 'NOT EXISTS',
                ],
                [
                    'key'     => '_prorank_seo_focus_keyword',
                    'value'   => '',
                    'compare' => '=',
                ],
            ];
        }

        if (!empty($meta_query)) {
            $meta_query['relation'] = 'AND';
            $query->set('meta_query', $meta_query);
        }
    }

    /**
     * Handle SEO score sorting without dropping posts missing scores.
     *
     * @param array     $clauses Query clauses.
     * @param \WP_Query $query   Query object.
     * @return array
     */
    public function orderby_seo_score(array $clauses, \WP_Query $query): array {
        if (!is_admin() || !$query->is_main_query()) {
            return $clauses;
        }

        $post_type = $query->get('post_type');
        if (!in_array($post_type, self::SUPPORTED_POST_TYPES, true)) {
            return $clauses;
        }

        if ($query->get('orderby') !== 'prorank_seo_score') {
            return $clauses;
        }

        global $wpdb;

        if (strpos($clauses['join'], 'prorank_seo_score_meta') === false) {
            $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} AS prorank_seo_score_meta"
                . " ON ({$wpdb->posts}.ID = prorank_seo_score_meta.post_id"
                . " AND prorank_seo_score_meta.meta_key = '_prorank_seo_score')";
        }

        $order = strtoupper((string) $query->get('order')) === 'ASC' ? 'ASC' : 'DESC';
        $clauses['orderby'] = " (prorank_seo_score_meta.meta_value IS NULL) ASC,"
            . " CAST(prorank_seo_score_meta.meta_value AS UNSIGNED) {$order} ";

        return $clauses;
    }

    /**
     * Safe multibyte string length with fallback.
     *
     * @param string $value String to measure.
     * @return int
     */
    private function safe_strlen(string $value): int {
        if (function_exists('mb_strlen')) {
            return mb_strlen($value, 'UTF-8');
        }

        return strlen($value);
    }

    /**
     * Safe multibyte substring with fallback.
     *
     * @param string $value  String to slice.
     * @param int    $start  Start offset.
     * @param int    $length Length.
     * @return string
     */
    private function safe_substr(string $value, int $start, int $length): string {
        if (function_exists('mb_substr')) {
            return mb_substr($value, $start, $length, 'UTF-8');
        }

        return substr($value, $start, $length);
    }

    /**
     * Render hidden row data used to prefill quick edit.
     */
    private function render_inline_seo_state(int $post_id): void {
        $canonical_url = (string) get_post_meta($post_id, '_prorank_seo_canonical_url', true);
        $meta_robots = $this->normalize_meta_robots(get_post_meta($post_id, '_prorank_meta_robots', true));

        echo '<span class="prorank-inline-seo-state" hidden';
        echo ' data-canonical-url="' . esc_attr($canonical_url) . '"';
        echo ' data-meta-robots="' . esc_attr(implode(',', $meta_robots)) . '"';
        echo '></span>';
    }

    /**
     * Labels for post-level robots directives.
     *
     * @return array<string,string>
     */
    private function get_meta_robots_labels(): array {
        return [
            'noindex' => __('No Index', 'prorank-seo'),
            'nofollow' => __('No Follow', 'prorank-seo'),
            'noarchive' => __('No Archive', 'prorank-seo'),
            'nosnippet' => __('No Snippet', 'prorank-seo'),
            'noimageindex' => __('No Image Index', 'prorank-seo'),
        ];
    }

    /**
     * Normalize robots directives to the allowed set.
     *
     * @param mixed $meta_robots Robots input.
     * @return array<int,string>
     */
    private function normalize_meta_robots($meta_robots): array {
        if (!is_array($meta_robots)) {
            $meta_robots = empty($meta_robots) ? [] : (array) $meta_robots;
        }

        $normalized = array_map(
            'sanitize_key',
            array_filter(array_map('strval', $meta_robots))
        );

        return array_values(array_intersect(self::META_ROBOTS_DIRECTIVES, array_unique($normalized)));
    }

    /**
     * Apply bulk robots actions to the current directive list.
     *
     * @param array<int,string> $current Current directives.
     * @param array<mixed>      $request Request payload.
     * @return array<int,string>
     */
    private function apply_bulk_meta_robots(array $current, array $request): array {
        foreach (self::META_ROBOTS_DIRECTIVES as $directive) {
            $action = sanitize_key((string) ($request['prorank_bulk_' . $directive] ?? ''));
            if ($action === 'enable' && !in_array($directive, $current, true)) {
                $current[] = $directive;
            } elseif ($action === 'disable') {
                $current = array_values(array_diff($current, [$directive]));
            }
        }

        return array_values(array_intersect(self::META_ROBOTS_DIRECTIVES, $current));
    }

    /**
     * Check if user has AI access
     *
     * @return bool
     */
    private function has_ai_access(): bool {
        return false;
    }
    
    /**
     * Process AI optimization for posts
     *
     * @param array $post_ids Post IDs.
     * @return int Number of optimized posts.
     */
    private function process_ai_optimization(array $post_ids): int {
        $optimized = 0;
        
        foreach ($post_ids as $post_id) {
            if ($this->queue_ai_optimization(intval($post_id))) {
                $optimized++;
            }
        }
        
        return $optimized;
    }
    
    /**
     * Queue post for AI optimization
     *
     * @param int $post_id Post ID.
     * @return bool
     */
    private function queue_ai_optimization(int $post_id): bool {
        // Add to queue for background processing
        $queue = get_option('prorank_ai_optimization_queue', []);
        $queue[] = $post_id;
        update_option('prorank_ai_optimization_queue', array_unique($queue));
        
        // Trigger background processing
        wp_schedule_single_event(time(), 'prorank_process_ai_optimization');
        
        return true;
    }
}
