<?php
if (!defined('ABSPATH')) exit;

class AISB_Admin {

    public static function init() {
        add_action('in_admin_header', [__CLASS__, 'suppress_foreign_notices'], 1000);
        add_action('admin_menu', [__CLASS__, 'menu']);
        add_action('admin_enqueue_scripts', [__CLASS__, 'assets']);
        add_filter('plugin_action_links_' . plugin_basename(AISB_FILE), [__CLASS__, 'links']);

        add_action('wp_ajax_aisb_save_settings', [__CLASS__, 'ajax_save_settings']);
        add_action('wp_ajax_aisb_test_api', [__CLASS__, 'ajax_test_api']);
        add_action('wp_ajax_aisb_start_bulk', [__CLASS__, 'ajax_start_bulk']);
        add_action('wp_ajax_aisb_boost_scores', [__CLASS__, 'ajax_boost_scores']);
        add_action('wp_ajax_aisb_job_status', [__CLASS__, 'ajax_job_status']);
        add_action('wp_ajax_aisb_job_control', [__CLASS__, 'ajax_job_control']);
        add_action('wp_ajax_aisb_generate_faq_schema', [__CLASS__, 'ajax_generate_faq_schema']);
    }

    public static function links($links) {
        array_unshift($links, '<a href="' . esc_url(aisb_admin_url('dashboard')) . '">' . esc_html__('Dashboard', 'pewit-ai-schema-builder') . '</a>');
        return $links;
    }

    public static function menu() {
        $brand = aisb_brand_name();
        add_menu_page($brand, $brand, 'manage_options', 'pewit-ai-schema-builder', [__CLASS__, 'page_dashboard'], 'dashicons-editor-code', 60);
        add_submenu_page('pewit-ai-schema-builder', __('Dashboard', 'pewit-ai-schema-builder'), __('Dashboard', 'pewit-ai-schema-builder'), 'manage_options', 'pewit-ai-schema-builder', [__CLASS__, 'page_dashboard']);
        add_submenu_page('pewit-ai-schema-builder', __('Schema', 'pewit-ai-schema-builder'), __('Schema', 'pewit-ai-schema-builder'), 'manage_options', 'pewit-ai-schema-builder-schema', [__CLASS__, 'page_schema']);
        add_submenu_page('pewit-ai-schema-builder', __('Jobs', 'pewit-ai-schema-builder'), __('Jobs', 'pewit-ai-schema-builder'), 'manage_options', 'pewit-ai-schema-builder-jobs', [__CLASS__, 'page_jobs']);
        add_submenu_page('pewit-ai-schema-builder', __('Settings', 'pewit-ai-schema-builder'), __('Settings', 'pewit-ai-schema-builder'), 'manage_options', 'pewit-ai-schema-builder-settings', [__CLASS__, 'page_settings']);
    }

    public static function assets($hook) {
        if (strpos($hook, 'pewit-ai-schema-builder') === false) {
            return;
        }
        wp_enqueue_style('pewit-shell', AISB_URL . 'assets/css/pewit-admin-shell.css', [], AISB_VERSION);
        wp_enqueue_style('aisb-admin', AISB_URL . 'assets/css/admin.css', ['pewit-shell'], AISB_VERSION);
        wp_enqueue_script('aisb-admin', AISB_URL . 'assets/js/admin.js', ['jquery'], AISB_VERSION, true);
        wp_localize_script('aisb-admin', 'AISB', [
            'ajax'        => admin_url('admin-ajax.php'),
            'nonce'       => wp_create_nonce('aisb_nonce'),
            'version'     => AISB_VERSION,
            'plan'        => aisb_plan(),
            'canAutomate' => aisb_can_automate() ? 1 : 0,
            'apiReady'    => aisb_api_ready() ? 1 : 0,
            'onboarding_done' => AISB_Onboarding::is_done() ? 1 : 0,
            'upgrade_url' => aisb_upgrade_url(),
            'docs_url'    => aisb_docs_url(),
            'i18n'        => AISB_I18n::js_strings(),
        ]);
    }

    private static function shell_open($active = 'dashboard') {
        $plan = aisb_plan();
        $can_auto = aisb_can_automate();
        $tabs = [
            'dashboard' => [__('Dashboard', 'pewit-ai-schema-builder'), aisb_admin_url('dashboard')],
            'schema'    => [__('Schema', 'pewit-ai-schema-builder'), aisb_admin_url('schema')],
            'jobs'      => [__('Jobs', 'pewit-ai-schema-builder'), aisb_admin_url('jobs')],
            'settings'  => [__('Settings', 'pewit-ai-schema-builder'), aisb_admin_url('settings')],
        ];
        ?>
        <div class="wrap pewit-wrap pewit-ai-schema-builder">
            <div class="pewit-app-body">
                <div class="pewit-app-toolbar">
                    <div class="pewit-app-brand">
                        <div class="pewit-app-icon" aria-hidden="true">
                            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
                        </div>
                        <div>
                            <div class="pewit-app-title-row">
                                <span class="pewit-app-title"><?php echo esc_html(aisb_brand_name()); ?></span>
                                <span class="pewit-badge pewit-badge-plan pewit-plan-<?php echo esc_attr($plan); ?>">
                                    <?php echo esc_html(sprintf(/* translators: %s are replaced with dynamic values */ __('Plan · %s', 'pewit-ai-schema-builder'), aisb_plan_label())); ?>
                                </span>
                                <span class="pewit-version"><?php echo esc_html(AISB_VERSION); ?></span>
                            </div>
                            <div class="pewit-app-subtitle">pewit-labs · pewit-ai-schema-builder</div>
                        </div>
                    </div>
                </div>

                <nav class="pewit-tabs" aria-label="<?php esc_attr_e('Admin navigation', 'pewit-ai-schema-builder'); ?>">
                    <?php foreach ($tabs as $key => $tab): ?>
                        <a class="pewit-tab <?php echo $active === $key ? 'is-active' : ''; ?>" href="<?php echo esc_url($tab[1]); ?>">
                            <?php echo esc_html($tab[0]); ?>
                        </a>
                    <?php endforeach; ?>
                </nav>

                <div id="aisb-notice" class="aisb-notice-bar" aria-live="polite"></div>
        <?php
    }

    private static function shell_close() {
        ?>
                <p class="pewit-footer-note">
                    <?php printf(
                        /* translators: %s are replaced with dynamic values */ esc_html__('Part of the %s suite.', 'pewit-ai-schema-builder'),
                        '<a href="https://pewitlabs.com/" target="_blank" rel="noopener">PEWIT Labs</a>'
                    ); ?>
                </p>
            </div>
        </div>
        <?php
    }

