<?php
/**
 * Admin Settings Page
 *
 * @package SEOvaultSEO
 */

namespace SEOvaultSEO\Admin;

use SEOvaultSEO\Core\Options;
use SEOvaultSEO\Compatibility\Detector;
use SEOvaultSEO\NotFound\AdminSettings as NotFoundAdminSettings;
use SEOvaultSEO\Utils\Sanitizer;
use SEOvaultSEO\Utils\Logger;

/**
 * Handles plugin settings page
 */
class SettingsPage {

	/**
	 * Initialize settings page
	 */
	public static function init() {
		add_action( 'admin_init', array( __CLASS__, 'register_settings' ) );
		add_action( 'admin_init', array( __CLASS__, 'handle_admin_actions' ) );
		add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
		add_action( 'wp_ajax_seovaultseo_robots_preview', array( __CLASS__, 'ajax_robots_preview' ) );
	}

	public static function plugin_action_links( $links ) {
		$settings_link = '<a href="' . esc_url( admin_url( 'admin.php?page=seovault-ai' ) ) . '">' . __( 'Settings', 'seovault-ai' ) . '</a>';
		array_unshift( $links, $settings_link );
		return $links;
	}

	public static function handle_admin_actions() {
		// Authorization for all settings actions (admin_init runs on every admin page).
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		// SEC-026: Clear API rate-limit / lockout transients.
		if ( isset( $_GET['action'] ) && 'clear_api_lockouts' === $_GET['action'] && check_admin_referer( 'seovaultseo_clear_api_lockouts_action' ) ) {
			$ip = isset( $_GET['lockout_ip'] ) ? sanitize_text_field( wp_unslash( $_GET['lockout_ip'] ) ) : '';
			Logger::log( 'Admin action: clear_api_lockouts', array( 'ip' => '' !== $ip ? 'provided' : 'all' ) );
			if ( '' !== $ip ) {
				\SEOvaultSEO\Api\RateLimiter::clear_rate_limits_for_ip( $ip );
				\SEOvaultSEO\Api\AuditLog::log_admin_clear_lockouts( 'ip' );
			} else {
				\SEOvaultSEO\Api\RateLimiter::clear_all_rate_limit_transients();
				\SEOvaultSEO\Api\AuditLog::log_admin_clear_lockouts( 'all' );
			}
			add_settings_error( 'seovault_seo_settings', 'api_lockouts_cleared', __( 'API lockouts and rate-limit counters cleared.', 'seovault-ai' ), 'updated' );
		}

		// P6-001: Key mutations are POST-only (avoid nonce-in-URL leakage).
		if ( isset( $_POST['seovaultseo_regenerate_key'] ) && check_admin_referer( 'seovaultseo_regenerate_key_action' ) ) {
			Logger::log( 'Admin action: regenerate site key (POST)' );
			$new_key = wp_generate_password( 40, false );
			Options::set_site_key( $new_key );
			Options::set_connection_verified( 'no' );
			Options::set_connected_at( '' );
			Logger::log( 'Admin action: site key regenerated, connection reset' );
			add_settings_error( 'seovault_seo_settings', 'key_regenerated', __( 'Site key regenerated. Update it in your SEOvault AI web app.', 'seovault-ai' ), 'updated' );
		}

		// SEC-041: Generate optional read-only API key (does not replace main site key).
		if ( isset( $_POST['seovaultseo_generate_site_read_key'] ) && check_admin_referer( 'seovaultseo_generate_read_key_action' ) ) {
			$new_key = wp_generate_password( 40, false );
			Options::set_site_read_key( $new_key );
			set_transient( 'seovaultseo_flash_site_read_key', $new_key, 5 * MINUTE_IN_SECONDS );
			Logger::log( 'Admin action: site read key generated' );
			add_settings_error( 'seovault_seo_settings', 'read_key_generated', __( 'Read-only API key generated. Copy it now — it will only be shown once.', 'seovault-ai' ), 'updated' );
		}

		// SEC-042: Generate optional write-scoped API key (does not replace main site key).
		if ( isset( $_POST['seovaultseo_generate_site_write_key'] ) && check_admin_referer( 'seovaultseo_generate_write_key_action' ) ) {
			$new_key = wp_generate_password( 40, false );
			Options::set_site_write_key( $new_key );
			set_transient( 'seovaultseo_flash_site_write_key', $new_key, 5 * MINUTE_IN_SECONDS );
			Logger::log( 'Admin action: site write key generated' );
			add_settings_error( 'seovault_seo_settings', 'write_key_generated', __( 'Write API key generated. Copy it now — it will only be shown once.', 'seovault-ai' ), 'updated' );
		}

		if ( isset( $_GET['action'] ) && 'purge_sitemap_cache' === $_GET['action'] && check_admin_referer( 'seovaultseo_sitemap_purge_action' ) ) {
			Logger::log( 'Admin action: purge_sitemap_cache' );
			if ( class_exists( '\SEOvaultSEO\Sitemap\Admin' ) ) {
				\SEOvaultSEO\Sitemap\Admin::purge_cache();
				Logger::log( 'Admin action: sitemap cache purged' );
				add_settings_error( 'seovault_seo_settings', 'sitemap_cache_purged', __( 'Sitemap cache purged and rebuild started.', 'seovault-ai' ), 'updated' );
			}
		}

		// Unified save: single "Save All Settings" button saves every section.
		if ( isset( $_POST['seovaultseo_save_all_settings'] ) && check_admin_referer( 'seovaultseo_save_all_settings_action' ) ) {
			$request = $_POST;
			// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified via check_admin_referer() above.
			Logger::log( 'Admin action: save_all_settings entered', array( 'active_tab' => self::post_text( $request, 'seovaultseo_active_tab' ) ) );
			// Connection / Output / Homepage / Templates / Social (formerly Settings API fields).
			Options::set_output_mode( self::post_text( $request, 'seovaultseo_output_mode', 'auto' ) );
			Options::set_home_title( self::post_text( $request, 'seovaultseo_home_title' ) );
			Options::set_home_description( self::post_textarea( $request, 'seovaultseo_home_description' ) );
			Options::set_title_template( self::post_text( $request, 'seovaultseo_title_template' ) );
			Options::set_title_separator( self::post_text( $request, 'seovaultseo_title_separator', '-' ) );
			Options::set_templates_enabled( isset( $_POST['seovaultseo_templates_enabled'] ) );
			Options::set_description_template( self::post_textarea( $request, 'seovaultseo_description_template' ) );
			Options::set_default_social_image( self::post_url( $request, 'seovaultseo_default_social_image' ) );

			// Social — Facebook.
			Options::set_facebook_page_url( self::post_url( $request, 'seovaultseo_facebook_page_url' ) );
			Options::set_facebook_author_fallback( self::post_url( $request, 'seovaultseo_facebook_author_fallback' ) );

			// Social — X (Twitter).
			Options::set_twitter_card_type( Sanitizer::sanitize_twitter_card_type( self::post_text( $request, 'seovaultseo_twitter_card_type', 'summary_large_image' ) ) );
			Options::set_twitter_profile_username( self::post_url( $request, 'seovaultseo_twitter_profile_username' ) );
			Options::set_twitter_profile_fallback( self::post_url( $request, 'seovaultseo_twitter_profile_fallback' ) );

			// Social — Other Accounts (array of URLs).
			$social_profiles = isset( $_POST['seovaultseo_social_profiles'] )
				? Sanitizer::sanitize_social_profiles( map_deep( wp_unslash( (array) $_POST['seovaultseo_social_profiles'] ), 'wp_strip_all_tags' ) )
				: array();
			Options::set_social_profiles( $social_profiles );

			$llms_was_enabled = Options::is_llms_enabled();
			Options::set_llms_enabled( isset( $_POST['seovaultseo_llms_enabled'] ) ? 'yes' : 'no' );
			if ( $llms_was_enabled !== Options::is_llms_enabled() ) {
				flush_rewrite_rules( false );
			}

			// Breadcrumbs.
			self::save_breadcrumbs( 'seovaultseo_save_all_settings_action' );

			// 404 Monitor.
			self::save_404_monitor( 'seovaultseo_save_all_settings_action' );

			// Image SEO.
			self::save_image_seo( 'seovaultseo_save_all_settings_action' );

			// Frontend Score.
			self::save_frontend_score( 'seovaultseo_save_all_settings_action' );

			// WooCommerce.
			self::save_woocommerce( 'seovaultseo_save_all_settings_action' );

			// Analytics / Tracking Code.
			self::save_analytics( 'seovaultseo_save_all_settings_action' );

			// Local SEO.
			self::save_local_seo( 'seovaultseo_save_all_settings_action' );

			// IndexNow.
			self::save_indexnow( 'seovaultseo_save_all_settings_action' );

			// Robots.
			self::save_robots( 'seovaultseo_save_all_settings_action' );

			// Sitemap.
			if ( class_exists( '\SEOvaultSEO\Sitemap\Admin' ) ) {
				\SEOvaultSEO\Sitemap\Admin::save_sitemap_settings();
			}

			// Tools — uninstall data policy.
			Options::set_erase_on_uninstall( isset( $_POST['seovaultseo_erase_on_uninstall'] ) );

			Logger::log(
				'Admin action: save_all_settings success',
				array(
					'output_mode'       => self::post_text( $request, 'seovaultseo_output_mode', 'auto' ),
					'llms_enabled'      => Options::is_llms_enabled(),
					'local_seo_present' => isset( $_POST['seovaultseo_local_settings'] ),
				)
			);
			add_settings_error( 'seovault_seo_settings', 'settings_saved', __( 'All settings saved.', 'seovault-ai' ), 'updated' );

			// PRG: redirect back to the same tab so the correct panel is server-rendered
			// (avoids Connection flash before JS runs).
			$tab = isset( $_POST['seovaultseo_active_tab'] )
				? sanitize_text_field( wp_unslash( $_POST['seovaultseo_active_tab'] ) )
				: 'vtab-connection';
			// phpcs:enable WordPress.Security.NonceVerification.Missing
			if ( class_exists( '\SEOvaultSEO\Admin\Dashboard' ) ) {
				$tab = \SEOvaultSEO\Admin\Dashboard::sanitize_general_tab( $tab );
			} else {
				$tab = ltrim( $tab, '#' );
			}
			set_transient( 'settings_errors', get_settings_errors(), 30 );
			// WP PHPUnit defines WP_TESTS_DOMAIN; preserve state for assertions
			// without sending an HTTP redirect header from the test process.
			if ( defined( 'WP_TESTS_DOMAIN' ) ) {
				return;
			}
			wp_safe_redirect( admin_url( 'admin.php?page=seovault-ai-general&tab=' . rawurlencode( $tab ) ) );
			exit;
		}

		// Legacy: keep individual handlers for backward compatibility (e.g. LLMs tab on separate page).
		if ( isset( $_POST['seovaultseo_save_breadcrumbs'] ) && check_admin_referer( 'seovaultseo_save_breadcrumbs_action' ) ) {
			Logger::log( 'Admin action: legacy save_breadcrumbs' );
			self::save_breadcrumbs( 'seovaultseo_save_breadcrumbs_action' );
			add_settings_error( 'seovault_seo_settings', 'breadcrumbs_saved', __( 'Breadcrumbs settings saved.', 'seovault-ai' ), 'updated' );
		}

		if ( isset( $_POST['seovaultseo_save_404_settings'] ) && check_admin_referer( 'seovaultseo_save_404_settings_action' ) ) {
			Logger::log( 'Admin action: legacy save_404_monitor' );
			self::save_404_monitor( 'seovaultseo_save_404_settings_action' );
			add_settings_error( 'seovault_seo_settings', 'monitor404_saved', __( '404 Monitor settings saved.', 'seovault-ai' ), 'updated' );
		}

		if ( isset( $_POST['seovaultseo_save_image_seo'] ) && check_admin_referer( 'seovaultseo_save_image_seo_action' ) ) {
			Logger::log( 'Admin action: legacy save_image_seo' );
			self::save_image_seo( 'seovaultseo_save_image_seo_action' );
			add_settings_error( 'seovault_seo_settings', 'image_seo_saved', __( 'Image SEO settings saved.', 'seovault-ai' ), 'updated' );
		}

		if ( isset( $_POST['seovaultseo_save_frontend_score'] ) && check_admin_referer( 'seovaultseo_save_frontend_score_action' ) ) {
			Logger::log( 'Admin action: legacy save_frontend_score' );
			self::save_frontend_score( 'seovaultseo_save_frontend_score_action' );
			add_settings_error( 'seovault_seo_settings', 'frontend_score_saved', __( 'Frontend Score settings saved.', 'seovault-ai' ), 'updated' );
		}

		if ( isset( $_POST['seovaultseo_save_wc_settings'] ) && check_admin_referer( 'seovaultseo_save_wc_settings_action' ) ) {
			Logger::log( 'Admin action: legacy save_woocommerce' );
			self::save_woocommerce( 'seovaultseo_save_wc_settings_action' );
			add_settings_error( 'seovault_seo_settings', 'wc_settings_saved', __( 'WooCommerce SEO settings saved.', 'seovault-ai' ), 'updated' );
		}

		if ( isset( $_POST['seovaultseo_save_indexnow'] ) && check_admin_referer( 'seovaultseo_save_indexnow_action' ) ) {
			Logger::log( 'Admin action: legacy save_indexnow' );
			self::save_indexnow( 'seovaultseo_save_indexnow_action' );
			add_settings_error( 'seovault_seo_settings', 'indexnow_saved', __( 'IndexNow settings saved.', 'seovault-ai' ), 'updated' );
		}

		if ( isset( $_POST['seovaultseo_indexnow_regenerate_key'] ) && check_admin_referer( 'seovaultseo_indexnow_regenerate_key_action' ) ) {
			Logger::log( 'Admin action: regenerate_indexnow_key (POST)' );
			$api     = \SEOvaultSEO\IndexNow\IndexNowApi::get_instance();
			$new_key = $api->generate_key();
			$written = $api->write_key_file( $new_key );
			if ( $written ) {
				$old_key = Options::get_indexnow_key();
				if ( '' !== $old_key && $old_key !== $new_key ) {
					$api->delete_key_file( $old_key );
				}
				Options::set_indexnow_key( $new_key );
				Logger::log( 'Admin action: indexnow key regenerated successfully' );
				add_settings_error( 'seovault_seo_settings', 'indexnow_key_regenerated', __( 'IndexNow API key regenerated.', 'seovault-ai' ), 'updated' );
			} else {
				Logger::error( 'Admin action: indexnow key file write failed' );
				add_settings_error( 'seovault_seo_settings', 'indexnow_key_failed', __( 'Could not write IndexNow key file to site root.', 'seovault-ai' ), 'error' );
			}
		}
	}

