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

class Linkquiver_Admin_Page {

    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_menu_page' ) );
        add_action( 'admin_init', array( $this, 'handle_actions' ) );
        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
    }

    public function add_menu_page() {
        add_options_page(
            'LinkQuiver',
            'LinkQuiver',
            'manage_options',
            'linkquiver',
            array( $this, 'render_page' )
        );
    }

    public function enqueue_styles( $hook ) {
        if ( 'settings_page_linkquiver' !== $hook ) {
            return;
        }
        wp_enqueue_style(
            'linkquiver-admin',
            LINKQUIVER_URL . 'admin/admin-page.css',
            array(),
            LINKQUIVER_VERSION
        );
    }

    public function handle_actions() {
        if ( ! isset( $_POST['linkquiver_action'] ) ) {
            return;
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        check_admin_referer( 'linkquiver_admin_action', 'linkquiver_nonce' );

        $action = sanitize_text_field( wp_unslash( $_POST['linkquiver_action'] ) );

        if ( 'save_key' === $action && isset( $_POST['linkquiver_api_key'] ) ) {
            // La cle posee ici est la branche ALTERNATIVE du gate des routes
            // /theme (Linkquiver_Rest_API::theme_permission), dont la branche
            // capacite exige `edit_theme_options` + `unfiltered_html`. La poser
            // derriere `manage_options` seul laissait un privilege FAIBLE ecrire
            // le credential qui deverrouille le privilege FORT : sur multisite,
            // un admin de sous-site (qui a `manage_options` mais pas
            // `unfiltered_html`) choisissait sa propre cle, puis s'en servait
            // pour injecter du <script> sur toutes les pages du site.
            //
            // On exige donc ici les memes capacites que la branche qu'elle
            // remplace. Un administrateur de site simple les a toutes ; c'est
            // exactement l'admin de sous-site multisite, et un site sous
            // DISALLOW_UNFILTERED_HTML, que ca ecarte.
            if ( ! current_user_can( 'edit_theme_options' ) || ! current_user_can( 'unfiltered_html' ) ) {
                add_settings_error(
                    'linkquiver_messages',
                    'linkquiver_key_forbidden',
                    __( 'You do not have permission to set the API key on this site.', 'linkquiver' ),
                    'error'
                );
                return;
            }

            $key = sanitize_text_field( wp_unslash( $_POST['linkquiver_api_key'] ) );
            if ( ! empty( $key ) ) {
                Linkquiver_API_Key::save( $key );
                add_settings_error(
                    'linkquiver_messages',
                    'linkquiver_key_saved',
                    __( 'API key saved successfully.', 'linkquiver' ),
                    'updated'
                );
            } else {
                add_settings_error(
                    'linkquiver_messages',
                    'linkquiver_key_empty',
                    __( 'Please enter a valid API key.', 'linkquiver' ),
                    'error'
                );
            }
        }

        // ── Redirect actions ────────────────────────────────────────────

        if ( 'add_redirect' === $action ) {
            $old_path = sanitize_text_field( wp_unslash( $_POST['redirect_old_path'] ?? '' ) );
            $new_url  = esc_url_raw( wp_unslash( $_POST['redirect_new_url'] ?? '' ) );
            $type     = absint( $_POST['redirect_type'] ?? 301 );

            if ( ! empty( $old_path ) && ! empty( $new_url ) ) {
                // upsert() returns 0 when it rejects the input (non-http(s)
                // scheme, protocol-relative //evil.com, or a root-path rule).
                // Report that instead of claiming success.
                $saved = Linkquiver_Redirect_Engine::upsert( $old_path, $new_url, null, $type );
                if ( $saved > 0 ) {
                    add_settings_error( 'linkquiver_messages', 'linkquiver_redirect_added', __( 'Redirect added.', 'linkquiver' ), 'updated' );
                } else {
                    add_settings_error( 'linkquiver_messages', 'linkquiver_redirect_error', __( 'Redirect rejected: destination must be an absolute http(s) URL and the source path cannot be the homepage.', 'linkquiver' ), 'error' );
                }
            } else {
                add_settings_error( 'linkquiver_messages', 'linkquiver_redirect_error', __( 'Both old path and new URL are required.', 'linkquiver' ), 'error' );
            }
        }

        if ( 'edit_redirect' === $action ) {
            $id       = absint( $_POST['redirect_id'] ?? 0 );
            $old_path = sanitize_text_field( wp_unslash( $_POST['redirect_old_path'] ?? '' ) );
            $new_url  = esc_url_raw( wp_unslash( $_POST['redirect_new_url'] ?? '' ) );
            $type     = absint( $_POST['redirect_type'] ?? 301 );

            // Apply the same http(s) scheme allowlist the REST upsert()/import()
            // paths enforce — esc_url_raw() alone lets protocol-relative
            // //evil.com through, which wp_validate_redirect() only reliably
            // blocks on WP >= 6.4.
            $scheme    = strtolower( wp_parse_url( $new_url, PHP_URL_SCHEME ) ?? '' );
            $norm_path = '/' . trim( $old_path, '/' );
            // Never allow a rule on the homepage: a 301 on '/' would trap the
            // site in a redirect loop. upsert() blocks this on the REST path;
            // the manual editor must not be a way around it.
            $is_root   = ( '' === $norm_path || '/' === $norm_path );

            if ( $id && ! empty( $old_path ) && ! empty( $new_url ) && in_array( $scheme, array( 'http', 'https' ), true ) && ! $is_root ) {
                global $wpdb;
                $table   = Linkquiver_Redirect_Engine::table_name();
                $updated = $wpdb->update(
                    $table,
                    array(
                        'old_path' => $norm_path,
                        'new_url'  => $new_url,
                        'type'     => $type,
                    ),
                    array( 'id' => $id ),
                    array( '%s', '%s', '%d' ),
                    array( '%d' )
                );
                // $wpdb->update returns false on a SQL error (e.g. the new
                // old_path collides with another row's UNIQUE index). Only claim
                // success when the write did not error.
                if ( false === $updated ) {
                    add_settings_error( 'linkquiver_messages', 'linkquiver_redirect_error', __( 'Update failed: that source path is already mapped by another redirect.', 'linkquiver' ), 'error' );
                } else {
                    Linkquiver_Redirect_Engine::flush_cache_group();
                    add_settings_error( 'linkquiver_messages', 'linkquiver_redirect_updated', __( 'Redirect updated.', 'linkquiver' ), 'updated' );
                }
            } else {
                add_settings_error( 'linkquiver_messages', 'linkquiver_redirect_error', __( 'A valid http(s) destination URL is required, and the source path cannot be the homepage.', 'linkquiver' ), 'error' );
            }
        }

        if ( 'delete_redirect' === $action ) {
            $id = absint( $_POST['redirect_id'] ?? 0 );
            if ( $id ) {
                global $wpdb;
                $table = Linkquiver_Redirect_Engine::table_name();
                $wpdb->delete( $table, array( 'id' => $id ), array( '%d' ) );
                Linkquiver_Redirect_Engine::flush_cache_group();
                add_settings_error( 'linkquiver_messages', 'linkquiver_redirect_deleted', __( 'Redirect deleted.', 'linkquiver' ), 'updated' );
            }
        }

        if ( 'flush_redirects' === $action ) {
            Linkquiver_Redirect_Engine::flush_all();
            add_settings_error( 'linkquiver_messages', 'linkquiver_redirects_flushed', __( 'All redirects deleted.', 'linkquiver' ), 'updated' );
        }

        if ( 'toggle_catchall' === $action ) {
            $current = get_option( 'linkquiver_catchall_404', 'no' );
            update_option( 'linkquiver_catchall_404', 'yes' === $current ? 'no' : 'yes' );
            add_settings_error( 'linkquiver_messages', 'linkquiver_catchall_toggled', __( 'Catch-all setting updated.', 'linkquiver' ), 'updated' );
        }

        // ── AI crawler log ──────────────────────────────────────────────

        if ( 'toggle_ai_crawlers' === $action ) {
            $enabled = ( 'no' !== get_option( Linkquiver_AI_Crawlers::OPT_ENABLED, 'yes' ) );
            update_option( Linkquiver_AI_Crawlers::OPT_ENABLED, $enabled ? 'no' : 'yes' );
            if ( $enabled ) {
                add_settings_error( 'linkquiver_messages', 'linkquiver_ai_off', __( 'AI crawler logging disabled.', 'linkquiver' ), 'updated' );
            } else {
                Linkquiver_AI_Crawlers::schedule_cron();
                add_settings_error( 'linkquiver_messages', 'linkquiver_ai_on', __( 'AI crawler logging enabled.', 'linkquiver' ), 'updated' );
            }
        }

        if ( 'save_ai_retention' === $action ) {
            $days = absint( $_POST['linkquiver_ai_retention'] ?? 90 );
            update_option( Linkquiver_AI_Crawlers::OPT_RETENTION, $days );
            add_settings_error( 'linkquiver_messages', 'linkquiver_ai_retention', __( 'Retention updated.', 'linkquiver' ), 'updated' );
        }

        if ( 'refresh_ai_ranges' === $action ) {
            Linkquiver_AI_Crawlers::refresh_ranges();
            $at = (int) get_option( Linkquiver_AI_Crawlers::OPT_RANGES_AT, 0 );
            if ( $at > 0 ) {
                add_settings_error( 'linkquiver_messages', 'linkquiver_ai_ranges_ok', __( 'Verification list refreshed.', 'linkquiver' ), 'updated' );
            } else {
                add_settings_error( 'linkquiver_messages', 'linkquiver_ai_ranges_ko', __( 'Could not reach the verification list. Hits will keep being recorded, but counted as unverified.', 'linkquiver' ), 'error' );
            }
        }
    }

    public function render_page() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        $is_preconfigured  = Linkquiver_API_Key::is_preconfigured();
        $has_key           = Linkquiver_API_Key::has_key();
        $last_validated_at = (int) get_option( 'linkquiver_last_validated_at', 0 );
        // "Validated" means the SaaS authenticated against this plugin in the
        // last 7 days — proof the key is actually connected end-to-end, not
        // just stored locally.
        $is_validated      = $has_key && $last_validated_at > 0 && ( time() - $last_validated_at ) < 7 * DAY_IN_SECONDS;
        $seo               = new Linkquiver_SEO_Handler();
        $seo_info          = $seo->detect();

        settings_errors( 'linkquiver_messages' );
        ?>
        <div class="wrap linkquiver-wrap">
            <h1>LinkQuiver</h1>

            <?php if ( '' === (string) get_option( 'permalink_structure', '' ) ) : ?>
            <!-- Redirects/URL-mapping rely on $wp->request, which WordPress only
                 populates under Pretty Permalinks. On Plain permalinks the web
                 server 404s custom paths before PHP runs, so 301s never fire. -->
            <div class="notice notice-warning">
                <p>
                    <strong><?php esc_html_e( 'Permalinks are set to “Plain”.', 'linkquiver' ); ?></strong>
                    <?php esc_html_e( 'LinkQuiver redirects and URL mapping require “Pretty” permalinks to work.', 'linkquiver' ); ?>
                    <?php
                    printf(
                        /* translators: %s: link to the WordPress Permalinks settings screen. */
                        esc_html__( 'Change them under %s (any option other than “Plain”), then save. Content publishing via the API is unaffected.', 'linkquiver' ),
                        '<a href="' . esc_url( admin_url( 'options-permalink.php' ) ) . '">' . esc_html__( 'Settings → Permalinks', 'linkquiver' ) . '</a>'
                    );
                    ?>
                </p>
            </div>
            <?php endif; ?>

            <?php if ( ! $has_key ) : ?>
            <!-- Welcome / Get Started — shown only on first install (no key yet) -->
            <div class="linkquiver-card linkquiver-welcome">
                <h2><?php esc_html_e( 'Get started', 'linkquiver' ); ?></h2>
                <p class="linkquiver-welcome-intro">
                    <?php esc_html_e( 'Connect this site to LinkQuiver to enable AI-powered content publishing, SEO automation, and content resurrection. Two steps, no Application Password setup required.', 'linkquiver' ); ?>
                </p>

                <ol class="linkquiver-steps">
                    <li class="linkquiver-step">
                        <div class="linkquiver-step-number">1</div>
                        <div class="linkquiver-step-body">
                            <h3><?php esc_html_e( 'Create a free LinkQuiver account', 'linkquiver' ); ?></h3>
                            <p><?php esc_html_e( 'Sign up at linkquiver.com to get your personal API key. Free accounts include access to the WordPress connector.', 'linkquiver' ); ?></p>
                            <p>
                                <a class="button button-secondary" href="https://linkquiver.com" target="_blank" rel="noopener noreferrer">
                                    <?php esc_html_e( 'Open linkquiver.com', 'linkquiver' ); ?> &rarr;
                                </a>
                            </p>
                        </div>
                    </li>
                    <li class="linkquiver-step">
                        <div class="linkquiver-step-number">2</div>
                        <div class="linkquiver-step-body">
                            <h3><?php esc_html_e( 'Paste your API key', 'linkquiver' ); ?></h3>
                            <p><?php esc_html_e( 'Copy the key from your linkquiver.com dashboard (WordPress section), paste it below, and save.', 'linkquiver' ); ?></p>
                            <form method="post" class="linkquiver-key-form">
                                <?php wp_nonce_field( 'linkquiver_admin_action', 'linkquiver_nonce' ); ?>
                                <input type="hidden" name="linkquiver_action" value="save_key" />
                                <label for="linkquiver_api_key" class="screen-reader-text"><?php esc_html_e( 'API Key', 'linkquiver' ); ?></label>
                                <input
                                    type="password"
                                    id="linkquiver_api_key"
                                    name="linkquiver_api_key"
                                    value=""
                                    class="regular-text"
                                    placeholder="rse_..."
                                />
                                <button type="button" class="button button-small" onclick="var f=document.getElementById('linkquiver_api_key');f.type=f.type==='password'?'text':'password';">
                                    <?php esc_html_e( 'Show/Hide', 'linkquiver' ); ?>
                                </button>
                                <button type="submit" class="button button-primary"><?php esc_html_e( 'Save API Key', 'linkquiver' ); ?></button>
                            </form>
                        </div>
                    </li>
                </ol>
            </div>

            <!-- Visual separator before the standalone redirect manager -->
            <div class="linkquiver-or-divider"><span><?php esc_html_e( 'OR', 'linkquiver' ); ?></span></div>

            <div class="linkquiver-card linkquiver-standalone-note">
                <p>
                    <strong><?php esc_html_e( 'No LinkQuiver account?', 'linkquiver' ); ?></strong>
                    <?php esc_html_e( 'The plugin also ships with a standalone 301 Redirect Manager that works on its own, no signup required. Scroll down to use it.', 'linkquiver' ); ?>
                </p>
            </div>
            <?php else : ?>
            <!-- API Key (configured state) -->
            <div class="linkquiver-card">
                <h2><?php esc_html_e( 'API Key', 'linkquiver' ); ?></h2>
                <table class="form-table">
                    <tr>
                        <th><?php esc_html_e( 'Status', 'linkquiver' ); ?></th>
                        <td>
                            <?php if ( $is_validated ) : ?>
                                <span class="linkquiver-badge linkquiver-badge-ok"><?php esc_html_e( 'Connected to LinkQuiver', 'linkquiver' ); ?></span>
                                <p class="description" style="margin-top:6px;">
                                    <?php
                                    printf(
                                        /* translators: %s: human-readable duration, e.g. "2 hours". */
                                        esc_html__( 'Last verified by LinkQuiver: %s ago.', 'linkquiver' ),
                                        esc_html( human_time_diff( $last_validated_at, time() ) )
                                    );
                                    ?>
                                </p>
                            <?php else : ?>
                                <span class="linkquiver-badge linkquiver-badge-neutral"><?php esc_html_e( 'API key set — waiting for LinkQuiver', 'linkquiver' ); ?></span>
                                <p class="description" style="margin-top:6px;">
                                    <?php
                                    printf(
                                        /* translators: %s: link to linkquiver.com. */
                                        esc_html__( 'Go to your %s site dashboard and click "Test connection" to validate the link end-to-end.', 'linkquiver' ),
                                        '<a href="https://linkquiver.com" target="_blank" rel="noopener noreferrer">linkquiver.com</a>'
                                    );
                                    ?>
                                </p>
                            <?php endif; ?>
                        </td>
                    </tr>
                    <tr>
                        <th><?php esc_html_e( 'Current Key', 'linkquiver' ); ?></th>
                        <td>
                            <code class="linkquiver-key">••••••••••••••••</code>
                            <p class="description" style="margin-top:6px;">
                                <?php esc_html_e( 'The API key is stored as a salted hash. The cleartext is no longer kept anywhere on this site — if you lose your copy you\'ll need to generate a new key in the LinkQuiver dashboard and save it here.', 'linkquiver' ); ?>
                            </p>
                        </td>
                    </tr>
                </table>
                <p><?php esc_html_e( 'To change your API key, enter a new one below.', 'linkquiver' ); ?></p>

                <form method="post">
                    <?php wp_nonce_field( 'linkquiver_admin_action', 'linkquiver_nonce' ); ?>
                    <input type="hidden" name="linkquiver_action" value="save_key" />
                    <table class="form-table">
                        <tr>
                            <th><label for="linkquiver_api_key"><?php esc_html_e( 'New API Key', 'linkquiver' ); ?></label></th>
                            <td>
                                <input
                                    type="password"
                                    id="linkquiver_api_key"
                                    name="linkquiver_api_key"
                                    value=""
                                    class="regular-text"
                                    placeholder="rse_..."
                                />
                                <button type="button" class="button button-small" onclick="var f=document.getElementById('linkquiver_api_key');f.type=f.type==='password'?'text':'password';">
                                    <?php esc_html_e( 'Show/Hide', 'linkquiver' ); ?>
                                </button>
                            </td>
                        </tr>
                    </table>
                    <button type="submit" class="button button-primary"><?php esc_html_e( 'Update API Key', 'linkquiver' ); ?></button>
                </form>
            </div>
            <?php endif; ?>

            <?php if ( $has_key ) : ?>
            <!-- System Status -->
            <div class="linkquiver-card">
                <h2><?php esc_html_e( 'System Status', 'linkquiver' ); ?></h2>
                <table class="linkquiver-status-table">
                    <tr>
                        <td><?php esc_html_e( 'WordPress', 'linkquiver' ); ?></td>
                        <td><?php echo esc_html( get_bloginfo( 'version' ) ); ?></td>
                        <td><span class="linkquiver-badge linkquiver-badge-ok"><?php esc_html_e( 'OK', 'linkquiver' ); ?></span></td>
                    </tr>
                    <tr>
                        <td><?php esc_html_e( 'PHP', 'linkquiver' ); ?></td>
                        <td><?php echo esc_html( PHP_VERSION ); ?></td>
                        <td><span class="linkquiver-badge <?php echo version_compare( PHP_VERSION, '7.4', '>=' ) ? 'linkquiver-badge-ok' : 'linkquiver-badge-warn'; ?>">
                            <?php echo version_compare( PHP_VERSION, '7.4', '>=' ) ? esc_html__( 'OK', 'linkquiver' ) : esc_html__( 'Update recommended', 'linkquiver' ); ?>
                        </span></td>
                    </tr>
                    <tr>
                        <td><?php esc_html_e( 'REST API', 'linkquiver' ); ?></td>
                        <td><?php esc_html_e( 'Available', 'linkquiver' ); ?></td>
                        <td><span class="linkquiver-badge linkquiver-badge-ok"><?php esc_html_e( 'OK', 'linkquiver' ); ?></span></td>
                    </tr>
                    <tr>
                        <?php
                        $upload_dir = wp_upload_dir();
                        $writable   = wp_is_writable( $upload_dir['basedir'] );
                        ?>
                        <td><?php esc_html_e( 'Uploads', 'linkquiver' ); ?></td>
                        <td><?php echo $writable ? esc_html__( 'Writable', 'linkquiver' ) : esc_html__( 'Not writable', 'linkquiver' ); ?></td>
                        <td><span class="linkquiver-badge <?php echo $writable ? 'linkquiver-badge-ok' : 'linkquiver-badge-error'; ?>">
                            <?php echo $writable ? esc_html__( 'OK', 'linkquiver' ) : esc_html__( 'Error', 'linkquiver' ); ?>
                        </span></td>
                    </tr>
                    <tr>
                        <td><?php esc_html_e( 'Max Upload', 'linkquiver' ); ?></td>
                        <td><?php echo esc_html( size_format( wp_max_upload_size() ) ); ?></td>
                        <td><span class="linkquiver-badge linkquiver-badge-ok"><?php esc_html_e( 'OK', 'linkquiver' ); ?></span></td>
                    </tr>
                    <tr>
                        <td><?php esc_html_e( 'SEO Plugin', 'linkquiver' ); ?></td>
                        <td><?php echo esc_html( ucfirst( str_replace( '_', ' ', $seo_info['plugin'] ) ) ); ?></td>
                        <td><span class="linkquiver-badge <?php echo $seo_info['active'] ? 'linkquiver-badge-ok' : 'linkquiver-badge-neutral'; ?>">
                            <?php echo $seo_info['active'] ? esc_html__( 'Detected', 'linkquiver' ) : esc_html__( 'None', 'linkquiver' ); ?>
                        </span></td>
                    </tr>
                </table>
            </div>

            <?php endif; ?>

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

            <!-- Redirections -->
            <div class="linkquiver-card">
                <h2><?php esc_html_e( 'Redirections', 'linkquiver' ); ?> <span class="linkquiver-badge linkquiver-badge-neutral"><?php echo esc_html( Linkquiver_Redirect_Engine::count() ); ?></span></h2>
                <?php $catchall = get_option( 'linkquiver_catchall_404', 'no' ); ?>
                <p class="description">
                    <?php esc_html_e( 'Manage 301 redirects from old URLs to WordPress posts. Add them manually below, or have them auto-populated when publishing through LinkQuiver. Works standalone — no LinkQuiver account required.', 'linkquiver' ); ?>
                    <?php
                    printf(
                        /* translators: %s: the word "enabled" or "disabled", already bolded. */
                        esc_html__( 'Unmatched URLs return a 301 to the homepage (%s).', 'linkquiver' ),
                        '<strong>' . ( 'yes' === $catchall ? esc_html__( 'enabled', 'linkquiver' ) : esc_html__( 'disabled', 'linkquiver' ) ) . '</strong>'
                    );
                    ?>
                </p>
                <form method="post" style="display:inline;">
                    <?php wp_nonce_field( 'linkquiver_admin_action', 'linkquiver_nonce' ); ?>
                    <input type="hidden" name="linkquiver_action" value="toggle_catchall" />
                    <button type="submit" class="button button-small"><?php echo 'yes' === $catchall ? esc_html__( 'Disable catch-all', 'linkquiver' ) : esc_html__( 'Enable catch-all', 'linkquiver' ); ?></button>
                </form>

                <!-- Add new redirect -->
                <form method="post" class="linkquiver-redirect-form">
                    <?php wp_nonce_field( 'linkquiver_admin_action', 'linkquiver_nonce' ); ?>
                    <input type="hidden" name="linkquiver_action" value="add_redirect" />
                    <div class="linkquiver-redirect-row">
                        <input type="text" name="redirect_old_path" placeholder="/old/page.html" class="regular-text linkquiver-input-path" required />
                        <span class="linkquiver-arrow">&rarr;</span>
                        <input type="url" name="redirect_new_url" placeholder="https://example.com/new-page/" class="regular-text linkquiver-input-url" required />
                        <select name="redirect_type" class="linkquiver-select-type">
                            <option value="301">301</option>
                            <option value="302">302</option>
                        </select>
                        <button type="submit" class="button button-primary"><?php esc_html_e( 'Add', 'linkquiver' ); ?></button>
                    </div>
                </form>

                <?php
                // Paginate at the SQL level so accounts with >1000 redirects
                // (bulk expired-domain imports) stay fully browsable/editable —
                // the previous fetch-then-slice capped visibility at 1000 rows.
                $per_page     = 20;
                $total        = Linkquiver_Redirect_Engine::count();
                $total_pages  = max( 1, (int) ceil( $total / $per_page ) );
                $current_page = min( $total_pages, max( 1, absint( $_GET['redir_page'] ?? 1 ) ) );
                $offset       = ( $current_page - 1 ) * $per_page;
                $page_items   = $total > 0 ? Linkquiver_Redirect_Engine::list_all( $per_page, $offset ) : array();
                if ( ! empty( $page_items ) ) :
                ?>
                    <table class="linkquiver-redirects-table">
                        <thead>
                            <tr>
                                <th><?php esc_html_e( 'Old Path', 'linkquiver' ); ?></th>
                                <th><?php esc_html_e( 'New URL', 'linkquiver' ); ?></th>
                                <th><?php esc_html_e( 'Type', 'linkquiver' ); ?></th>
                                <th><?php esc_html_e( 'Actions', 'linkquiver' ); ?></th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ( $page_items as $r ) : ?>
                            <tr id="redir-row-<?php echo esc_attr( $r['id'] ); ?>">
                                <td>
                                    <code class="linkquiver-redirect-path"><?php echo esc_html( $r['old_path'] ); ?></code>
                                </td>
                                <td>
                                    <a href="<?php echo esc_url( $r['new_url'] ); ?>" target="_blank" rel="noopener" class="linkquiver-redirect-url">
                                        <?php echo esc_html( $r['new_url'] ); ?>
                                    </a>
                                </td>
                                <td><span class="linkquiver-badge linkquiver-badge-ok"><?php echo esc_html( $r['type'] ); ?></span></td>
                                <td class="linkquiver-redirect-actions">
                                    <button type="button" class="button button-small" onclick="linkquiverEditRedirect(<?php echo esc_attr( $r['id'] ); ?>, '<?php echo esc_js( $r['old_path'] ); ?>', '<?php echo esc_js( $r['new_url'] ); ?>', <?php echo esc_attr( $r['type'] ); ?>)"><?php esc_html_e( 'Edit', 'linkquiver' ); ?></button>
                                    <form method="post" style="display:inline;" onsubmit="return confirm('<?php echo esc_js( __( 'Delete this redirect?', 'linkquiver' ) ); ?>');">
                                        <?php wp_nonce_field( 'linkquiver_admin_action', 'linkquiver_nonce' ); ?>
                                        <input type="hidden" name="linkquiver_action" value="delete_redirect" />
                                        <input type="hidden" name="redirect_id" value="<?php echo esc_attr( $r['id'] ); ?>" />
                                        <button type="submit" class="button button-small button-link-delete"><?php esc_html_e( 'Delete', 'linkquiver' ); ?></button>
                                    </form>
                                </td>
                            </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>

                    <?php if ( $total_pages > 1 ) : ?>
                    <div class="linkquiver-pagination">
                        <?php
                        $base_url = admin_url( 'options-general.php?page=linkquiver' );
                        for ( $i = 1; $i <= $total_pages; $i++ ) :
                            $page_url = add_query_arg( 'redir_page', $i, $base_url );
                        ?>
                            <?php if ( $i === $current_page ) : ?>
                                <span class="linkquiver-page-current"><?php echo esc_html( $i ); ?></span>
                            <?php else : ?>
                                <a href="<?php echo esc_url( $page_url ); ?>" class="linkquiver-page-link"><?php echo esc_html( $i ); ?></a>
                            <?php endif; ?>
                        <?php endfor; ?>
                        <span class="linkquiver-page-info">(<?php
                            printf(
                                /* translators: %s: total number of redirects. */
                                esc_html__( '%s total', 'linkquiver' ),
                                esc_html( $total )
                            );
                        ?>)</span>
                    </div>
                    <?php endif; ?>

                    <div style="margin-top: 12px;">
                        <form method="post" style="display:inline;" onsubmit="return confirm('<?php echo esc_js( __( 'Delete ALL redirects? This cannot be undone.', 'linkquiver' ) ); ?>');">
                            <?php wp_nonce_field( 'linkquiver_admin_action', 'linkquiver_nonce' ); ?>
                            <input type="hidden" name="linkquiver_action" value="flush_redirects" />
                            <button type="submit" class="button button-link-delete"><?php esc_html_e( 'Delete all redirects', 'linkquiver' ); ?></button>
                        </form>
                    </div>

                <?php else : ?>
                    <p class="linkquiver-empty"><?php esc_html_e( 'No redirects yet. They will be added automatically when you publish pages via the API, or you can add them manually above.', 'linkquiver' ); ?></p>
                <?php endif; ?>
            </div>

            <!-- Edit redirect modal (inline) -->
            <div id="linkquiver-edit-modal" class="linkquiver-card" style="display:none; border-left: 4px solid #0073aa;">
                <h3 style="margin-top:0;"><?php esc_html_e( 'Edit Redirect', 'linkquiver' ); ?></h3>
                <form method="post">
                    <?php wp_nonce_field( 'linkquiver_admin_action', 'linkquiver_nonce' ); ?>
                    <input type="hidden" name="linkquiver_action" value="edit_redirect" />
                    <input type="hidden" name="redirect_id" id="edit-redirect-id" />
                    <div class="linkquiver-redirect-row">
                        <input type="text" name="redirect_old_path" id="edit-redirect-old-path" class="regular-text linkquiver-input-path" required />
                        <span class="linkquiver-arrow">&rarr;</span>
                        <input type="url" name="redirect_new_url" id="edit-redirect-new-url" class="regular-text linkquiver-input-url" required />
                        <select name="redirect_type" id="edit-redirect-type" class="linkquiver-select-type">
                            <option value="301">301</option>
                            <option value="302">302</option>
                        </select>
                        <button type="submit" class="button button-primary"><?php esc_html_e( 'Save', 'linkquiver' ); ?></button>
                        <button type="button" class="button" onclick="document.getElementById('linkquiver-edit-modal').style.display='none';"><?php esc_html_e( 'Cancel', 'linkquiver' ); ?></button>
                    </div>
                </form>
            </div>

            <script>
            function linkquiverEditRedirect(id, oldPath, newUrl, type) {
                document.getElementById('edit-redirect-id').value = id;
                document.getElementById('edit-redirect-old-path').value = oldPath;
                document.getElementById('edit-redirect-new-url').value = newUrl;
                document.getElementById('edit-redirect-type').value = type;
                var modal = document.getElementById('linkquiver-edit-modal');
                modal.style.display = 'block';
                modal.scrollIntoView({ behavior: 'smooth' });
            }

            </script>
        </div>
        <?php
    }

    /**
     * AI crawler visibility panel.
     *
     * Shows the last 30 days per bot, with the verified share spelled out.
     * The wording is deliberately careful: "verified" here means the source IP
     * was inside a range the vendor publishes, which is a much weaker claim
     * than "this number is trustworthy". See class-ai-crawlers.php.
     */
    private function render_ai_crawlers_card() {
        $enabled   = ( 'no' !== get_option( Linkquiver_AI_Crawlers::OPT_ENABLED, 'yes' ) );
        $ranges_at = (int) get_option( Linkquiver_AI_Crawlers::OPT_RANGES_AT, 0 );
        $retention = Linkquiver_AI_Crawlers::retention_days();
        $report    = $enabled ? Linkquiver_AI_Crawlers::report( 30, 10 ) : null;
        ?>
        <div class="linkquiver-card">
            <h2><?php esc_html_e( 'AI crawler visibility', 'linkquiver' ); ?></h2>
            <p class="description">
                <?php esc_html_e( 'Counts which AI crawlers (GPTBot, ClaudeBot, PerplexityBot and others) fetched which post. Only daily totals per bot and per post are stored: no IP address, no User-Agent, nothing per-visitor.', 'linkquiver' ); ?>
            </p>
            <p class="description">
                <?php esc_html_e( 'A hit counts as verified only when it came from an IP range the vendor itself publishes. That rules out a crawler wearing a fake User-Agent; it does not turn the figure into proof, since the numbers are produced on this server.', 'linkquiver' ); ?>
            </p>

            <form method="post" style="display:inline;">
                <?php wp_nonce_field( 'linkquiver_admin_action', 'linkquiver_nonce' ); ?>
                <input type="hidden" name="linkquiver_action" value="toggle_ai_crawlers" />
                <button type="submit" class="button button-small">
                    <?php echo $enabled ? esc_html__( 'Disable logging', 'linkquiver' ) : esc_html__( 'Enable logging', 'linkquiver' ); ?>
                </button>
            </form>

            <?php if ( $enabled ) : ?>
            <form method="post" style="display:inline; margin-left:8px;">
                <?php wp_nonce_field( 'linkquiver_admin_action', 'linkquiver_nonce' ); ?>
                <input type="hidden" name="linkquiver_action" value="refresh_ai_ranges" />
                <button type="submit" class="button button-small"><?php esc_html_e( 'Refresh verification list', 'linkquiver' ); ?></button>
            </form>

            <form method="post" style="display:inline; margin-left:8px;">
                <?php wp_nonce_field( 'linkquiver_admin_action', 'linkquiver_nonce' ); ?>
                <input type="hidden" name="linkquiver_action" value="save_ai_retention" />
                <label for="linkquiver_ai_retention"><?php esc_html_e( 'Keep data for', 'linkquiver' ); ?></label>
                <select name="linkquiver_ai_retention" id="linkquiver_ai_retention">
                    <?php foreach ( array( 30, 90, 180, 365 ) as $choice ) : ?>
                    <option value="<?php echo esc_attr( $choice ); ?>" <?php selected( $retention, $choice ); ?>>
                        <?php
                        printf(
                            /* translators: %d: number of days. */
                            esc_html( _n( '%d day', '%d days', $choice, 'linkquiver' ) ),
                            (int) $choice
                        );
                        ?>
                    </option>
                    <?php endforeach; ?>
                </select>
                <button type="submit" class="button button-small"><?php esc_html_e( 'Save', 'linkquiver' ); ?></button>
            </form>

            <p class="description" style="margin-top:10px;">
                <?php if ( $ranges_at > 0 ) : ?>
                    <?php
                    printf(
                        /* translators: %s: human-readable duration, e.g. "3 hours". */
                        esc_html__( 'Verification list updated %s ago.', 'linkquiver' ),
                        esc_html( human_time_diff( $ranges_at, time() ) )
                    );
                    ?>
                <?php else : ?>
                    <?php esc_html_e( 'Verification list not downloaded yet — hits are recorded but all counted as unverified until it arrives.', 'linkquiver' ); ?>
                <?php endif; ?>
            </p>

                <?php if ( ! empty( $report['bots'] ) ) : ?>
                <table class="linkquiver-status-table">
                    <thead>
                        <tr>
                            <th><?php esc_html_e( 'Crawler', 'linkquiver' ); ?></th>
                            <th><?php esc_html_e( 'Hits (30 days)', 'linkquiver' ); ?></th>
                            <th><?php esc_html_e( 'Verified', 'linkquiver' ); ?></th>
                        </tr>
                    </thead>
                    <tbody>
                    <?php foreach ( $report['bots'] as $bot => $counts ) : ?>
                        <tr>
                            <td><code><?php echo esc_html( $bot ); ?></code></td>
                            <td><?php echo esc_html( number_format_i18n( $counts['hits'] ) ); ?></td>
                            <td><?php echo esc_html( number_format_i18n( $counts['verified'] ) ); ?></td>
                        </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
                <?php else : ?>
                <p class="linkquiver-empty"><?php esc_html_e( 'No AI crawler has been seen yet. Give it a few days.', 'linkquiver' ); ?></p>
                <?php endif; ?>
            <?php endif; ?>
        </div>
        <?php
    }
}