    public static function page_dashboard() {
        self::shell_open('dashboard');
        $with = AISB_Schema::count_with_schema();
        $without = AISB_Schema::count_without_schema();
        $api_ready = aisb_api_ready();
        $settings = aisb_get_settings();
        $rr_stats = AISB_Rich_Results_Score::site_stats();
        ?>
        <div class="pewit-grid pewit-grid-stats">
            <div class="pewit-card pewit-stat-card pewit-killer-card">
                <div class="pewit-stat-value"><?php echo (int) $rr_stats['avg_score']; ?><span class="aisb-rr-unit">/100</span></div>
                <div class="pewit-stat-label"><?php esc_html_e('Rich Results Score (avg)', 'pewit-ai-schema-builder'); ?></div>
                <p class="description"><?php printf(/* translators: %d are replaced with dynamic values */ esc_html__('%d posts below 80', 'pewit-ai-schema-builder'), (int) $rr_stats['below_80']); ?></p>
            </div>
            <div class="pewit-card pewit-stat-card">
                <div class="pewit-stat-value"><?php echo esc_html(number_format_i18n($with)); ?></div>
                <div class="pewit-stat-label"><?php esc_html_e('Posts with schema', 'pewit-ai-schema-builder'); ?></div>
            </div>
            <div class="pewit-card pewit-stat-card">
                <div class="pewit-stat-value"><?php echo esc_html(number_format_i18n($without)); ?></div>
                <div class="pewit-stat-label"><?php esc_html_e('Published without schema', 'pewit-ai-schema-builder'); ?></div>
            </div>
            <div class="pewit-card pewit-stat-card">
                <div class="pewit-stat-value"><?php echo $api_ready ? '✓' : '—'; ?></div>
                <div class="pewit-stat-label"><?php esc_html_e('API status', 'pewit-ai-schema-builder'); ?></div>
            </div>
            <div class="pewit-card pewit-stat-card">
                <div class="pewit-stat-value"><?php echo esc_html($settings['output_mode'] === 'enabled' ? __('On', 'pewit-ai-schema-builder') : __('Off', 'pewit-ai-schema-builder')); ?></div>
                <div class="pewit-stat-label"><?php esc_html_e('Frontend output', 'pewit-ai-schema-builder'); ?></div>
            </div>
        </div>

        <div class="pewit-actions" style="margin-bottom:20px">
            <button type="button" class="pewit-btn pewit-btn-primary" id="aisb-boost-scores"><?php esc_html_e('Boost low scores (&lt;80)', 'pewit-ai-schema-builder'); ?></button>
            <button type="button" class="pewit-btn pewit-btn-secondary" id="aisb-export-score-csv"><?php esc_html_e('Export schema scores (CSV)', 'pewit-ai-schema-builder'); ?></button>
        </div>

        <div class="pewit-hero-card">
            <h1><?php esc_html_e('Generate JSON-LD from your content', 'pewit-ai-schema-builder'); ?></h1>
            <p class="pewit-lead"><?php esc_html_e('Detect custom post types, map schema types, and generate Google/Bing-ready JSON-LD. EtchWP sites get native admin styling and richer Content Hub context.', 'pewit-ai-schema-builder'); ?></p>
            <div class="pewit-actions">
                <a class="pewit-btn pewit-btn-primary" href="<?php echo esc_url(admin_url('edit.php')); ?>"><?php esc_html_e('Edit posts', 'pewit-ai-schema-builder'); ?></a>
                <a class="pewit-btn pewit-btn-secondary" href="<?php echo esc_url(aisb_admin_url('settings')); ?>"><?php esc_html_e('Settings', 'pewit-ai-schema-builder'); ?></a>
                <a class="pewit-btn pewit-btn-secondary" href="<?php echo esc_url(aisb_admin_url('schema')); ?>"><?php esc_html_e('View schema list', 'pewit-ai-schema-builder'); ?></a>
            </div>
        </div>

        <?php if (AISB_Etch::is_active()): ?>
        <div class="pewit-callout pewit-callout-etch">
            <strong><?php esc_html_e('EtchWP detected', 'pewit-ai-schema-builder'); ?></strong>
            <?php echo esc_html(AISB_Etch::status_line()); ?>
        </div>
        <?php endif; ?>

        <?php if (AISB_JetEngine_CCT::is_active() || AISB_MetaBox_Fields::is_active()): ?>
        <div class="pewit-callout pewit-callout-etch">
            <strong><?php esc_html_e('External fields', 'pewit-ai-schema-builder'); ?></strong>
            <?php
            $parts = [];
            if (AISB_JetEngine_CCT::is_active()) {
                $parts[] = sprintf(/* translators: %s are replaced with dynamic values */ __('JetEngine CCT (%s)', 'pewit-ai-schema-builder'), AISB_JetEngine_CCT::version() ?: 'active');
            }
            if (AISB_MetaBox_Fields::is_active()) {
                $parts[] = __('MetaBox custom tables', 'pewit-ai-schema-builder');
            }
            echo esc_html(implode(' · ', $parts));
            ?>
            <a class="pewit-btn pewit-btn-secondary" href="<?php echo esc_url(aisb_admin_url('settings')); ?>#aisb-external-fields"><?php esc_html_e('Field mapping', 'pewit-ai-schema-builder'); ?></a>
        </div>
        <?php endif; ?>

        <?php
        $enabled_cpts = AISB_CPT_Registry::get_enabled_post_types();
        if (count($enabled_cpts) > 3):
        ?>
        <div class="pewit-card">
            <h2><?php esc_html_e('Enabled post types', 'pewit-ai-schema-builder'); ?></h2>
            <p class="description"><?php echo esc_html(implode(', ', $enabled_cpts)); ?></p>
            <a class="pewit-btn pewit-btn-secondary" href="<?php echo esc_url(aisb_admin_url('settings')); ?>#aisb-cpt-settings"><?php esc_html_e('Manage CPT mapping', 'pewit-ai-schema-builder'); ?></a>
        </div>
        <?php endif; ?>

        <?php
        AISB_Onboarding::render_modal();
        self::shell_close();
    }