	/**
	 * Save breadcrumbs settings from POST data.
	 *
	 * @param string $nonce_action Verified form nonce action.
	 * @return void
	 */
	private static function save_breadcrumbs( $nonce_action ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		check_admin_referer( $nonce_action );
		$request = $_POST;

		Logger::log( 'Settings: save_breadcrumbs entered' );
		Options::set_breadcrumbs_enabled( isset( $_POST['breadcrumbs_enabled'] ) );
		Options::set_breadcrumbs_separator( self::post_text( $request, 'breadcrumbs_separator', '&raquo;' ) );
		Options::set_breadcrumbs_home_label( self::post_text( $request, 'breadcrumbs_home_label', 'Home' ) );
		Options::set_breadcrumbs_prefix( self::post_text( $request, 'breadcrumbs_prefix' ) );
		Options::set_breadcrumbs_404_label( self::post_text( $request, 'breadcrumbs_404_label' ) );
		Options::set_breadcrumbs_search_format( self::post_text( $request, 'breadcrumbs_search_format' ) );
		Options::set_breadcrumbs_archive_format( self::post_text( $request, 'breadcrumbs_archive_format' ) );
		Options::set_breadcrumbs_show_home( isset( $_POST['breadcrumbs_show_home'] ) );
		Options::set_breadcrumbs_remove_post_title( isset( $_POST['breadcrumbs_remove_post_title'] ) );
		Options::set_breadcrumbs_show_ancestors( isset( $_POST['breadcrumbs_show_ancestors'] ) );
		Options::set_breadcrumbs_hide_taxonomy_name( isset( $_POST['breadcrumbs_hide_taxonomy_name'] ) );
		Options::set_breadcrumbs_show_blog( isset( $_POST['breadcrumbs_show_blog'] ) );
		Logger::log( 'Settings: save_breadcrumbs success' );
	}

	/**
	 * Save 404 Monitor settings from POST data.
	 *
	 * @param string $nonce_action Verified form nonce action.
	 * @return void
	 */
	private static function save_404_monitor( $nonce_action ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		check_admin_referer( $nonce_action );
		$request = $_POST;

		Logger::log( 'Settings: save_404_monitor entered' );
		Options::set_monitor404_enabled( isset( $_POST['monitor404_enabled'] ) );
		Options::set_monitor404_mode( self::post_text( $request, 'monitor404_mode', 'simple' ) );
		Options::set_monitor404_limit( absint( self::post_text( $request, 'monitor404_limit', '1000' ) ) );
		Options::set_monitor404_ignore_query_params( isset( $_POST['monitor404_ignore_query_params'] ) );
		NotFoundAdminSettings::save_fallback_settings(
			array(
				'notfound_fallback_mode'          => self::post_text( $request, 'notfound_fallback_mode', Options::get_notfound_fallback_mode() ),
				'notfound_fallback_status'        => self::post_text( $request, 'notfound_fallback_status', (string) Options::get_notfound_fallback_status() ),
				'notfound_fallback_custom_url'    => self::post_text( $request, 'notfound_fallback_custom_url', Options::get_notfound_fallback_custom_url() ),
				'notfound_fallback_custom_page_id' => self::post_text( $request, 'notfound_fallback_custom_page_id', (string) Options::get_notfound_fallback_custom_page_id() ),
			)
		);
		NotFoundAdminSettings::save_recovery_settings(
			array(
				'notfound_recovery_enabled'          => isset( $_POST['notfound_recovery_enabled'] ),
				'notfound_recovery_headline'         => self::post_text( $request, 'notfound_recovery_headline', '' ),
				'notfound_recovery_message'          => self::post_textarea( $request, 'notfound_recovery_message', '' ),
				'notfound_recovery_show_search'      => isset( $_POST['notfound_recovery_show_search'] ),
				'notfound_recovery_show_home_cta'    => isset( $_POST['notfound_recovery_show_home_cta'] ),
				'notfound_recovery_cta_label'        => self::post_text( $request, 'notfound_recovery_cta_label', '' ),
				'notfound_recovery_show_recent'      => isset( $_POST['notfound_recovery_show_recent'] ),
				'notfound_recovery_recent_count'     => self::post_text( $request, 'notfound_recovery_recent_count', '3' ),
				'notfound_recovery_use_theme_colors' => isset( $_POST['notfound_recovery_use_theme_colors'] ),
				'notfound_recovery_accent'           => self::post_text( $request, 'notfound_recovery_accent', '#1a1a1a' ),
				'notfound_recovery_content_mode'     => self::post_text( $request, 'notfound_recovery_content_mode', 'builtin' ),
				'notfound_recovery_page_id'          => self::post_text( $request, 'notfound_recovery_page_id', '0' ),
			)
		);

		$excludes = array();
		if ( isset( $_POST['monitor404_excludes'] ) && is_array( $_POST['monitor404_excludes'] ) ) {
			$raw_excludes = map_deep( wp_unslash( $_POST['monitor404_excludes'] ), 'sanitize_text_field' );
			foreach ( $raw_excludes as $rule ) {
				$pattern = trim( sanitize_text_field( $rule['pattern'] ?? '' ) );
				if ( '' !== $pattern ) {
					$excludes[] = array(
						'pattern'    => $pattern,
						'comparison' => sanitize_text_field( $rule['comparison'] ?? 'contains' ),
					);
				}
			}
		}
		Options::set_monitor404_excludes( $excludes );
		Logger::log(
			'Settings: save_404_monitor success',
			array(
				'enabled' => isset( $_POST['monitor404_enabled'] ),
				'mode'    => self::post_text( $request, 'monitor404_mode', 'simple' ),
			)
		);
	}