    public static function page_schema() {
        self::shell_open('schema');
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list pagination; no state change.
        $paged = isset($_GET['paged']) ? max(1, absint(wp_unslash($_GET['paged']))) : 1;
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin list filter tab; no state change.
        $filter = isset($_GET['filter']) ? sanitize_key(wp_unslash($_GET['filter'])) : 'all';
        $post_types = AISB_CPT_Registry::get_enabled_post_types();
        if (empty($post_types)) {
            $post_types = AISB_CPT_Registry::default_enabled_post_types();
        }

        $args = [
            'post_type'      => $post_types,
            'post_status'    => 'publish',
            'posts_per_page' => 20,
            'paged'          => $paged,
            'orderby'        => 'modified',
            'order'          => 'DESC',
        ];
        if ($filter === 'missing') {
            // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Intentional coverage filter on schema post meta.
            $args['meta_query'] = [
                'relation' => 'OR',
                ['key' => AISB_Schema::META_JSON, 'compare' => 'NOT EXISTS'],
                ['key' => AISB_Schema::META_JSON, 'value' => '', 'compare' => '='],
            ];
        } elseif ($filter === 'has') {
            // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Intentional coverage filter on schema post meta.
            $args['meta_query'] = [
                ['key' => AISB_Schema::META_JSON, 'compare' => 'EXISTS'],
                ['key' => AISB_Schema::META_JSON, 'value' => '', 'compare' => '!='],
            ];
        }

        $query = new WP_Query($args);
        ?>
        <div class="pewit-card">
            <div class="aisb-list-toolbar">
                <h2><?php esc_html_e('Schema coverage', 'pewit-ai-schema-builder'); ?></h2>
                <div class="aisb-filter-tabs">
                    <a class="pewit-btn pewit-btn-secondary <?php echo $filter === 'all' ? 'is-active' : ''; ?>" href="<?php echo esc_url(add_query_arg(['filter' => 'all', 'paged' => false], aisb_admin_url('schema'))); ?>"><?php esc_html_e('All', 'pewit-ai-schema-builder'); ?></a>
                    <a class="pewit-btn pewit-btn-secondary <?php echo $filter === 'has' ? 'is-active' : ''; ?>" href="<?php echo esc_url(add_query_arg(['filter' => 'has', 'paged' => false], aisb_admin_url('schema'))); ?>"><?php esc_html_e('With schema', 'pewit-ai-schema-builder'); ?></a>
                    <a class="pewit-btn pewit-btn-secondary <?php echo $filter === 'missing' ? 'is-active' : ''; ?>" href="<?php echo esc_url(add_query_arg(['filter' => 'missing', 'paged' => false], aisb_admin_url('schema'))); ?>"><?php esc_html_e('Missing schema', 'pewit-ai-schema-builder'); ?></a>
                </div>
            </div>
            <table class="widefat striped aisb-table">
                <thead>
                    <tr>
                        <th><?php esc_html_e('Title', 'pewit-ai-schema-builder'); ?></th>
                        <th><?php esc_html_e('Type', 'pewit-ai-schema-builder'); ?></th>
                        <th><?php esc_html_e('Schema', 'pewit-ai-schema-builder'); ?></th>
                        <th><?php esc_html_e('Validation', 'pewit-ai-schema-builder'); ?></th>
                        <th></th>
                    </tr>
                </thead>
                <tbody>
                <?php if ($query->have_posts()): ?>
                    <?php while ($query->have_posts()): $query->the_post(); ?>
                        <?php
                        $schema = AISB_Schema::get_json(get_the_ID());
                        $stype = AISB_Schema::get_type(get_the_ID()) ?: ($schema['@type'] ?? '—');
                        $val = $schema ? AISB_Schema::validate($schema, $stype) : null;
                        ?>
                        <tr>
                            <td><a href="<?php echo esc_url(get_edit_post_link()); ?>"><?php the_title(); ?></a></td>
                            <td><?php echo esc_html(get_post_type_object(get_post_type())->labels->singular_name); ?></td>
                            <td>
                                <?php if ($schema): ?>
                                    <span class="aisb-badge aisb-badge-ok"><?php echo esc_html(is_array($stype) ? ($stype[0] ?? 'Schema') : $stype); ?></span>
                                <?php else: ?>
                                    <span class="aisb-badge aisb-badge-missing"><?php esc_html_e('None', 'pewit-ai-schema-builder'); ?></span>
                                <?php endif; ?>
                            </td>
                            <td>
                                <?php if ($val): ?>
                                    <?php if ($val['valid']): ?>
                                        <span class="aisb-badge aisb-badge-ok"><?php esc_html_e('Valid', 'pewit-ai-schema-builder'); ?></span>
                                    <?php else: ?>
                                        <span class="aisb-badge aisb-badge-error"><?php echo esc_html(count($val['errors'])); ?> <?php esc_html_e('issues', 'pewit-ai-schema-builder'); ?></span>
                                    <?php endif; ?>
                                <?php else: ?>
                                    —
                                <?php endif; ?>
                            </td>
                            <td><a class="button button-small" href="<?php echo esc_url(get_edit_post_link()); ?>"><?php esc_html_e('Edit', 'pewit-ai-schema-builder'); ?></a></td>
                        </tr>
                    <?php endwhile; wp_reset_postdata(); ?>
                <?php else: ?>
                    <tr><td colspan="5"><?php esc_html_e('No posts found.', 'pewit-ai-schema-builder'); ?></td></tr>
                <?php endif; ?>
                </tbody>
            </table>
            <?php if ($query->max_num_pages > 1): ?>
                <div class="tablenav bottom">
                    <div class="tablenav-pages">
                        <?php
                        echo wp_kses_post(paginate_links([
                            'base'    => add_query_arg('paged', '%#%', aisb_admin_url('schema')),
                            'format'  => '',
                            'current' => $paged,
                            'total'   => $query->max_num_pages,
                        ]));
                        ?>
                    </div>
                </div>
            <?php endif; ?>
        </div>
        <?php
        self::shell_close();
    }

    public static function page_jobs() {
        self::shell_open('jobs');
        $job = AISB_Queue::get_job();
        $without = AISB_Schema::count_without_schema();
        ?>
        <div class="pewit-card">
            <h2><?php esc_html_e('Background jobs', 'pewit-ai-schema-builder'); ?></h2>
            <p class="description"><?php esc_html_e('Bulk generate schema for published posts that do not have JSON-LD yet. Runs via WP-Cron in small batches.', 'pewit-ai-schema-builder'); ?></p>
            <p><strong><?php echo esc_html(number_format_i18n($without)); ?></strong> <?php esc_html_e('published posts without schema.', 'pewit-ai-schema-builder'); ?></p>
            <div class="pewit-actions">
                <button type="button" class="pewit-btn pewit-btn-primary" id="aisb-start-bulk" <?php disabled(!$without || !aisb_api_ready()); ?>>
                    <?php esc_html_e('Generate for all missing', 'pewit-ai-schema-builder'); ?>
                </button>
                <?php if ($job && in_array($job['status'] ?? '', ['running', 'paused'], true)): ?>
                    <button type="button" class="pewit-btn pewit-btn-secondary" id="aisb-job-pause" data-action="<?php echo ($job['status'] ?? '') === 'paused' ? 'resume' : 'pause'; ?>">
                        <?php echo ($job['status'] ?? '') === 'paused' ? esc_html__('Resume', 'pewit-ai-schema-builder') : esc_html__('Pause', 'pewit-ai-schema-builder'); ?>
                    </button>
                    <button type="button" class="pewit-btn pewit-btn-secondary" id="aisb-job-stop" data-action="stop"><?php esc_html_e('Stop', 'pewit-ai-schema-builder'); ?></button>
                <?php endif; ?>
            </div>
            <?php if (!aisb_api_ready()): ?>
                <p class="aisb-notice aisb-notice-warn"><?php esc_html_e('Configure WordPress AI connectors (Settings → Connectors on WP 7.0+) or add your API key in Settings before starting a job.', 'pewit-ai-schema-builder'); ?></p>
            <?php endif; ?>
        </div>

        <div class="pewit-card" id="aisb-job-panel">
            <?php self::render_job_panel($job); ?>
        </div>
        <?php
        self::shell_close();
    }