	/**
	 * Save Image SEO settings from POST data.
	 *
	 * @param string $nonce_action Verified form nonce action.
	 * @return void
	 */
	private static function save_image_seo( $nonce_action ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		check_admin_referer( $nonce_action );
		$request = $_POST;

		Logger::log( 'Settings: save_image_seo entered' );
		Options::set_image_seo_enabled( isset( $_POST['image_seo_enabled'] ) );
		Options::set_image_seo_alt_format( self::post_text( $request, 'image_seo_alt_format' ) );
		Options::set_image_seo_title_format( self::post_text( $request, 'image_seo_title_format' ) );
		Options::set_image_seo_override_empty_alt( isset( $_POST['image_seo_override_empty_alt'] ) );
		Options::set_image_seo_webapp_priority( isset( $_POST['image_seo_webapp_priority'] ) );
		Logger::log( 'Settings: save_image_seo success', array( 'enabled' => isset( $_POST['image_seo_enabled'] ) ) );
	}

	/**
	 * Save Frontend Score settings from POST data.
	 *
	 * @param string $nonce_action Verified form nonce action.
	 * @return void
	 */
	private static function save_frontend_score( $nonce_action ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		check_admin_referer( $nonce_action );
		$request = $_POST;

		Logger::log( 'Settings: save_frontend_score entered' );
		Options::set_frontend_score_enabled( isset( $_POST['frontend_score_enabled'] ) );
		Options::set_frontend_score_position( Sanitizer::sanitize_frontend_score_position( self::post_text( $request, 'frontend_score_position', 'bottom' ) ) );

		$post_types = isset( $_POST['frontend_score_post_types'] ) && is_array( $_POST['frontend_score_post_types'] )
			? Sanitizer::sanitize_frontend_score_post_types( map_deep( wp_unslash( $_POST['frontend_score_post_types'] ), 'sanitize_key' ) )
			: array();
		Options::set_frontend_score_post_types( $post_types );
		Logger::log(
			'Settings: save_frontend_score success',
			array(
				'enabled'         => isset( $_POST['frontend_score_enabled'] ),
				'post_type_count' => count( $post_types ),
			)
		);
	}

	/**
	 * Save WooCommerce settings from POST data.
	 *
	 * @param string $nonce_action Verified form nonce action.
	 * @return void
	 */
	private static function save_woocommerce( $nonce_action ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		check_admin_referer( $nonce_action );

		Logger::log( 'Settings: save_woocommerce entered' );
		$old_product_base  = Options::is_wc_remove_product_base();
		$old_category_base = Options::is_wc_remove_category_base();
		$old_parent_slugs  = Options::is_wc_remove_parent_slugs();

		Options::set_wc_remove_product_base( isset( $_POST['wc_remove_product_base'] ) );
		Options::set_wc_remove_category_base( isset( $_POST['wc_remove_category_base'] ) );
		Options::set_wc_remove_parent_slugs( isset( $_POST['wc_remove_parent_slugs'] ) );
		Options::set_wc_remove_generator( isset( $_POST['wc_remove_generator'] ) );
		Options::set_wc_remove_shop_schema( isset( $_POST['wc_remove_shop_schema'] ) );

		$brand_taxonomy = sanitize_text_field( wp_unslash( $_POST['wc_brand_taxonomy'] ?? '' ) );
		Options::set_wc_brand_taxonomy( $brand_taxonomy );

		// Enhanced WooCommerce Pro settings.
		$gtin_type = sanitize_text_field( wp_unslash( $_POST['wc_gtin_type'] ?? 'gtin8' ) );
		Options::set_wc_gtin_type( $gtin_type );

		$gtin_label = sanitize_text_field( wp_unslash( $_POST['wc_gtin_label'] ?? '' ) );
		Options::set_wc_gtin_label( $gtin_label );

		Options::set_wc_show_gtin_frontend( isset( $_POST['wc_show_gtin_frontend'] ) );
		Options::set_wc_noindex_hidden_products( isset( $_POST['wc_noindex_hidden_products'] ) );
		Options::set_wc_additional_schema( isset( $_POST['wc_additional_schema'] ) );

		$custom_brand = sanitize_text_field( wp_unslash( $_POST['wc_custom_brand'] ?? '' ) );
		Options::set_wc_custom_brand( $custom_brand );

		$multicurrency = sanitize_text_field( wp_unslash( $_POST['wc_multicurrency_currencies'] ?? '' ) );
		Options::set_wc_multicurrency_currencies( $multicurrency );

		Options::set_wc_per_product_identifiers( isset( $_POST['wc_per_product_identifiers'] ) );
		Options::set_wc_import_export_enabled( isset( $_POST['wc_import_export'] ) );

		// Enhanced schema settings.
		Options::set_wc_enhanced_schema( isset( $_POST['wc_enhanced_schema'] ) );
		Options::set_wc_remove_breadcrumb_schema( isset( $_POST['wc_remove_breadcrumb_schema'] ) );
		Options::set_wc_schema_variation_images( isset( $_POST['wc_schema_variation_images'] ) );
		Options::set_wc_schema_product_attributes( isset( $_POST['wc_schema_product_attributes'] ) );

		$return_policy_page = absint( wp_unslash( $_POST['wc_schema_return_policy_page'] ?? 0 ) );
		Options::set_wc_schema_return_policy_page( $return_policy_page );

		// Flush rewrite rules if permalink settings changed.
		$new_product_base  = Options::is_wc_remove_product_base();
		$new_category_base = Options::is_wc_remove_category_base();
		$new_parent_slugs  = Options::is_wc_remove_parent_slugs();

		if ( $old_product_base !== $new_product_base || $old_category_base !== $new_category_base || $old_parent_slugs !== $new_parent_slugs ) {
			flush_rewrite_rules( false );
			Logger::log( 'Settings: save_woocommerce rewrite rules flushed' );
		}

		Logger::log( 'Settings: save_woocommerce success' );
	}

	/**
	 * Save IndexNow settings from POST data.
	 *
	 * @param string $nonce_action Verified form nonce action.
	 * @return void
	 */
	private static function save_indexnow( $nonce_action ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		check_admin_referer( $nonce_action );

		Logger::log( 'Settings: save_indexnow entered' );
		Options::set_indexnow_enabled( isset( $_POST['indexnow_enabled'] ) );

		$raw_types = isset( $_POST['indexnow_post_types'] )
			? ( is_array( $_POST['indexnow_post_types'] )
				? map_deep( wp_unslash( $_POST['indexnow_post_types'] ), 'sanitize_key' )
				: sanitize_key( wp_unslash( (string) $_POST['indexnow_post_types'] ) ) )
			: array();
		if ( ! is_array( $raw_types ) ) {
			$raw_types = ( '' === $raw_types ) ? array() : array( $raw_types );
		}
		$post_types = Sanitizer::sanitize_indexnow_post_types( $raw_types );
		Options::set_indexnow_post_types( $post_types );
		Logger::log(
			'Settings: save_indexnow success',
			array(
				'enabled'         => isset( $_POST['indexnow_enabled'] ),
				'post_type_count' => count( $post_types ),
			)
		);
	}

	/**
	 * Save Analytics / Tracking Code settings from POST data.
	 *
	 * @param string $nonce_action Verified form nonce action.
	 * @return void
	 */
	private static function save_analytics( $nonce_action ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		check_admin_referer( $nonce_action );
		$request = $_POST;

		Logger::log( 'Settings: save_analytics entered' );
		Options::set_ga_tracking_enabled( isset( $_POST['ga_tracking_enabled'] ) );
		Options::set_ga_measurement_id( Sanitizer::sanitize_ga_measurement_id( self::post_text( $request, 'ga_measurement_id' ) ) );
		Options::set_ga_track_logged_in( isset( $_POST['ga_track_logged_in'] ) );
		Options::set_ga_anonymize_ip( isset( $_POST['ga_anonymize_ip'] ) );
		// The Free nonce and capability checks above cover trusted extensions;
		// each extension remains responsible for sanitizing its own fields.
		// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Hook name is the prefixed public API constant declared in ExtensionApi.
		do_action( \SEOvaultSEO\Extension\ExtensionApi::ACTION_ANALYTICS_SETTINGS_SAVED, $request );
		Logger::log(
			'Settings: save_analytics success',
			array(
				'enabled' => isset( $_POST['ga_tracking_enabled'] ),
				'method'  => 'gtag',
			)
		);
	}

	/**
	 * Save Local SEO settings from POST data.
	 *
	 * @param string $nonce_action Verified form nonce action.
	 * @return void
	 */
	private static function save_local_seo( $nonce_action ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		check_admin_referer( $nonce_action );

		if ( ! isset( $_POST['seovaultseo_local_settings'] ) || ! is_array( $_POST['seovaultseo_local_settings'] ) ) {
			Logger::log( 'Settings: save_local_seo skipped' );
			return;
		}

		if ( ! class_exists( '\SEOvaultSEO\LocalSEO\AdminPage' ) ) {
			Logger::error( 'Settings: save_local_seo unavailable' );
			return;
		}

		Logger::log( 'Settings: save_local_seo entered' );
		$raw_settings = map_deep( wp_unslash( $_POST['seovaultseo_local_settings'] ), 'wp_strip_all_tags' );
		$settings     = \SEOvaultSEO\LocalSEO\AdminPage::sanitize_settings( $raw_settings );
		update_option( 'seovaultseo_local_settings', $settings, false );
		Logger::log( 'Settings: save_local_seo success', array( 'field_count' => count( $settings ) ) );
	}

	/**
	 * Save Robots settings from POST data.
	 *
	 * @param string $nonce_action Verified form nonce action.
	 * @return void
	 */
	private static function save_robots( $nonce_action ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		check_admin_referer( $nonce_action );
		$request = $_POST;

		Logger::log( 'Settings: save_robots entered' );
		Options::set_search_noindex( isset( $_POST['robots_search_noindex'] ) ? 'yes' : 'no' );
		Options::set_date_archives_noindex( isset( $_POST['robots_date_archives_noindex'] ) ? 'yes' : 'no' );
		Options::set_attachment_noindex( isset( $_POST['robots_attachment_noindex'] ) ? 'yes' : 'no' );
		Options::set_post_format_noindex( isset( $_POST['robots_post_format_noindex'] ) ? 'yes' : 'no' );
		Options::set_empty_archives_noindex( isset( $_POST['robots_empty_archives_noindex'] ) ? 'yes' : 'no' );
		Options::set_advanced_query_protection( isset( $_POST['robots_advanced_query_protection'] ) ? 'yes' : 'no' );

		$raw_noindex_types = isset( $_POST['robots_noindex_post_types'] )
			? ( is_array( $_POST['robots_noindex_post_types'] )
				? map_deep( wp_unslash( $_POST['robots_noindex_post_types'] ), 'sanitize_key' )
				: sanitize_key( wp_unslash( (string) $_POST['robots_noindex_post_types'] ) ) )
			: array();
		Options::set_noindex_post_types( Sanitizer::sanitize_noindex_post_types( $raw_noindex_types ) );

		Options::set_robots_max_snippet( Sanitizer::sanitize_snippet_length( self::post_signed_int( $request, 'robots_max_snippet', -1 ) ) );
		Options::set_robots_max_image_preview( self::post_text( $request, 'robots_max_image_preview', 'large' ) );
		Options::set_robots_max_video_preview( Sanitizer::sanitize_video_preview_length( self::post_signed_int( $request, 'robots_max_video_preview', -1 ) ) );

		Options::set_robots_policy( Sanitizer::sanitize_robots_policy( self::post_text( $request, 'robots_policy' ) ) );
		Options::set_robots_custom_content( self::post_textarea( $request, 'robots_custom_content' ) );
		Logger::log( 'Settings: save_robots success', array( 'policy' => self::post_text( $request, 'robots_policy' ) ) );
	}