    private static function render_job_panel($job) {
        if (!$job) {
            echo '<p class="description">' . esc_html__('No active job.', 'pewit-ai-schema-builder') . '</p>';
            return;
        }
        $done = (int) ($job['done'] ?? 0);
        $total = (int) ($job['total'] ?? 0);
        $pct = $total ? min(100, round(($done / $total) * 100)) : 0;
        ?>
        <h3><?php esc_html_e('Current job', 'pewit-ai-schema-builder'); ?></h3>
        <p><strong><?php esc_html_e('Status:', 'pewit-ai-schema-builder'); ?></strong> <?php echo esc_html(ucfirst($job['status'] ?? '')); ?></p>
        <p><strong><?php esc_html_e('Progress:', 'pewit-ai-schema-builder'); ?></strong> <?php echo esc_html($done . ' / ' . $total); ?>
            <?php if (!empty($job['eta_text']) && ($job['status'] ?? '') === 'running'): ?>
                · <?php esc_html_e('ETA', 'pewit-ai-schema-builder'); ?>: <?php echo esc_html($job['eta_text']); ?>
            <?php endif; ?>
        </p>
        <div class="aisb-progress"><div class="aisb-progress-bar" style="width:<?php echo esc_attr($pct); ?>%"></div></div>
        <?php if (!empty($job['current'])): ?>
            <p class="description"><?php esc_html_e('Processing:', 'pewit-ai-schema-builder'); ?> #<?php echo esc_html($job['current']['id']); ?> — <?php echo esc_html($job['current']['title'] ?? ''); ?></p>
        <?php endif; ?>
        <?php if (!empty($job['log'])): ?>
            <details class="aisb-job-log">
                <summary><?php esc_html_e('Activity log', 'pewit-ai-schema-builder'); ?></summary>
                <ul>
                    <?php foreach (array_reverse(array_slice($job['log'], -20)) as $entry): ?>
                        <li class="aisb-log-<?php echo esc_attr($entry['level'] ?? 'info'); ?>">
                            [<?php echo esc_html($entry['time'] ?? ''); ?>] <?php echo esc_html($entry['message'] ?? ''); ?>
                        </li>
                    <?php endforeach; ?>
                </ul>
            </details>
        <?php endif; ?>
        <?php
    }

    public static function page_settings() {
        self::shell_open('settings');
        AISB_CPT_Registry::seed_defaults();
        $s = aisb_get_settings();
        $has_key = !empty($s['api_key']);
        $discovered = AISB_CPT_Registry::discover_post_types();
        $enabled = AISB_CPT_Registry::get_enabled_post_types();
        $map = AISB_CPT_Registry::get_cpt_schema_map();
        $catalog = AISB_CPT_Registry::schema_type_catalog();
        $seo_notice = AISB_SEO_Plugins::admin_notice_html();
        $test_urls = AISB_Rich_Results::test_urls();
        ?>
        <div class="pewit-card aisb-settings-card" id="aisb-cpt-settings">
            <h2><?php esc_html_e('Post types & schema mapping', 'pewit-ai-schema-builder'); ?></h2>
            <p class="description"><?php esc_html_e('Choose which public post types get metaboxes, bulk jobs and frontend JSON-LD. Default schema type is used for auto-detect and bulk generation.', 'pewit-ai-schema-builder'); ?></p>
            <table class="widefat striped aisb-cpt-table">
                <thead>
                    <tr>
                        <th><?php esc_html_e('Enable', 'pewit-ai-schema-builder'); ?></th>
                        <th><?php esc_html_e('Post type', 'pewit-ai-schema-builder'); ?></th>
                        <th><?php esc_html_e('Published', 'pewit-ai-schema-builder'); ?></th>
                        <th><?php esc_html_e('Default schema', 'pewit-ai-schema-builder'); ?></th>
                    </tr>
                </thead>
                <tbody>
                <?php foreach ($discovered as $name => $info): ?>
                    <tr>
                        <td>
                            <input type="checkbox" class="aisb-cpt-enabled" name="aisb_cpt_enabled[]" value="<?php echo esc_attr($name); ?>" <?php checked(in_array($name, $enabled, true)); ?> />
                        </td>
                        <td>
                            <strong><?php echo esc_html($info['label']); ?></strong>
                            <code><?php echo esc_html($name); ?></code>
                            <?php if (!empty($info['etch'])): ?>
                                <span class="aisb-badge aisb-badge-etch">Etch</span>
                            <?php endif; ?>
                            <?php if (!empty($info['jet_cct'])): ?>
                                <span class="aisb-badge aisb-badge-cct" title="<?php echo esc_attr($info['jet_cct_name']); ?>">CCT</span>
                            <?php endif; ?>
                            <?php if (!empty($info['metabox_fields'])): ?>
                                <span class="aisb-badge aisb-badge-metabox">MetaBox</span>
                            <?php endif; ?>
                        </td>
                        <td><?php echo esc_html(number_format_i18n($info['count'])); ?></td>
                        <td>
                            <select class="aisb-cpt-map" name="aisb_cpt_map[<?php echo esc_attr($name); ?>]">
                                <?php
                                $current = $map[$name] ?? AISB_CPT_Registry::guess_schema_type($name, $info['label']);
                                foreach ($catalog as $type_key => $type_label):
                                ?>
                                    <option value="<?php echo esc_attr($type_key); ?>" <?php selected($current, $type_key); ?>><?php echo esc_html($type_label); ?></option>
                                <?php endforeach; ?>
                            </select>
                        </td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
        </div>

        <?php
        $jet_ccts = AISB_JetEngine_CCT::discover();
        $mb_tables = AISB_MetaBox_Fields::discover_tables();
        $field_map = AISB_Field_Mapper::get_map();
        $schema_targets = AISB_Field_Mapper::schema_targets();
        $has_external = !empty($jet_ccts) || !empty($mb_tables);
        ?>
        <div class="pewit-card aisb-settings-card" id="aisb-external-fields">
            <h2><?php esc_html_e('JetEngine CCT & MetaBox field mapping', 'pewit-ai-schema-builder'); ?></h2>
            <p class="description">
                <?php esc_html_e('Map custom fields from JetEngine Custom Content Types or MetaBox custom tables to Schema.org properties. CCT rows without a linked single CPT are listed for reference — enable the related CPT above.', 'pewit-ai-schema-builder'); ?>
            </p>

            <?php if (AISB_JetEngine_CCT::is_active()): ?>
            <h3><?php esc_html_e('JetEngine Custom Content Types', 'pewit-ai-schema-builder'); ?></h3>
            <?php if (empty($jet_ccts)): ?>
                <p class="description"><?php esc_html_e('No CCT definitions found.', 'pewit-ai-schema-builder'); ?></p>
            <?php else: ?>
                <table class="widefat striped aisb-cpt-table">
                    <thead>
                        <tr>
                            <th><?php esc_html_e('CCT', 'pewit-ai-schema-builder'); ?></th>
                            <th><?php esc_html_e('Linked CPT', 'pewit-ai-schema-builder'); ?></th>
                            <th><?php esc_html_e('Status', 'pewit-ai-schema-builder'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                    <?php foreach ($jet_ccts as $cct): ?>
                        <tr>
                            <td><strong><?php echo esc_html($cct['name']); ?></strong> <code><?php echo esc_html($cct['slug']); ?></code></td>
                            <td>
                                <?php if (!empty($cct['related_post_type'])): ?>
                                    <code><?php echo esc_html($cct['related_post_type']); ?></code>
                                <?php else: ?>
                                    <span class="aisb-notice-warn"><?php esc_html_e('None — enable Has Single Page in JetEngine', 'pewit-ai-schema-builder'); ?></span>
                                <?php endif; ?>
                            </td>
                            <td><?php echo esc_html($cct['hint']); ?></td>
                        </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            <?php endif; ?>
            <?php else: ?>
                <p class="description"><?php esc_html_e('JetEngine not detected.', 'pewit-ai-schema-builder'); ?></p>
            <?php endif; ?>

            <?php if (AISB_MetaBox_Fields::is_active() && !empty($mb_tables)): ?>
            <h3><?php esc_html_e('MetaBox custom tables', 'pewit-ai-schema-builder'); ?></h3>
            <ul class="aisb-mb-table-list">
                <?php foreach ($mb_tables as $pt => $info): ?>
                    <li><code><?php echo esc_html($pt); ?></code> — <?php echo esc_html(implode(', ', $info['tables'])); ?> (<?php echo esc_html(count($info['fields'])); ?> <?php esc_html_e('fields', 'pewit-ai-schema-builder'); ?>)</li>
                <?php endforeach; ?>
            </ul>
            <?php endif; ?>

            <?php
            $mapping_pts = [];
            foreach ($discovered as $name => $info) {
                if (!in_array($name, $enabled, true)) {
                    continue;
                }
                $fields = AISB_Field_Mapper::discover_fields_for_post_type($name);
                if (!empty($fields)) {
                    $mapping_pts[$name] = $fields;
                }
            }
            ?>
            <?php if (!empty($mapping_pts)): ?>
            <h3><?php esc_html_e('Field → Schema mapping', 'pewit-ai-schema-builder'); ?></h3>
            <p class="description"><?php esc_html_e('Auto-suggestions are pre-filled. Empty target = skip field.', 'pewit-ai-schema-builder'); ?></p>
            <?php foreach ($mapping_pts as $pt => $fields):
                $pt_map = !empty($field_map[$pt]) ? $field_map[$pt] : AISB_Field_Mapper::seed_map_for_post_type($pt);
                $pt_label = $discovered[$pt]['label'] ?? $pt;
            ?>
            <details class="aisb-field-map-group" open>
                <summary><strong><?php echo esc_html($pt_label); ?></strong> <code><?php echo esc_html($pt); ?></code></summary>
                <table class="widefat striped aisb-field-map-table">
                    <thead>
                        <tr>
                            <th><?php esc_html_e('Source field', 'pewit-ai-schema-builder'); ?></th>
                            <th><?php esc_html_e('Schema property', 'pewit-ai-schema-builder'); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                    <?php foreach ($fields as $field):
                        $current = $pt_map[$field] ?? '';
                    ?>
                        <tr class="aisb-field-map-row" data-post-type="<?php echo esc_attr($pt); ?>" data-source-field="<?php echo esc_attr($field); ?>">
                            <td><code><?php echo esc_html($field); ?></code></td>
                            <td>
                                <select class="aisb-field-target">
                                    <option value=""><?php esc_html_e('— Skip —', 'pewit-ai-schema-builder'); ?></option>
                                    <?php foreach ($schema_targets as $target => $label): ?>
                                        <option value="<?php echo esc_attr($target); ?>" <?php selected($current, $target); ?>><?php echo esc_html($target . ' — ' . $label); ?></option>
                                    <?php endforeach; ?>
                                </select>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </details>
            <?php endforeach; ?>
            <?php elseif ($has_external): ?>
                <p class="description"><?php esc_html_e('Enable a post type linked to CCT or MetaBox above to configure field mapping.', 'pewit-ai-schema-builder'); ?></p>
            <?php else: ?>
                <p class="description"><?php esc_html_e('Install JetEngine (CCT) or MetaBox (custom table storage) to unlock field mapping.', 'pewit-ai-schema-builder'); ?></p>
            <?php endif; ?>
        </div>

        <div class="pewit-card aisb-settings-card">
            <h2><?php esc_html_e('Output', 'pewit-ai-schema-builder'); ?></h2>
            <label for="aisb_output_mode"><?php esc_html_e('Frontend JSON-LD output', 'pewit-ai-schema-builder'); ?></label>
            <select id="aisb_output_mode">
                <option value="enabled" <?php selected($s['output_mode'], 'enabled'); ?>><?php esc_html_e('Enabled — output script tag in wp_head', 'pewit-ai-schema-builder'); ?></option>
                <option value="disabled" <?php selected($s['output_mode'], 'disabled'); ?>><?php esc_html_e('Disabled — store only, no frontend output', 'pewit-ai-schema-builder'); ?></option>
            </select>

            <label for="aisb_seo_coexist_mode"><?php esc_html_e('SEO plugin coexistence', 'pewit-ai-schema-builder'); ?></label>
            <select id="aisb_seo_coexist_mode">
                <option value="smart" <?php selected($s['seo_coexist_mode'] ?? 'smart', 'smart'); ?>><?php esc_html_e('Smart — output ours (review if Yoast/Rank Math active)', 'pewit-ai-schema-builder'); ?></option>
                <option value="sync_seo" <?php selected($s['seo_coexist_mode'] ?? '', 'sync_seo'); ?>><?php esc_html_e('Sync to SEO plugin — push JSON-LD to Yoast/Rank Math, no duplicate wp_head', 'pewit-ai-schema-builder'); ?></option>
                <option value="pewit_only" <?php selected($s['seo_coexist_mode'] ?? '', 'pewit_only'); ?>><?php esc_html_e('PEWIT only — always output our JSON-LD', 'pewit-ai-schema-builder'); ?></option>
                <option value="disabled" <?php selected($s['seo_coexist_mode'] ?? '', 'disabled'); ?>><?php esc_html_e('No frontend output (same as disabled above)', 'pewit-ai-schema-builder'); ?></option>
            </select>
            <?php if ($seo_notice): ?>
                <p class="aisb-notice aisb-notice-warn"><?php echo esc_html($seo_notice); ?></p>
            <?php endif; ?>

            <label>
                <input type="checkbox" id="aisb_global_schema_enabled" value="1" <?php checked(!empty($s['global_schema_enabled'])); ?> />
                <?php esc_html_e('Homepage: WebSite + Organization @graph', 'pewit-ai-schema-builder'); ?>
            </label>
            <label>
                <input type="checkbox" id="aisb_breadcrumb_enabled" value="1" <?php checked(!empty($s['breadcrumb_enabled'])); ?> />
                <?php esc_html_e('Singular pages: BreadcrumbList JSON-LD', 'pewit-ai-schema-builder'); ?>
            </label>
            <p class="description">
                <?php esc_html_e('Validate with:', 'pewit-ai-schema-builder'); ?>
                <a href="<?php echo esc_url($test_urls['google']); ?>" target="_blank" rel="noopener"><?php esc_html_e('Google Rich Results Test', 'pewit-ai-schema-builder'); ?></a>
                ·
                <a href="<?php echo esc_url($test_urls['bing']); ?>" target="_blank" rel="noopener"><?php esc_html_e('Bing Markup Validator', 'pewit-ai-schema-builder'); ?></a>
            </p>
        </div>

        <div class="pewit-card aisb-settings-card">
            <h2><?php esc_html_e('Notifications', 'pewit-ai-schema-builder'); ?></h2>
            <label>
                <input type="checkbox" id="aisb_notify_email_enabled" value="1" <?php checked(!empty($s['notify_email_enabled'])); ?> />
                <?php esc_html_e('Email when bulk job completes', 'pewit-ai-schema-builder'); ?>
            </label>
            <label>
                <input type="checkbox" id="aisb_notify_email_failures" value="1" <?php checked(!isset($s['notify_email_failures']) || !empty($s['notify_email_failures'])); ?> />
                <?php esc_html_e('Also email when job has failures', 'pewit-ai-schema-builder'); ?>
            </label>
            <label for="aisb_notify_email_to"><?php esc_html_e('Recipient', 'pewit-ai-schema-builder'); ?></label>
            <input type="email" id="aisb_notify_email_to" class="regular-text" value="<?php echo esc_attr($s['notify_email_to']); ?>" />
        </div>

        <div class="pewit-card aisb-settings-card">
            <h2><?php esc_html_e('Validation autopilot', 'pewit-ai-schema-builder'); ?></h2>
            <label>
                <input type="checkbox" id="aisb_validation_scan_enabled" value="1" <?php checked(!empty($s['validation_scan_enabled'])); ?> />
                <?php esc_html_e('Weekly scan for schema validation errors (email if issues found)', 'pewit-ai-schema-builder'); ?>
            </label>
            <?php
            $report = AISB_Validation_Cron::get_report();
            if ($report && !empty($report['ran_at'])):
            ?>
                <p class="description">
                    <?php
                    printf(
                        /* translators: %1$s, %2$d, %3$d are replaced with dynamic values */ esc_html__('Last scan: %1$s — %2$d checked, %3$d issues.', 'pewit-ai-schema-builder'),
                        esc_html($report['ran_at']),
                        (int) ($report['checked'] ?? 0),
                        (int) ($report['issue_count'] ?? 0)
                    );
                    ?>
                </p>
            <?php endif; ?>
        </div>

        <?php if (AISB_Etch::is_active()): ?>
        <div class="pewit-card aisb-settings-card">
            <h2><?php esc_html_e('EtchWP integration', 'pewit-ai-schema-builder'); ?></h2>
            <p class="description"><?php echo esc_html(AISB_Etch::status_line()); ?></p>
            <label>
                <input type="checkbox" id="aisb_etch_native_ui" value="1" <?php checked(AISB_Etch::use_native_admin_ui()); ?> />
                <?php esc_html_e('Use Etch-native dark admin styling', 'pewit-ai-schema-builder'); ?>
            </label>
            <p class="description"><?php esc_html_e('Reads Etch block JSON and Content Hub meta for richer schema prompts. Compatible with Media props (attachment ID) and Dynamic Image elements.', 'pewit-ai-schema-builder'); ?></p>
        </div>
        <?php endif; ?>

        <div class="pewit-card aisb-settings-card">
            <h2><?php esc_html_e('AI provider', 'pewit-ai-schema-builder'); ?></h2>
            <?php
            if (class_exists('Pewit_Labs_Hub')) {
                Pewit_Labs_Hub::render_settings_notice('pewit-ai-schema-builder');
            }
            $hub_mode = class_exists('Pewit_Labs_Hub') && Pewit_Labs_Hub::should_show_hub();
            if ($hub_mode) :
                ?>
            <p class="description"><?php esc_html_e('Provider, API key, model and global context are managed under PEWIT Labs shared settings. Options below stay local to this plugin.', 'pewit-ai-schema-builder'); ?></p>
            <?php else : ?>
            <?php if (AISB_LLM_API::core_text_generation_supported()): ?>
                <p class="description"><?php esc_html_e('WordPress AI connectors are configured. Generation uses the core AI Client (Settings → Connectors). You can still add a plugin API key as a fallback on older hosts.', 'pewit-ai-schema-builder'); ?></p>
            <?php else: ?>
                <p class="description"><?php esc_html_e('On WordPress 7.0+, you can configure providers under Settings → Connectors instead of entering a key below.', 'pewit-ai-schema-builder'); ?></p>
            <?php endif; ?>
            <?php
            aisb_render_provider_select($s, [
                'id'          => 'aisb_provider',
                'pro_allowed' => aisb_is_pro() || aisb_is_trial(),
                'text_domain' => 'pewit-ai-schema-builder',
                'description' => __('OpenAI, Claude, or Gemini — use your own API key (BYOK) when core connectors are not configured.', 'pewit-ai-schema-builder'),
            ]);
            ?>
            <label><?php esc_html_e('API key', 'pewit-ai-schema-builder'); ?></label>
            <div class="aisb-api-key-field">
                <div id="aisb-api-key-saved" <?php echo $has_key ? '' : 'style="display:none"'; ?>>
                    <code id="aisb_api_key_mask"><?php echo esc_html(aisb_api_key_masked_label($s['api_key'])); ?></code>
                    <button type="button" class="button button-small" id="aisb-api-key-change"><?php esc_html_e('Change key', 'pewit-ai-schema-builder'); ?></button>
                </div>
                <div id="aisb-api-key-edit" class="aisb-inline" <?php echo $has_key ? 'style="display:none"' : ''; ?>>
                    <input type="password" id="aisb_api_key" class="regular-text" placeholder="sk-..." autocomplete="new-password" />
                    <?php if ($has_key): ?>
                    <button type="button" class="button button-small" id="aisb-api-key-cancel"><?php esc_html_e('Cancel', 'pewit-ai-schema-builder'); ?></button>
                    <?php endif; ?>
                </div>
                <button type="button" class="button" id="aisb-test-api"><?php esc_html_e('Save & Test', 'pewit-ai-schema-builder'); ?></button>
            </div>
            <p id="aisb-api-status" class="aisb-status <?php echo (aisb_api_ready()) ? 'ok' : ''; ?>">
                <?php
                if (AISB_LLM_API::core_text_generation_supported()) {
                    esc_html_e('WordPress AI connectors ready', 'pewit-ai-schema-builder');
                } elseif (!empty($s['api_key_verified'])) {
                    esc_html_e('API connected', 'pewit-ai-schema-builder');
                } elseif ($has_key) {
                    esc_html_e('Key saved — click Save & Test to verify', 'pewit-ai-schema-builder');
                } else {
                    esc_html_e('Not configured', 'pewit-ai-schema-builder');
                }
                ?>
            </p>

            <label for="aisb_model"><?php esc_html_e('Model', 'pewit-ai-schema-builder'); ?></label>
            <input type="text" id="aisb_model" class="regular-text" value="<?php echo esc_attr($s['model']); ?>" />

            <label for="aisb_global_context"><?php esc_html_e('Global context', 'pewit-ai-schema-builder'); ?></label>
            <textarea id="aisb_global_context" rows="3" class="large-text"><?php echo esc_textarea($s['global_context']); ?></textarea>
            <p class="description"><?php esc_html_e('Optional site-wide context sent with every generation (industry, tone, brand).', 'pewit-ai-schema-builder'); ?></p>
            <?php endif; ?>
        </div>

        <div class="pewit-card aisb-settings-card">
            <h2><?php esc_html_e('Automation', 'pewit-ai-schema-builder'); ?></h2>
            <label>
                <input type="checkbox" id="aisb_auto_generate" value="1" <?php checked(!empty($s['auto_generate'])); ?> />
                <?php esc_html_e('Auto-generate schema on publish/update', 'pewit-ai-schema-builder'); ?>
            </label>
            <div class="aisb-inline-fields">
                <label for="aisb_batch_size"><?php esc_html_e('Batch size', 'pewit-ai-schema-builder'); ?></label>
                <input type="number" id="aisb_batch_size" min="1" max="10" value="<?php echo esc_attr((int) $s['batch_size']); ?>" />
                <label for="aisb_batch_delay"><?php esc_html_e('Delay (seconds)', 'pewit-ai-schema-builder'); ?></label>
                <input type="number" id="aisb_batch_delay" min="2" max="60" value="<?php echo esc_attr((int) $s['batch_delay']); ?>" />
            </div>
        </div>

        <div class="pewit-actions">
            <button type="button" class="pewit-btn pewit-btn-primary" id="aisb-save-settings"><?php esc_html_e('Save settings', 'pewit-ai-schema-builder'); ?></button>
        </div>
        <?php
        self::shell_close();
    }

    public static function ajax_save_settings() {
        check_ajax_referer('aisb_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied', 'pewit-ai-schema-builder')], 403);
        }
        $s = aisb_get_settings();
        if (isset($_POST['api_key']) && $_POST['api_key'] !== '') {
            $s['api_key'] = sanitize_text_field(wp_unslash($_POST['api_key']));
        }
        if (isset($_POST['model'])) {
            $s['model'] = sanitize_text_field(wp_unslash($_POST['model']));
        }
        if (isset($_POST['provider'])) {
            $s['provider'] = AISB_LLM_API::sanitize_provider_setting(sanitize_text_field(wp_unslash($_POST['provider'])), aisb_is_pro() || aisb_is_trial());
        }
        if (isset($_POST['global_context'])) {
            $s['global_context'] = sanitize_textarea_field(wp_unslash($_POST['global_context']));
        }
        if (isset($_POST['output_mode'])) {
            $mode = sanitize_key(wp_unslash($_POST['output_mode']));
            $s['output_mode'] = in_array($mode, ['enabled', 'disabled'], true) ? $mode : 'enabled';
        }
        if (isset($_POST['seo_coexist_mode'])) {
            $coexist = sanitize_key(wp_unslash($_POST['seo_coexist_mode']));
            $s['seo_coexist_mode'] = in_array($coexist, ['smart', 'sync_seo', 'pewit_only', 'disabled'], true) ? $coexist : 'smart';
        }
        $s['global_schema_enabled'] = !empty($_POST['global_schema_enabled']) ? 1 : 0;
        $s['breadcrumb_enabled'] = !empty($_POST['breadcrumb_enabled']) ? 1 : 0;
        $s['etch_native_ui'] = !empty($_POST['etch_native_ui']) ? 1 : 0;
        $s['notify_email_enabled'] = !empty($_POST['notify_email_enabled']) ? 1 : 0;
        $s['notify_email_failures'] = !empty($_POST['notify_email_failures']) ? 1 : 0;
        if (isset($_POST['notify_email_to'])) {
            $s['notify_email_to'] = sanitize_email(wp_unslash($_POST['notify_email_to']));
        }
            $s['validation_scan_enabled'] = !empty($_POST['validation_scan_enabled']) ? 1 : 0;

        if (isset($_POST['enabled_post_types']) && is_array($_POST['enabled_post_types'])) {
            $s['enabled_post_types'] = array_values(array_unique(array_map('sanitize_key', wp_unslash($_POST['enabled_post_types']))));
        }
        if (isset($_POST['cpt_schema_map']) && is_array($_POST['cpt_schema_map'])) {
            $catalog = array_keys(AISB_CPT_Registry::schema_type_catalog());
            $clean = [];
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nested array; each key/value sanitized in the loop below.
            $cpt_schema_map = wp_unslash($_POST['cpt_schema_map']);
            foreach ($cpt_schema_map as $pt => $type) {
                $pt = sanitize_key($pt);
                $type = sanitize_key($type);
                if ($pt && in_array($type, $catalog, true)) {
                    $clean[$pt] = $type;
                }
            }
            $s['cpt_schema_map'] = $clean;
        }

        if (isset($_POST['schema_field_map']) && is_array($_POST['schema_field_map'])) {
            $targets = array_keys(AISB_Field_Mapper::schema_targets());
            $clean_map = [];
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nested array; each key/value sanitized in the loop below.
            $schema_field_map = wp_unslash($_POST['schema_field_map']);
            foreach ($schema_field_map as $pt => $fields) {
                $pt = sanitize_key($pt);
                if (!$pt || !is_array($fields)) {
                    continue;
                }
                $clean_map[$pt] = [];
                foreach ($fields as $source => $target) {
                    $source = sanitize_key($source);
                    $target = sanitize_key($target);
                    if ($source && $target && in_array($target, $targets, true)) {
                        $clean_map[$pt][$source] = $target;
                    }
                }
            }
            $s['schema_field_map'] = $clean_map;
        }

            $s['auto_generate'] = !empty($_POST['auto_generate']) ? 1 : 0;
            $s['batch_size'] = max(1, min(10, absint(wp_unslash($_POST['batch_size'] ?? 3))));
            $s['batch_delay'] = max(2, min(60, absint(wp_unslash($_POST['batch_delay'] ?? 5))));
        aisb_update_settings($s);
        AISB_Validation_Cron::maybe_schedule();
        wp_send_json_success(['message' => __('Settings saved.', 'pewit-ai-schema-builder')]);
    }

    public static function ajax_generate_faq_schema() {
        check_ajax_referer('aisb_nonce', 'nonce');
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(['message' => __('Permission denied', 'pewit-ai-schema-builder')], 403);
        }
        $post_id = isset($_POST['post_id']) ? absint(wp_unslash($_POST['post_id'])) : 0;
        if (!$post_id) {
            wp_send_json_error(['message' => __('Invalid post.', 'pewit-ai-schema-builder')]);
        }
        $schema = AISB_Integrations::build_faq_schema($post_id);
        if (is_wp_error($schema)) {
            wp_send_json_error(['message' => $schema->get_error_message()]);
        }
        $schema = AISB_Rich_Results::enrich_from_post($schema, $post_id);
        $validation = AISB_Schema::validate($schema, 'FAQPage');
        wp_send_json_success([
            'json'       => wp_json_encode($schema, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            'validation' => $validation,
        ]);
    }

    public static function ajax_test_api() {
        check_ajax_referer('aisb_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied', 'pewit-ai-schema-builder')], 403);
        }
        $key = isset($_POST['api_key']) ? sanitize_text_field(wp_unslash($_POST['api_key'])) : '';
        $s = aisb_get_settings();
        if ($key !== '') {
            $s['api_key'] = $key;
        }
        if (isset($_POST['model'])) {
            $s['model'] = sanitize_text_field(wp_unslash($_POST['model']));
        }
        if (isset($_POST['provider'])) {
            $s['provider'] = AISB_LLM_API::sanitize_provider_setting(sanitize_text_field(wp_unslash($_POST['provider'])), aisb_is_pro() || aisb_is_trial());
        }
        if (isset($_POST['global_context'])) {
            $s['global_context'] = sanitize_textarea_field(wp_unslash($_POST['global_context']));
        }
        $test = AISB_API::test($s['api_key']);
        $s['api_key_verified'] = !empty($test['ok']) ? 1 : 0;
        aisb_update_settings($s);
        if (!empty($test['ok'])) {
            wp_send_json_success([
                'message' => $test['message'],
                'mask'    => aisb_api_key_masked_label($s['api_key']),
            ]);
        }
        wp_send_json_error(['message' => $test['message'] ?? __('Connection failed', 'pewit-ai-schema-builder')]);
    }

    public static function ajax_start_bulk() {
        check_ajax_referer('aisb_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied', 'pewit-ai-schema-builder')], 403);
        }
        $existing = AISB_Queue::get_job();
        if ($existing && in_array($existing['status'] ?? '', ['running', 'paused'], true)) {
            wp_send_json_error(['message' => __('A job is already running.', 'pewit-ai-schema-builder')]);
        }
        $job = AISB_Queue::create_missing_job();
        if (is_wp_error($job)) {
            wp_send_json_error(['message' => $job->get_error_message()]);
        }
        ob_start();
        self::render_job_panel($job);
        wp_send_json_success(['message' => __('Bulk job started.', 'pewit-ai-schema-builder'), 'html' => ob_get_clean()]);
    }

    public static function ajax_boost_scores() {
        check_ajax_referer('aisb_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied', 'pewit-ai-schema-builder')], 403);
        }
        $existing = AISB_Queue::get_job();
        if ($existing && in_array($existing['status'] ?? '', ['running', 'paused'], true)) {
            wp_send_json_error(['message' => __('A job is already running.', 'pewit-ai-schema-builder')]);
        }
        $max = max(50, min(95, absint(wp_unslash($_POST['max_score'] ?? 80))));
        $job = AISB_Score_Booster::create_boost_job($max);
        if (is_wp_error($job)) {
            wp_send_json_error(['message' => $job->get_error_message()]);
        }
        ob_start();
        self::render_job_panel($job);
        wp_send_json_success([
            'message' => sprintf(/* translators: %d are replaced with dynamic values */ __('Score boost job started for %d posts.', 'pewit-ai-schema-builder'), (int) ($job['total'] ?? 0)),
            'html'    => ob_get_clean(),
        ]);
    }

    public static function ajax_job_status() {
        check_ajax_referer('aisb_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied', 'pewit-ai-schema-builder')], 403);
        }
        $job = AISB_Queue::get_job();
        ob_start();
        self::render_job_panel($job);
        wp_send_json_success(['job' => $job, 'html' => ob_get_clean()]);
    }

    public static function ajax_job_control() {
        check_ajax_referer('aisb_nonce', 'nonce');
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied', 'pewit-ai-schema-builder')], 403);
        }
        $action = sanitize_key($_POST['job_action'] ?? '');
        if ($action === 'pause') {
            $job = AISB_Queue::pause();
        } elseif ($action === 'resume') {
            $job = AISB_Queue::resume();
        } elseif ($action === 'stop') {
            $job = AISB_Queue::stop();
        } else {
            wp_send_json_error(['message' => __('Invalid action', 'pewit-ai-schema-builder')]);
        }
        ob_start();
        self::render_job_panel($job);
        wp_send_json_success(['job' => $job, 'html' => ob_get_clean()]);
    }

    /**
     * Other plugins often print license nags on every admin screen.
     * Clear them on our UI so the PEWIT shell stays clean.
     */
    public static function suppress_foreign_notices() {
        $page = isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : '';
        if ($page === '' || strpos($page, 'pewit-ai-schema-builder') !== 0) {
            return;
        }
        remove_all_actions('admin_notices');
        remove_all_actions('all_admin_notices');
        remove_all_actions('user_admin_notices');
        remove_all_actions('network_admin_notices');
    }
}