	/**
	 * AJAX handler for robots.txt preview.
	 *
	 * @return void
	 */
	public static function ajax_robots_preview() {
		Logger::log( 'AJAX robots_preview: entered' );
		check_ajax_referer( 'seovaultseo_robots_preview', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			Logger::error( 'AJAX robots_preview: capability check failed' );
			wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'seovault-ai' ) ), 403 );
		}

		$request        = $_POST;
		$policy         = self::post_text( $request, 'policy' );
		$custom_content = self::post_textarea( $request, 'custom_content' );

		Logger::log( 'AJAX robots_preview: inputs', array( 'policy' => $policy ) );

		if ( class_exists( '\SEOvaultSEO\AiVisibility\Robots' ) ) {
			$result = \SEOvaultSEO\AiVisibility\Robots::preview( $policy, $custom_content );
			Logger::log( 'AJAX robots_preview: success', array( 'policy' => $policy ) );
			wp_send_json_success( $result );
		} else {
			Logger::error( 'AJAX robots_preview: Robots module not available' );
			wp_send_json_error( array( 'message' => __( 'Robots module not available.', 'seovault-ai' ) ) );
		}
	}

	public static function ajax_log_save_all_settings_click() {
		check_ajax_referer( 'seovaultseo_admin', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			Logger::error( 'AJAX save_all_settings click log: capability check failed' );
			wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'seovault-ai' ) ), 403 );
		}

		$active_tab = sanitize_text_field( wp_unslash( $_POST['active_tab'] ?? '' ) );
		Logger::log( 'Admin action: save_all_settings button clicked', array( 'active_tab' => $active_tab ) );
		wp_send_json_success();
	}

	/**
	 * Enqueue assets on the settings page.
	 *
	 * Dashboard::enqueue_assets() owns the shared settings assets. This method
	 * only loads the legacy settings-page behaviors that remain for backward
	 * compatibility.
	 *
	 * @param string $hook Current admin page hook.
	 */
	public static function enqueue_assets( $hook ) {
		if ( false === strpos( (string) $hook, 'seovault-ai' ) ) {
			return;
		}
		wp_enqueue_script(
			'seovaultseo-legacy-settings',
			SEOVAULT_SEO_URL . 'assets/js/legacy-settings.js',
			array( 'jquery' ),
			SEOVAULT_SEO_VERSION,
			true
		);
		wp_localize_script(
			'seovaultseo-legacy-settings',
			'seovaultseo_legacy_settings',
			array(
				'ajax_url'         => admin_url( 'admin-ajax.php' ),
				'import_url'       => rest_url( 'seovaultai/v1/seo/import' ),
				'site_key'         => Options::get_site_key(),
				'indexnow_nonce'   => wp_create_nonce( 'seovaultseo_indexnow_admin' ),
				'thirdparty_nonce' => wp_create_nonce( 'seovaultseo_admin' ),
				'i18n'             => array(
					'enter_url'           => __( 'Please enter at least one URL.', 'seovault-ai' ),
					'submitting'          => __( 'Submitting...', 'seovault-ai' ),
					'submission_failed'   => __( 'Submission failed.', 'seovault-ai' ),
					'site_key_missing'    => __( 'Site key not found. Please ensure the plugin is properly activated.', 'seovault-ai' ),
					'scan_failed'         => __( 'Scan failed.', 'seovault-ai' ),
					'scan_request_failed' => __( 'Scan request failed.', 'seovault-ai' ),
					'posts_found'         => __( 'Posts with Yoast/Rank Math blocks:', 'seovault-ai' ),
					'convertible'         => __( 'Convertible blocks (sampled):', 'seovault-ai' ),
					'blocked'             => __( 'Blocked blocks (sampled):', 'seovault-ai' ),
					'by_source'           => __( 'By source:', 'seovault-ai' ),
					'confirm_convert'     => __( 'Convert all found Yoast/Rank Math FAQ and How-to blocks to SEOvault blocks? A backup is saved per post.', 'seovault-ai' ),
					'batch_failed'        => __( 'Batch conversion failed.', 'seovault-ai' ),
				),
			)
		);
	}

	/**
	 * Add settings menu page
	 */
	public static function add_menu_page() {
		add_menu_page(
			__( 'SEOvault AI', 'seovault-ai' ),
			__( 'SEOvault AI', 'seovault-ai' ),
			'manage_options',
			'seovault-ai',
			array( __CLASS__, 'render_settings_page' ),
			'dashicons-search',
			100
		);
	}

	/**
	 * Register settings
	 */
	public static function register_settings() {
		register_setting(
			'seovault_seo_settings',
			'seovaultseo_output_mode',
			array(
				'type'              => 'string',
				'sanitize_callback' => array( Sanitizer::class, 'sanitize_output_mode' ),
				'default'           => 'auto',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_home_title',
			array(
				'type'              => 'string',
				'sanitize_callback' => array( Sanitizer::class, 'sanitize_title' ),
				'default'           => '',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_home_description',
			array(
				'type'              => 'string',
				'sanitize_callback' => array( Sanitizer::class, 'sanitize_description' ),
				'default'           => '',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_title_template',
			array(
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'default'           => '',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_title_separator',
			array(
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'default'           => '-',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_templates_enabled',
			array(
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'default'           => 'yes',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_description_template',
			array(
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_textarea_field',
				'default'           => '',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_default_social_image',
			array(
				'type'              => 'string',
				'sanitize_callback' => 'esc_url_raw',
				'default'           => '',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_facebook_page_url',
			array(
				'type'              => 'string',
				'sanitize_callback' => 'esc_url_raw',
				'default'           => '',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_facebook_author_fallback',
			array(
				'type'              => 'string',
				'sanitize_callback' => 'esc_url_raw',
				'default'           => '',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_twitter_card_type',
			array(
				'type'              => 'string',
				'sanitize_callback' => array( Sanitizer::class, 'sanitize_twitter_card_type' ),
				'default'           => 'summary_large_image',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_twitter_profile_username',
			array(
				'type'              => 'string',
				'sanitize_callback' => 'esc_url_raw',
				'default'           => '',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_twitter_profile_fallback',
			array(
				'type'              => 'string',
				'sanitize_callback' => 'esc_url_raw',
				'default'           => '',
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_social_profiles',
			array(
				'type'              => 'array',
				'sanitize_callback' => array( Sanitizer::class, 'sanitize_social_profiles' ),
				'default'           => array(),
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_default_author',
			array(
				'type'              => 'integer',
				'sanitize_callback' => array( Sanitizer::class, 'sanitize_id' ),
				'default'           => 0,
			)
		);

		register_setting(
			'seovault_seo_settings',
			'seovaultseo_default_category',
			array(
				'type'              => 'integer',
				'sanitize_callback' => array( Sanitizer::class, 'sanitize_id' ),
				'default'           => 0,
			)
		);
	}

	/**
	 * Render settings page
	 */
	public static function render_settings_page() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'seovault-ai' ) );
		}

		$status = Detector::get_status();

		?>
		<div class="wrap seovaultseo-legacy-settings">
			<h1><?php esc_html_e( 'SEOvault AI Settings', 'seovault-ai' ); ?></h1>

			<?php settings_errors( 'seovault_seo_settings' ); ?>

			<h2 class="nav-tab-wrapper">
				<a href="#connection" class="nav-tab nav-tab-active"><?php esc_html_e( 'Connection', 'seovault-ai' ); ?></a>
				<a href="#general" class="nav-tab"><?php esc_html_e( 'General', 'seovault-ai' ); ?></a>
				<a href="#homepage" class="nav-tab"><?php esc_html_e( 'Homepage', 'seovault-ai' ); ?></a>
				<a href="#templates" class="nav-tab"><?php esc_html_e( 'Templates', 'seovault-ai' ); ?></a>
				<a href="#social" class="nav-tab"><?php esc_html_e( 'Social', 'seovault-ai' ); ?></a>
				<a href="#indexnow" class="nav-tab"><?php esc_html_e( 'IndexNow', 'seovault-ai' ); ?></a>
				<a href="#import" class="nav-tab"><?php esc_html_e( 'Import', 'seovault-ai' ); ?></a>
				<a href="#status" class="nav-tab"><?php esc_html_e( 'Status', 'seovault-ai' ); ?></a>
			</h2>

			<form method="post" action="options.php">
				<?php settings_fields( 'seovault_seo_settings' ); ?>

				<div id="connection" class="seovault-ai-tab-content">
					<?php
					$site_key     = Options::get_site_key();
					$connected_at = Options::get_connected_at();
					$is_connected = Options::is_connection_verified() && ! empty( $connected_at );
					$rest_url     = rest_url( 'seovaultai/v1/verify' );
					$masked_key   = '';
					if ( strlen( $site_key ) > 10 ) {
						$masked_key = substr( $site_key, 0, 6 ) . str_repeat( '•', strlen( $site_key ) - 10 ) . substr( $site_key, -4 );
					}
					?>
					<table class="form-table">
						<tr>
							<th scope="row"><?php esc_html_e( 'Status', 'seovault-ai' ); ?></th>
							<td>
								<?php if ( $is_connected ) : ?>
									<span style="color: #00a32a; font-weight: 600;">● <?php esc_html_e( 'Connected', 'seovault-ai' ); ?></span>
									<p class="description"><?php echo esc_html( sprintf( /* translators: %s: verification timestamp */ __( 'Last verified: %s UTC', 'seovault-ai' ), $connected_at ) ); ?></p>
								<?php else : ?>
									<span style="color: #b5891a; font-weight: 600;">● <?php esc_html_e( 'Waiting for connection', 'seovault-ai' ); ?></span>
									<p class="description"><?php esc_html_e( 'Copy the site key below and add this site in the SEOvault AI web app.', 'seovault-ai' ); ?></p>
								<?php endif; ?>
							</td>
						</tr>
						<tr>
							<th scope="row"><?php esc_html_e( 'Site Key', 'seovault-ai' ); ?></th>
							<td>
								<code style="font-size: 13px; padding: 4px 8px; background: #f0f0f1; border-radius: 3px;"><?php echo esc_html( $masked_key ); ?></code>
								<p class="description"><?php esc_html_e( 'This key authenticates requests from the SEOvault AI web app.', 'seovault-ai' ); ?></p>
								<details style="margin-top: 8px;">
									<summary style="cursor: pointer; color: #0D9488;"><?php esc_html_e( 'Show full key', 'seovault-ai' ); ?></summary>
									<input type="text" value="<?php echo esc_attr( $site_key ); ?>" readonly class="seovaultseo-select-on-click" style="width: 100%; max-width: 400px; margin-top: 6px; font-family: monospace; font-size: 12px;" />
								</details>
							</td>
						</tr>
						<tr>
							<th scope="row"><?php esc_html_e( 'API Endpoint', 'seovault-ai' ); ?></th>
							<td>
								<code style="font-size: 12px; padding: 3px 6px; background: #f0f0f1; border-radius: 3px;"><?php echo esc_html( $rest_url ); ?></code>
								<p class="description"><?php esc_html_e( 'The web app uses this URL to communicate with your site.', 'seovault-ai' ); ?></p>
							</td>
						</tr>
						<tr>
							<th scope="row"><?php esc_html_e( 'Plugin Version', 'seovault-ai' ); ?></th>
							<td><?php echo esc_html( SEOVAULT_SEO_VERSION ); ?></td>
						</tr>
					</table>

					<hr>

					<h3><?php esc_html_e( 'AI-readable llms.txt', 'seovault-ai' ); ?></h3>
					<?php $llms_enabled = Options::is_llms_enabled(); ?>
					<table class="form-table">
						<tr>
							<th scope="row"><?php esc_html_e( 'Serve AI-readable llms.txt files from SEOvault AI', 'seovault-ai' ); ?></th>
							<td>
								<div class="seovaultseo-toggle-row">
									<label class="seovaultseo-switch">
										<input type="checkbox" name="seovaultseo_llms_enabled" id="seovaultseo_llms_enabled" value="1" <?php checked( $llms_enabled ); ?> />
										<span class="seovaultseo-slider"></span>
									</label>
									<label for="seovaultseo_llms_enabled" class="seovaultseo-toggle-label"><?php esc_html_e( 'Allow SEOvault AI to manage llms.txt', 'seovault-ai' ); ?></label>
								</div>
								<p class="description"><?php esc_html_e( 'When enabled, the plugin serves virtual /llms.txt and /llms-full.txt routes. The content of these files is generated on the SEOvault AI web app; go to the AI Visibility page in your web app dashboard, generate the llms.txt content, and publish it to your site. The plugin will then serve it at /llms.txt and /llms-full.txt automatically.', 'seovault-ai' ); ?></p>
							</td>
						</tr>
					</table>

					<hr>

					<h3><?php esc_html_e( 'Security', 'seovault-ai' ); ?></h3>
					<p><?php esc_html_e( 'If you suspect your site key has been compromised, regenerate it below. You will need to update the key in your web app settings.', 'seovault-ai' ); ?></p>
					<p>
						<button type="submit" form="seovaultseo-legacy-regenerate-site-key-form" class="button button-secondary" data-seovaultseo-confirm="<?php echo esc_attr__( 'This will disconnect any apps using the current key. Continue?', 'seovault-ai' ); ?>">
							<?php esc_html_e( 'Regenerate Site Key', 'seovault-ai' ); ?>
						</button>
					</p>
				</div>

				<div id="general" class="seovault-ai-tab-content" style="display: none;">
					<table class="form-table">
						<tr>
							<th scope="row">
								<label for="seovaultseo_output_mode">
									<?php esc_html_e( 'Output Mode', 'seovault-ai' ); ?>
								</label>
							</th>
							<td>
								<select name="seovaultseo_output_mode" id="seovaultseo_output_mode" class="seovaultseo-select">
									<option value="auto" <?php selected( Options::get_output_mode(), 'auto' ); ?>>
										<?php esc_html_e( 'Autopilot — Set & Run (Recommended)', 'seovault-ai' ); ?>
									</option>
									<option value="manual" <?php selected( Options::get_output_mode(), 'manual' ); ?>>
										<?php esc_html_e( 'Manual — You control every field', 'seovault-ai' ); ?>
									</option>
									<option value="disabled" <?php selected( Options::get_output_mode(), 'disabled' ); ?>>
										<?php esc_html_e( 'Disabled — Pause SEOvault AI', 'seovault-ai' ); ?>
									</option>
								</select>
								<p class="description">
									<?php esc_html_e( 'Autopilot: SEOvault AI automatically generates all SEO meta tags, schema, sitemaps, and more. Manual: No auto-generation — you fill in every field yourself. Disabled: SEOvault AI goes dormant, no frontend output at all.', 'seovault-ai' ); ?>
								</p>
							</td>
						</tr>
					</table>
				</div>

				<div id="homepage" class="seovault-ai-tab-content" style="display: none;">
					<p class="description" style="margin-bottom:16px;"><?php esc_html_e( 'Set a custom title and description for your homepage to control how it appears in search engine results.', 'seovault-ai' ); ?></p>

					<div class="seovaultseo-homepage-field">
						<div class="seovaultseo-homepage-field-header">
							<label for="seovaultseo_home_title" class="seovaultseo-homepage-label"><?php esc_html_e( 'Search Engine Title', 'seovault-ai' ); ?></label>
							<span class="seovaultseo-char-counter" data-target="seovaultseo_home_title" data-max="60">
								<span class="seovaultseo-char-count">0</span> / 60
							</span>
						</div>
						<div class="seovaultseo-homepage-input-wrap">
							<input type="text" name="seovaultseo_home_title" id="seovaultseo_home_title" value="<?php echo esc_attr( Options::get_home_title() ); ?>" class="regular-text seovaultseo-var-target seovaultseo-homepage-title-input" placeholder="<?php echo esc_attr( Options::get_home_title() ? '' : wp_strip_all_tags( get_bloginfo( 'name' ) ) ); ?>" />
							<button type="button" class="button button-secondary seovaultseo-var-picker-btn" data-target="seovaultseo_home_title" title="<?php esc_attr_e( 'Insert variable', 'seovault-ai' ); ?>"><span class="dashicons dashicons-arrow-down-alt2"></span></button>
						</div>
						<p class="description"><?php esc_html_e( 'Click the dropdown button to insert variables. Leave empty to use the site name.', 'seovault-ai' ); ?></p>
					</div>

					<div class="seovaultseo-homepage-field">
						<div class="seovaultseo-homepage-field-header">
							<label for="seovaultseo_home_description" class="seovaultseo-homepage-label"><?php esc_html_e( 'Search Engine Description', 'seovault-ai' ); ?></label>
							<span class="seovaultseo-char-counter" data-target="seovaultseo_home_description" data-max="160">
								<span class="seovaultseo-char-count">0</span> / 160
							</span>
						</div>
						<div class="seovaultseo-homepage-input-wrap">
							<textarea name="seovaultseo_home_description" id="seovaultseo_home_description" rows="3" class="large-text seovaultseo-var-target seovaultseo-homepage-desc-input" placeholder="<?php echo esc_attr( Options::get_home_description() ? '' : wp_strip_all_tags( get_bloginfo( 'description' ) ) ); ?>"><?php echo esc_textarea( Options::get_home_description() ); ?></textarea>
						</div>
						<p class="description"><?php esc_html_e( 'Leave empty to use the site description.', 'seovault-ai' ); ?></p>
					</div>

					<div class="seovaultseo-google-preview-wrap">
						<h3 class="seovaultseo-google-preview-title"><?php esc_html_e( 'Search Engine Preview', 'seovault-ai' ); ?></h3>
						<div class="seovaultseo-google-preview" id="seovaultseo-google-preview">
							<div class="seovaultseo-google-preview-site">
								<?php if ( get_site_icon_url( 32 ) ) : ?>
									<img src="<?php echo esc_url( get_site_icon_url( 32 ) ); ?>" alt="" class="seovaultseo-google-preview-favicon" />
								<?php else : ?>
									<span class="dashicons dashicons-search seovaultseo-google-preview-favicon-icon"></span>
								<?php endif; ?>
								<div class="seovaultseo-google-preview-site-info">
									<span class="seovaultseo-google-preview-site-name"><?php echo esc_html( wp_strip_all_tags( get_bloginfo( 'name' ) ) ); ?></span>
									<span class="seovaultseo-google-preview-url"><?php echo esc_html( wp_parse_url( home_url(), PHP_URL_HOST ) ); ?></span>
								</div>
							</div>
							<p class="seovaultseo-google-preview-page-title" id="seovaultseo-google-preview-title-text"><?php echo esc_html( Options::get_home_title() ? Options::get_home_title() : wp_strip_all_tags( get_bloginfo( 'name' ) ) ); ?></p>
							<p class="seovaultseo-google-preview-page-desc" id="seovaultseo-google-preview-desc-text"><?php echo esc_html( Options::get_home_description() ? Options::get_home_description() : wp_strip_all_tags( get_bloginfo( 'description' ) ) ); ?></p>
						</div>
					</div>
				</div>

				<div id="templates" class="seovault-ai-tab-content" style="display: none;">
					<div class="seovaultseo-templates-layout">
						<div class="seovaultseo-templates-form">
							<table class="form-table">
								<tr>
									<th scope="row">
										<?php esc_html_e( 'Enable Templates', 'seovault-ai' ); ?>
									</th>
									<td>
										<label>
											<input type="checkbox" name="seovaultseo_templates_enabled" id="seovaultseo_templates_enabled" value="yes" <?php checked( Options::is_templates_enabled() ); ?> />
											<?php esc_html_e( 'Apply title and description templates automatically', 'seovault-ai' ); ?>
										</label>
										<p class="description">
											<?php esc_html_e( 'When enabled, posts without a custom SEO title or description will use these templates. Disable to use raw post titles and auto-generated descriptions.', 'seovault-ai' ); ?>
										</p>
									</td>
								</tr>
								<tr id="seovaultseo-template-row-separator">
									<th scope="row">
										<label for="seovaultseo_title_separator">
											<?php esc_html_e( 'Title Separator', 'seovault-ai' ); ?>
										</label>
									</th>
									<td>
										<select name="seovaultseo_title_separator" id="seovaultseo_title_separator" class="seovaultseo-select">
											<?php
											$current_sep = Options::get_title_separator();
											$separators  = array(
												'-' => __( '— (Dash)', 'seovault-ai' ),
												'|' => __( '| (Pipe)', 'seovault-ai' ),
												'·' => __( '· (Middot)', 'seovault-ai' ),
												'»' => __( '» (Guillemet)', 'seovault-ai' ),
												':' => __( ': (Colon)', 'seovault-ai' ),
												'/' => __( '/ (Slash)', 'seovault-ai' ),
												'–' => __( '– (En-dash)', 'seovault-ai' ),
												'—' => __( '— (Em-dash)', 'seovault-ai' ),
												'•' => __( '• (Bullet)', 'seovault-ai' ),
											);
											foreach ( $separators as $char => $label ) {
												printf(
													'<option value="%s"%s>%s</option>',
													esc_attr( $char ),
													selected( $current_sep, $char, false ),
													esc_html( $label )
												);
											}
											?>
										</select>
										<p class="description">
											<?php esc_html_e( 'Character used by the %%sep%% variable in templates.', 'seovault-ai' ); ?>
										</p>
									</td>
								</tr>
								<tr id="seovaultseo-template-row-title">
									<th scope="row">
										<label for="seovaultseo_title_template">
											<?php esc_html_e( 'Title Template', 'seovault-ai' ); ?>
										</label>
									</th>
									<td>
										<input
											type="text"
											name="seovaultseo_title_template"
											id="seovaultseo_title_template"
											value="<?php echo esc_attr( Options::get_title_template() ); ?>"
											class="regular-text seovaultseo-var-target"
											placeholder="%title% %sep% %sitename%"
										/>
										<p class="description">
											<?php esc_html_e( 'Click a variable on the right to insert it. Default: %%title%% %%sep%% %%sitename%%', 'seovault-ai' ); ?>
										</p>
									</td>
								</tr>
								<tr id="seovaultseo-template-row-description">
									<th scope="row">
										<label for="seovaultseo_description_template">
											<?php esc_html_e( 'Description Template', 'seovault-ai' ); ?>
										</label>
									</th>
									<td>
										<textarea
											name="seovaultseo_description_template"
											id="seovaultseo_description_template"
											rows="5"
											class="large-text seovaultseo-var-target"
											placeholder="%excerpt%"
										><?php echo esc_textarea( Options::get_description_template() ); ?></textarea>
										<p class="description">
											<?php esc_html_e( 'Click a variable on the right to insert it. Default: %%excerpt%%', 'seovault-ai' ); ?>
										</p>
									</td>
								</tr>
							</table>
						</div>

						<div class="seovaultseo-var-panel" id="seovaultseo-var-panel">
							<div class="seovaultseo-var-panel-header">
								<h3><?php esc_html_e( 'Variables', 'seovault-ai' ); ?></h3>
								<input type="text" class="seovaultseo-var-panel-search" id="seovaultseo-var-panel-search" placeholder="<?php esc_attr_e( 'Search variables…', 'seovault-ai' ); ?>" />
							</div>
							<div class="seovaultseo-var-panel-body" id="seovaultseo-var-panel-body">
								<p class="seovaultseo-var-panel-hint"><?php esc_html_e( 'Click a field on the left, then click + to insert.', 'seovault-ai' ); ?></p>
								<div class="seovaultseo-var-panel-list" id="seovaultseo-var-panel-list"></div>
							</div>
						</div>
					</div>
				</div>

				<div id="social" class="seovault-ai-tab-content" style="display: none;">
					<table class="form-table">
						<tr>
							<th scope="row">
								<label for="seovaultseo_default_social_image">
									<?php esc_html_e( 'Default Social Image', 'seovault-ai' ); ?>
								</label>
							</th>
							<td>
								<input
									type="url"
									name="seovaultseo_default_social_image"
									id="seovaultseo_default_social_image"
									value="<?php echo esc_attr( Options::get_default_social_image() ); ?>"
									class="regular-text"
								/>
								<p class="description">
									<?php esc_html_e( 'URL to a default image for Open Graph and Twitter cards when no featured image is set.', 'seovault-ai' ); ?>
								</p>
							</td>
						</tr>
					</table>
				</div>

				<div id="indexnow" class="seovault-ai-tab-content" style="display: none;">
					<?php
					$indexnow_key      = Options::get_indexnow_key();
					$indexnow_enabled  = Options::is_indexnow_enabled();
					$indexnow_types    = Options::get_indexnow_post_types();
					$public_post_types = get_post_types( array( 'public' => true ), 'objects' );
					$api               = \SEOvaultSEO\IndexNow\IndexNowApi::get_instance();
					$indexnow_log      = $api->get_log();
					$key_file_exists   = '' !== $indexnow_key && file_exists( $api->get_key_file_path( $indexnow_key ) );
					?>

					<h3><?php esc_html_e( 'IndexNow — Instant Indexing', 'seovault-ai' ); ?></h3>
					<p class="description">
						<?php esc_html_e( 'IndexNow notifies search engines (Bing, Yandex, Seznam, and more) when your content is published or updated, so they can crawl and index it faster.', 'seovault-ai' ); ?>
					</p>

					<table class="form-table">
						<tr>
							<th scope="row"><?php esc_html_e( 'API Key Status', 'seovault-ai' ); ?></th>
							<td>
								<?php if ( '' !== $indexnow_key ) : ?>
									<span style="color: #00a32a; font-weight: 600;">● <?php esc_html_e( 'Key configured', 'seovault-ai' ); ?></span>
									<?php if ( $key_file_exists ) : ?>
										<span style="color: #00a32a; margin-left: 8px;">● <?php esc_html_e( 'Key file accessible', 'seovault-ai' ); ?></span>
									<?php else : ?>
										<span style="color: #d63638; margin-left: 8px;">● <?php esc_html_e( 'Key file missing', 'seovault-ai' ); ?></span>
									<?php endif; ?>
									<p class="description">
										<code style="font-size: 12px; padding: 2px 6px; background: #f0f0f1; border-radius: 3px;"><?php echo esc_html( $indexnow_key ); ?></code>
										<br><?php esc_html_e( 'Key location:', 'seovault-ai' ); ?>
										<code style="font-size: 11px; padding: 2px 6px; background: #f0f0f1; border-radius: 3px;"><?php echo esc_url( trailingslashit( home_url() ) . $indexnow_key . '.txt' ); ?></code>
									</p>
								<?php else : ?>
									<span style="color: #b5891a; font-weight: 600;">● <?php esc_html_e( 'No API key set', 'seovault-ai' ); ?></span>
									<p class="description"><?php esc_html_e( 'A key will be generated automatically when you save settings below.', 'seovault-ai' ); ?></p>
								<?php endif; ?>
							</td>
						</tr>
						<tr>
							<th scope="row">
								<label for="indexnow_enabled">
									<?php esc_html_e( 'Auto-Submit on Publish', 'seovault-ai' ); ?>
								</label>
							</th>
							<td>
								<label>
									<input type="checkbox" name="indexnow_enabled" id="indexnow_enabled" value="1" <?php checked( $indexnow_enabled ); ?> />
									<?php esc_html_e( 'Automatically submit URLs to IndexNow when posts are published or updated.', 'seovault-ai' ); ?>
								</label>
							</td>
						</tr>
						<tr>
							<th scope="row"><?php esc_html_e( 'Post Types to Auto-Submit', 'seovault-ai' ); ?></th>
							<td>
								<?php foreach ( $public_post_types as $pt ) : ?>
									<?php
									if ( 'attachment' === $pt->name ) {
										continue;}
									?>
									<label style="display: inline-block; margin-right: 16px;">
										<input type="checkbox" name="indexnow_post_types[]" value="<?php echo esc_attr( $pt->name ); ?>" <?php checked( in_array( $pt->name, $indexnow_types, true ) ); ?> />
										<?php echo esc_html( $pt->labels->name ); ?>
									</label>
								<?php endforeach; ?>
								<p class="description"><?php esc_html_e( 'Select which post types should be auto-submitted to IndexNow.', 'seovault-ai' ); ?></p>
							</td>
						</tr>
					</table>

					<p><input type="submit" name="seovaultseo_save_indexnow" class="button button-primary" value="<?php esc_attr_e( 'Save IndexNow Settings', 'seovault-ai' ); ?>" /></p>

					<hr>

					<h3><?php esc_html_e( 'Regenerate API Key', 'seovault-ai' ); ?></h3>
					<p class="description"><?php esc_html_e( 'Generate a new IndexNow API key. The old key file will be deleted.', 'seovault-ai' ); ?></p>
					<p>
						<button type="submit" form="seovaultseo-legacy-regenerate-indexnow-key-form" class="button button-secondary" data-seovaultseo-confirm="<?php echo esc_attr__( 'This will invalidate the old key. Continue?', 'seovault-ai' ); ?>">
							<?php esc_html_e( 'Regenerate Key', 'seovault-ai' ); ?>
						</button>
					</p>

					<hr>

					<h3><?php esc_html_e( 'Manual URL Submission', 'seovault-ai' ); ?></h3>
					<p class="description"><?php esc_html_e( 'Submit individual URLs to IndexNow manually. Enter one URL per line.', 'seovault-ai' ); ?></p>
					<textarea id="seovaultseo-indexnow-urls" rows="5" style="width: 100%; max-width: 600px; font-family: monospace;" placeholder="<?php esc_attr_e( 'https://example.com/page-1/', 'seovault-ai' ); ?>&#10;<?php esc_attr_e( 'https://example.com/page-2/', 'seovault-ai' ); ?>"></textarea>
					<p>
						<button type="button" class="button button-primary" id="seovaultseo-indexnow-submit-btn"><?php esc_html_e( 'Submit URLs', 'seovault-ai' ); ?></button>
						<span id="seovaultseo-indexnow-submit-result" style="margin-left: 12px;"></span>
					</p>

					<hr>

					<h3><?php esc_html_e( 'Submission History', 'seovault-ai' ); ?></h3>
					<?php if ( empty( $indexnow_log ) ) : ?>
						<p><?php esc_html_e( 'No submissions yet.', 'seovault-ai' ); ?></p>
					<?php else : ?>
						<table class="widefat striped" style="max-width: 800px;">
							<thead>
								<tr>
									<th><?php esc_html_e( 'URL', 'seovault-ai' ); ?></th>
									<th><?php esc_html_e( 'Status', 'seovault-ai' ); ?></th>
									<th><?php esc_html_e( 'Type', 'seovault-ai' ); ?></th>
									<th><?php esc_html_e( 'Message', 'seovault-ai' ); ?></th>
									<th><?php esc_html_e( 'Date', 'seovault-ai' ); ?></th>
								</tr>
							</thead>
							<tbody>
								<?php foreach ( array_reverse( $indexnow_log ) as $entry ) : ?>
									<tr>
										<td style="word-break: break-all;"><?php echo esc_html( $entry['url'] ); ?></td>
										<td>
											<?php if ( in_array( (int) $entry['status'], array( 200, 202, 204 ), true ) ) : ?>
												<span style="color: #00a32a;"><?php echo esc_html( $entry['status'] ); ?></span>
											<?php else : ?>
												<span style="color: #d63638;"><?php echo esc_html( $entry['status'] ); ?></span>
											<?php endif; ?>
										</td>
										<td><?php echo $entry['manual_submission'] ? esc_html__( 'Manual', 'seovault-ai' ) : esc_html__( 'Auto', 'seovault-ai' ); ?></td>
										<td><?php echo esc_html( $entry['message'] ); ?></td>
										<td><?php echo esc_html( gmdate( 'Y-m-d H:i:s', (int) $entry['time'] ) ); ?></td>
									</tr>
								<?php endforeach; ?>
							</tbody>
						</table>
						<p style="margin-top: 10px;">
							<button type="button" class="button button-secondary" id="seovaultseo-indexnow-clear-log-btn"><?php esc_html_e( 'Clear Log', 'seovault-ai' ); ?></button>
						</p>
					<?php endif; ?>
				</div>

				<div id="import" class="seovault-ai-tab-content" style="display: none;">
					<h3><?php esc_html_e( 'Import SEO Meta from Other SEO Plugins', 'seovault-ai' ); ?></h3>
					<p>
						<?php esc_html_e( 'Import SEO meta data from other SEO plugins (Yoast, Rank Math, AIOSEO, Slim SEO, The SEO Framework). This will not delete the old plugin\'s data.', 'seovault-ai' ); ?>
					</p>

					<?php
					$active_provider = Detector::detect_active_provider();
					$has_competitor  = Detector::PROVIDER_NONE !== $active_provider;
					?>

					<?php if ( $has_competitor ) : ?>
						<div class="seovault-import-info">
							<p>
								<strong><?php esc_html_e( 'Detected Plugin:', 'seovault-ai' ); ?></strong>
								<?php echo esc_html( ucfirst( $active_provider ) ); ?>
							</p>
							<p>
								<strong><?php esc_html_e( 'Posts with Meta:', 'seovault-ai' ); ?></strong>
								<?php echo esc_html( \SEOvaultSEO\Importer\Reader::count_posts_with_meta( $active_provider ) ); ?>
							</p>
						</div>

						<form id="seovault-import-form" method="post">
							<?php wp_nonce_field( 'seovault_import', 'seovault_import_nonce' ); ?>
							<table class="form-table">
								<tr>
									<th scope="row">
										<label for="seovault_import_overwrite">
											<?php esc_html_e( 'Overwrite Existing SEOvault Meta', 'seovault-ai' ); ?>
										</label>
									</th>
									<td>
										<input
											type="checkbox"
											name="seovault_import_overwrite"
											id="seovault_import_overwrite"
											value="1"
										/>
										<p class="description">
											<?php esc_html_e( 'If checked, existing SEOvault meta will be overwritten. Otherwise, only empty fields will be imported.', 'seovault-ai' ); ?>
										</p>
									</td>
								</tr>
							</table>

							<p class="submit">
								<button type="button" class="button button-primary" id="seovault-import-btn">
									<?php esc_html_e( 'Import SEO Meta', 'seovault-ai' ); ?>
								</button>
							</p>
						</form>

						<div id="seovault-import-progress" style="display: none; margin-top: 20px;">
							<p><strong><?php esc_html_e( 'Import Progress:', 'seovault-ai' ); ?></strong></p>
							<div id="seovault-import-log" style="background: #fff; border: 1px solid #ccc; padding: 10px; height: 200px; overflow-y: scroll; font-family: monospace; font-size: 12px;"></div>
						</div>
					<?php else : ?>
						<div class="notice notice-info">
							<p>
								<?php esc_html_e( 'No other SEO plugin detected. Activate another SEO plugin to import its SEO meta data.', 'seovault-ai' ); ?>
							</p>
						</div>
					<?php endif; ?>

					<hr style="margin: 28px 0;" />

					<h3><?php esc_html_e( 'Convert FAQ & How-To Blocks (Yoast / Rank Math)', 'seovault-ai' ); ?></h3>
					<p>
						<?php esc_html_e( 'When Yoast or Rank Math is deactivated, their FAQ and How-to Gutenberg blocks become broken/unsupported. Scan your content and convert them to SEOvault FAQ and How-to blocks. Original content is backed up per post so you can roll back.', 'seovault-ai' ); ?>
					</p>
					<p>
						<button type="button" class="button button-secondary" id="seovault-thirdparty-block-scan-btn">
							<?php esc_html_e( 'Scan for Blocks', 'seovault-ai' ); ?>
						</button>
						<button type="button" class="button button-primary" id="seovault-thirdparty-block-convert-btn" disabled>
							<?php esc_html_e( 'Convert All Found Blocks', 'seovault-ai' ); ?>
						</button>
					</p>
					<div id="seovault-thirdparty-block-summary" style="display:none; margin-top: 12px;"></div>
					<div id="seovault-thirdparty-block-progress" style="display: none; margin-top: 16px;">
						<p><strong><?php esc_html_e( 'Conversion Progress:', 'seovault-ai' ); ?></strong></p>
						<div id="seovault-thirdparty-block-log" style="background: #fff; border: 1px solid #ccc; padding: 10px; height: 200px; overflow-y: scroll; font-family: monospace; font-size: 12px;"></div>
					</div>
				</div>

				<div id="status" class="seovault-ai-tab-content" style="display: none;">
					<table class="form-table">
						<tr>
							<th scope="row">
								<?php esc_html_e( 'Active Provider', 'seovault-ai' ); ?>
							</th>
							<td>
								<?php
								$provider_label = Detector::get_provider_label( $status['active_provider'] );
								$provider_class = ( Detector::PROVIDER_SEOVAULT === $status['active_provider'] ) ? 'seovaultseo-badge-success' : 'seovaultseo-badge-muted';
								echo '<span class="seovaultseo-badge ' . esc_attr( $provider_class ) . '">' . esc_html( $provider_label ) . '</span>';
								?>
							</td>
						</tr>
						<tr>
							<th scope="row">
								<?php esc_html_e( 'Detected Plugin', 'seovault-ai' ); ?>
							</th>
							<td>
								<?php
								$detected_label = Detector::get_provider_label( $status['detected_competitor'] );
								$detected_class = ( Detector::PROVIDER_NONE !== $status['detected_competitor'] ) ? 'seovaultseo-badge-warning' : 'seovaultseo-badge-success';
								echo '<span class="seovaultseo-badge ' . esc_attr( $detected_class ) . '">' . esc_html( $detected_label ) . '</span>';
								?>
							</td>
						</tr>
						<tr>
							<th scope="row">
								<?php esc_html_e( 'Other SEO Plugins Detected', 'seovault-ai' ); ?>
							</th>
							<td>
								<?php
								if ( $status['has_competitors'] ) {
									echo '<span class="seovaultseo-badge seovaultseo-badge-error"><span class="dashicons dashicons-warning"></span> ' . esc_html__( 'Yes', 'seovault-ai' ) . '</span>';
								} else {
									echo '<span class="seovaultseo-badge seovaultseo-badge-success"><span class="dashicons dashicons-yes-alt"></span> ' . esc_html__( 'No', 'seovault-ai' ) . '</span>';
								}
								?>
							</td>
						</tr>
						<?php if ( $status['has_competitors'] ) : ?>
						<tr>
							<th scope="row">
								<?php esc_html_e( 'Installed SEO Plugins', 'seovault-ai' ); ?>
							</th>
							<td>
								<?php
								foreach ( $status['competitors'] as $plugin_name ) {
									echo '<span class="seovaultseo-badge seovaultseo-badge-warning">' . esc_html( $plugin_name ) . '</span> ';
								}
								?>
							</td>
						</tr>
						<?php endif; ?>
						<tr>
							<th scope="row">
								<?php esc_html_e( 'Should Output SEO', 'seovault-ai' ); ?>
							</th>
							<td>
								<?php
								if ( $status['should_output_seo'] ) {
									echo '<span class="seovaultseo-badge seovaultseo-badge-success">' . esc_html__( 'Yes', 'seovault-ai' ) . '</span>';
								} else {
									echo '<span class="seovaultseo-badge seovaultseo-badge-error">' . esc_html__( 'No', 'seovault-ai' ) . '</span>';
								}
								?>
							</td>
						</tr>
						<tr>
							<th scope="row">
								<?php esc_html_e( 'Output Mode', 'seovault-ai' ); ?>
							</th>
							<td>
								<?php
								$mode_label = Detector::get_output_mode_label( $status['output_mode'] );
								$mode_class = 'seovaultseo-badge-info';
								if ( 'disabled' === $status['output_mode'] ) {
									$mode_class = 'seovaultseo-badge-error';
								} elseif ( 'auto' === $status['output_mode'] ) {
									$mode_class = 'seovaultseo-badge-success';
								}
								echo '<span class="seovaultseo-badge ' . esc_attr( $mode_class ) . '">' . esc_html( $mode_label ) . '</span>';
								?>
							</td>
						</tr>
						<tr>
							<th scope="row">
								<?php esc_html_e( 'Old Connector Active', 'seovault-ai' ); ?>
							</th>
							<td>
								<?php
								if ( $status['old_connector_active'] ) {
									echo '<span class="seovaultseo-badge seovaultseo-badge-warning">' . esc_html__( 'Yes', 'seovault-ai' ) . '</span>';
								} else {
									echo '<span class="seovaultseo-badge seovaultseo-badge-success">' . esc_html__( 'No', 'seovault-ai' ) . '</span>';
								}
								?>
							</td>
						</tr>
					</table>
				</div>

				<?php submit_button(); ?>
			</form>

			<?php
			// P6-001: Standalone POST forms for key actions (avoid nested forms / nonce-in-URL).
			$legacy_key_action = admin_url( 'admin.php?page=seovault-ai' );
			?>
			<form id="seovaultseo-legacy-regenerate-site-key-form" method="post" action="<?php echo esc_url( $legacy_key_action ); ?>" class="screen-reader-text" aria-hidden="true" tabindex="-1">
				<?php wp_nonce_field( 'seovaultseo_regenerate_key_action' ); ?>
				<input type="hidden" name="seovaultseo_regenerate_key" value="1" />
			</form>
			<form id="seovaultseo-legacy-regenerate-indexnow-key-form" method="post" action="<?php echo esc_url( $legacy_key_action . '#indexnow' ); ?>" class="screen-reader-text" aria-hidden="true" tabindex="-1">
				<?php wp_nonce_field( 'seovaultseo_indexnow_regenerate_key_action' ); ?>
				<input type="hidden" name="seovaultseo_indexnow_regenerate_key" value="1" />
			</form>
		</div>

		<?php
	}

	/**
	 * Read a sanitized single-line POST value.
	 *
	 * @param array  $request Verified POST request data.
	 * @param string $key     POST key.
	 * @param string $default Default when key is missing.
	 * @return string
	 */
	private static function post_text( array $request, $key, $default = '' ) {
		if ( ! isset( $request[ $key ] ) ) {
			return $default;
		}

		return sanitize_text_field( wp_unslash( $request[ $key ] ) );
	}

	/**
	 * Read a sanitized multi-line POST value.
	 *
	 * @param array  $request Verified POST request data.
	 * @param string $key     POST key.
	 * @param string $default Default when key is missing.
	 * @return string
	 */
	private static function post_textarea( array $request, $key, $default = '' ) {
		if ( ! isset( $request[ $key ] ) ) {
			return $default;
		}

		return sanitize_textarea_field( wp_unslash( $request[ $key ] ) );
	}

	/**
	 * Read a sanitized URL from POST.
	 *
	 * @param array  $request Verified POST request data.
	 * @param string $key     POST key.
	 * @param string $default Default when key is missing.
	 * @return string
	 */
	private static function post_url( array $request, $key, $default = '' ) {
		if ( ! isset( $request[ $key ] ) ) {
			return $default;
		}

		return esc_url_raw( wp_unslash( $request[ $key ] ) );
	}

	/**
	 * Read a signed integer from POST.
	 *
	 * This preserves negative values (e.g. -1 for "unlimited" robots
	 * directives such as max-snippet/max-video-preview). absint() would
	 * incorrectly coerce -1 to 1.
	 *
	 * @param array  $request Verified POST request data.
	 * @param string $key     POST key.
	 * @param int    $default Default when key is missing.
	 * @return int
	 */
	private static function post_signed_int( array $request, $key, $default = 0 ) {
		if ( ! isset( $request[ $key ] ) ) {
			return $default;
		}

		return (int) sanitize_text_field( wp_unslash( $request[ $key ] ) );
	}
}
