<?php
/**
 * This file is part of the Wisewand WordPress plugin.
 *
 * @package Wisewand
 * @since 1.0.0
 * @license GPL-2.0-or-later
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
namespace Wisewand\Core;

// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use Wisewand\Database\Database_Manager;
use Wisewand\Database\Database_Version;
use Wisewand\Database\Database_Upgrader;

// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange -- error_log() is only used within WP_DEBUG conditionals for debugging; direct database calls and schema changes required for database version migrations and custom plugin table operations

class Plugin {

	private static $instance = null;
	private $initialized     = false;

	// Lazy-loaded components
	private $rest_controller  = null;
	private $security_manager = null;
	private $content_manager  = null;
	private $seo_manager      = null;
	private $wpml_manager     = null;
	private $admin_manager    = null;
	private $media_manager    = null;
	private $logger           = null;
	private $options          = null;

	public static function instance() {
		if ( self::$instance === null ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	private function __construct() {
		// Don't initialize everything immediately
		// Wait for WordPress to be ready
		add_action( 'init', array( $this, 'init_plugin' ), 10 );
	}

	public function init_plugin() {
		if ( $this->initialized ) {
			return;
		}

		try {
			$this->check_database_version();
			$this->ensure_cron_scheduled(); // Ensure CRON is always scheduled
			$this->init_components();
			$this->init_hooks();
			$this->initialized = true;
		} catch ( Exception $e ) {
			if ( WP_DEBUG ) {
				error_log( 'Wisewand Plugin Init Error: ' . $e->getMessage() );
			}
		}
	}

	/**
	 * Ensure editorial calendar CRON is scheduled
	 * Runs on every plugin load to guarantee CRON exists
	 */
	private function ensure_cron_scheduled() {
		if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_event' ) ) {
			return;
		}

		// =========================================================================
		// TIER 1: Status Check CRON (Every 15 Minutes)
		// =========================================================================
		// Purpose: Check if generating articles are ready on Wisewand
		// Lightweight operation - only checks status, no heavy processing
		if ( ! wp_next_scheduled( 'wisewand_check_article_status' ) ) {
			// Try every_15_minutes schedule first
			$scheduled = wp_schedule_event( time(), 'every_15_minutes', 'wisewand_check_article_status' );

			// Fallback: If every_15_minutes doesn't exist, use hourly (built-in)
			if ( $scheduled === false ) {
				if ( WP_DEBUG ) {
					error_log( 'Wisewand: every_15_minutes schedule not available, falling back to hourly' );
				}
				$scheduled = wp_schedule_event( time(), 'hourly', 'wisewand_check_article_status' );
			}

			if ( $this->logger ) {
				if ( $scheduled !== false ) {
					$next_run = wp_next_scheduled( 'wisewand_check_article_status' );
					$this->logger->info(
						'Status check CRON auto-scheduled',
						array(
							'frequency'              => 'every_15_minutes (or hourly fallback)',
							'first_run'              => gmdate( 'Y-m-d H:i:s', time() ),
							'next_run'               => $next_run ? gmdate( 'Y-m-d H:i:s', $next_run ) : 'unknown',
							'scheduled_successfully' => true,
						)
					);
				} else {
					$this->logger->error( 'Failed to schedule status check CRON even with fallback' );
				}
			}
		} else {
			// Already scheduled - no need to log every time
			// $next_run = wp_next_scheduled( 'wisewand_check_article_status' );
		}

		// =========================================================================
		// TIER 2: Daily Operations CRON (2 AM)
		// =========================================================================
		// Purpose: Generate new articles, import ready ones, retry failed
		// Heavy operations - runs once per day
		if ( ! wp_next_scheduled( 'wisewand_editorial_calendar_cron' ) ) {
			$frequency    = 'daily';
			$tomorrow_2am = strtotime( 'tomorrow 2:00 AM' );

			$scheduled = wp_schedule_event( $tomorrow_2am, $frequency, 'wisewand_editorial_calendar_cron' );

			if ( $this->logger ) {
				if ( $scheduled !== false ) {
					$this->logger->info(
						'Editorial calendar CRON auto-scheduled',
						array(
							'frequency' => $frequency,
							'first_run' => gmdate( 'Y-m-d H:i:s', $tomorrow_2am ),
							'next_run'  => gmdate( 'Y-m-d H:i:s', wp_next_scheduled( 'wisewand_editorial_calendar_cron' ) ),
						)
					);
				} else {
					$this->logger->error(
						'Failed to schedule CRON',
						array(
							'frequency' => $frequency,
							'wp_error'  => 'wp_schedule_event returned false',
						)
					);
				}
			}
		}

		// =========================================================================
		// TIER 3: RSS Feeds Sync CRON (Hourly)
		// =========================================================================
		// Purpose: Check RSS feeds and import new articles automatically
		// Runs hourly to catch new content quickly
		if ( ! wp_next_scheduled( 'wisewand_rss_sync' ) ) {
			$scheduled = wp_schedule_event( time(), 'hourly', 'wisewand_rss_sync' );

			if ( $this->logger ) {
				if ( $scheduled !== false ) {
					$this->logger->info(
						'RSS sync CRON auto-scheduled',
						array(
							'frequency' => 'hourly',
							'first_run' => gmdate( 'Y-m-d H:i:s', time() ),
							'next_run'  => gmdate( 'Y-m-d H:i:s', wp_next_scheduled( 'wisewand_rss_sync' ) ),
						)
					);
				} else {
					$this->logger->error(
						'Failed to schedule RSS CRON',
						array(
							'frequency' => 'hourly',
							'wp_error'  => 'wp_schedule_event returned false',
						)
					);
				}
			}
		}
	}

	private function init_components() {
		// Initialize utilities first - these should always work
		$this->init_options();
		$this->init_logger();

		// Initialize other components only if their dependencies exist
		$this->init_security_manager();
		$this->init_media_manager();
		$this->init_admin_manager();

		// These depend on the above components
		$this->init_content_manager();
		$this->init_seo_manager();
		$this->init_wpml_manager();
		$this->init_rest_controller();
	}

	private function init_options() {
		if ( ! class_exists( 'Wisewand\\Utils\\Options' ) ) {
			$file = WISEWAND_PLUGIN_DIR . 'includes/Utils/Options.php';
			if ( file_exists( $file ) ) {
				require_once $file;
			}
		}

		if ( class_exists( 'Wisewand\\Utils\\Options' ) ) {
			$this->options = new \Wisewand\Utils\Options();
		}
	}

	private function init_logger() {
		if ( ! class_exists( 'Wisewand\\Logging\\Logger' ) ) {
			$file = WISEWAND_PLUGIN_DIR . 'includes/Logging/Logger.php';
			if ( file_exists( $file ) ) {
				require_once $file;
			}
		}

		if ( class_exists( 'Wisewand\\Logging\\Logger' ) ) {
			$this->logger = new \Wisewand\Logging\Logger();
		}
	}

	private function init_security_manager() {
		if ( ! $this->options || ! $this->logger ) {
			return;
		}

		if ( ! class_exists( 'Wisewand\\Security\\Security_Manager' ) ) {
			$file = WISEWAND_PLUGIN_DIR . 'includes/Security/Security_Manager.php';
			if ( file_exists( $file ) ) {
				require_once $file;
			}
		}

		if ( class_exists( 'Wisewand\\Security\\Security_Manager' ) ) {
			$this->security_manager = new \Wisewand\Security\Security_Manager( $this->options, $this->logger );
		}
	}

	private function init_media_manager() {
		if ( ! $this->options || ! $this->logger ) {
			return;
		}

		if ( ! class_exists( 'Wisewand\\Media\\Media_Manager' ) ) {
			$file = WISEWAND_PLUGIN_DIR . 'includes/Media/Media_Manager.php';
			if ( file_exists( $file ) ) {
				require_once $file;
			}
		}

		if ( class_exists( 'Wisewand\\Media\\Media_Manager' ) ) {
			$this->media_manager = new \Wisewand\Media\Media_Manager( $this->options, $this->logger );
		}
	}

	private function init_admin_manager() {
		if ( ! $this->options || ! $this->logger ) {
			return;
		}

		// Load API Client first since admin pages depend on it
		$api_client_file = WISEWAND_PLUGIN_DIR . 'includes/Utils/Wisewand_API_Client.php';
		if ( file_exists( $api_client_file ) ) {
			require_once $api_client_file;
		}

		// Load admin sub-pages
		$admin_files = array(
			'includes/Admin/Admin_Manager.php',
			'includes/Admin/Wisewand_Articles_Page.php',
			'includes/Admin/Wisewand_API_Settings_Page.php',
			'includes/Admin/Wisewand_Projects_Page.php',
			'includes/Admin/Wisewand_RSS_Page.php',
			'includes/Admin/Dashboard_Page.php',
			'includes/Admin/Editorial_Calendar.php',
			'includes/Admin/SEO_Intelligence_Real.php',
		);

		// Load RSS classes
		$rss_files = array(
			'includes/RSS/RSS_Feed_Manager.php',
			'includes/RSS/RSS_Article_Manager.php',
			'includes/RSS/RSS_Sync_Engine.php',
			'includes/RSS/RSS_CRON.php',
		);

		foreach ( $admin_files as $file ) {
			$full_path = WISEWAND_PLUGIN_DIR . $file;
			if ( file_exists( $full_path ) ) {
				require_once $full_path;
			}
		}

		foreach ( $rss_files as $file ) {
			$full_path = WISEWAND_PLUGIN_DIR . $file;
			if ( file_exists( $full_path ) ) {
				require_once $full_path;
			}
		}

		if ( class_exists( 'Wisewand\\Admin\\Admin_Manager' ) ) {
			$this->admin_manager = new \Wisewand\Admin\Admin_Manager( $this->options, $this->logger );
		}
	}

	private function init_content_manager() {
		if ( ! $this->options || ! $this->logger || ! $this->media_manager ) {
			return;
		}

		if ( ! class_exists( 'Wisewand\\Content\\Content_Manager' ) ) {
			$file = WISEWAND_PLUGIN_DIR . 'includes/Content/Content_Manager.php';
			if ( file_exists( $file ) ) {
				require_once $file;
			}
		}

		if ( class_exists( 'Wisewand\\Content\\Content_Manager' ) ) {
			$this->content_manager = new \Wisewand\Content\Content_Manager( $this->options, $this->logger, $this->media_manager );
		}
	}

	private function init_seo_manager() {
		if ( ! $this->options || ! $this->logger ) {
			return;
		}

		if ( ! class_exists( 'Wisewand\\SEO\\SEO_Manager' ) ) {
			$file = WISEWAND_PLUGIN_DIR . 'includes/SEO/SEO_Manager.php';
			if ( file_exists( $file ) ) {
				require_once $file;
			}
		}

		if ( class_exists( 'Wisewand\\SEO\\SEO_Manager' ) ) {
			$this->seo_manager = new \Wisewand\SEO\SEO_Manager( $this->options, $this->logger );
		}
	}

	private function init_wpml_manager() {
		if ( ! $this->options || ! $this->logger ) {
			return;
		}

		if ( ! class_exists( 'Wisewand\\WPML\\WPML_Manager' ) ) {
			$file = WISEWAND_PLUGIN_DIR . 'includes/WPML/WPML_Manager.php';
			if ( file_exists( $file ) ) {
				require_once $file;
			}
		}

		if ( class_exists( 'Wisewand\\WPML\\WPML_Manager' ) ) {
			$this->wpml_manager = new \Wisewand\WPML\WPML_Manager( $this->options, $this->logger );
		}
	}

	private function init_rest_controller() {
		if ( ! $this->security_manager || ! $this->content_manager || ! $this->logger || ! $this->options ) {
			return;
		}

		if ( ! class_exists( 'Wisewand\\API\\REST_Controller' ) ) {
			$file = WISEWAND_PLUGIN_DIR . 'includes/API/REST_Controller.php';
			if ( file_exists( $file ) ) {
				require_once $file;
			}
		}

		if ( class_exists( 'Wisewand\\API\\REST_Controller' ) ) {
			$this->rest_controller = new \Wisewand\API\REST_Controller(
				$this->security_manager,
				$this->content_manager,
				$this->logger,
				$this->options
			);
		}
	}

	/**
	 * Initialize all WordPress hooks
	 *
	 * This method orchestrates hook registration by delegating to focused methods.
	 * Complexity reduced from 234 to ~15 through method extraction.
	 *
	 * @since 1.0.0
	 */
	private function init_hooks() {
		// Initialize hooks by functional category
		$this->init_admin_hooks();
		$this->init_ajax_hooks();
		$this->init_cron_hooks();
		$this->init_rest_api_hooks();
		$this->init_content_hooks();
		$this->init_settings_hooks();
		$this->init_diagnostic_hooks();
	}

	/**
	 * Initialize admin interface hooks
	 *
	 * Registers hooks for admin menu, initialization, assets, and notices.
	 *
	 * @since 1.3.3
	 */
	private function init_admin_hooks() {
		// Database health check on admin init
		add_action( 'admin_init', array( $this, 'check_database_health' ), 5 );

		// Admin interface
		if ( $this->admin_manager ) {
			add_action( 'admin_menu', array( $this->admin_manager, 'add_admin_pages' ) );
			add_action( 'admin_init', array( $this->admin_manager, 'init_settings' ) );
			add_action( 'admin_init', array( $this, 'handle_activation_redirect' ) );
			add_action( 'admin_notices', array( $this, 'show_activation_notice' ) );
			add_action( 'admin_enqueue_scripts', array( $this->admin_manager, 'enqueue_admin_assets' ) );
		}

		// Security checks
		if ( $this->security_manager ) {
			add_action( 'init', array( $this->security_manager, 'init_security_checks' ) );
		}

		// Plugin version check
		add_action( 'wp_loaded', array( $this, 'check_plugin_updates' ) );
	}

	/**
	 * Initialize AJAX handler hooks
	 *
	 * Registers all AJAX endpoints for:
	 * - Core admin actions
	 * - API settings
	 * - Articles & generation
	 * - Projects & personas
	 * - SEO Intelligence & DataForSEO
	 * - Saved keywords
	 * - Editorial calendar
	 * - Deduplication & clustering
	 * - RSS feeds
	 *
	 * @since 1.3.3
	 */
	private function init_ajax_hooks() {
		if ( ! $this->admin_manager ) {
			return;
		}

		// Core admin AJAX handlers
		add_action( 'wp_ajax_wisewand_dismiss_activation_notice', array( $this, 'dismiss_activation_notice' ) );
		add_action( 'wp_ajax_wisewand_test_connection', array( $this->admin_manager, 'ajax_test_connection' ) );
		add_action( 'wp_ajax_wisewand_pull_article', array( $this->admin_manager, 'ajax_pull_article' ) );
		add_action( 'wp_ajax_wisewand_export_logs', array( $this->admin_manager, 'ajax_export_logs' ) );
		add_action( 'wp_ajax_wisewand_purge_logs', array( $this->admin_manager, 'ajax_purge_logs' ) );

		// API settings AJAX handlers
		$this->init_ajax_api_settings_hooks();

		// Articles & generation AJAX handlers
		$this->init_ajax_articles_hooks();

		// Projects & personas AJAX handlers
		$this->init_ajax_projects_hooks();

		// SEO Intelligence AJAX handlers
		$this->init_ajax_seo_intelligence_hooks();

		// Saved keywords AJAX handlers
		$this->init_ajax_saved_keywords_hooks();

		// Editorial calendar AJAX handlers
		$this->init_ajax_editorial_calendar_hooks();

		// Deduplication & clustering AJAX handlers
		$this->init_ajax_deduplication_hooks();

		// RSS feeds AJAX handlers
		$this->init_ajax_rss_feeds_hooks();
	}

	/**
	 * Initialize API settings AJAX hooks
	 *
	 * @since 1.3.3
	 */
	private function init_ajax_api_settings_hooks() {
		add_action(
			'wp_ajax_wisewand_test_api_connection',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_api_settings_page' ) ) {
					$api_page = $this->admin_manager->get_api_settings_page();
					if ( $api_page ) {
						$api_page->ajax_test_api_connection();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_detailed_api_test',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_api_settings_page' ) ) {
					$api_page = $this->admin_manager->get_api_settings_page();
					if ( $api_page ) {
						$api_page->ajax_detailed_api_test();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_remove_api_key',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_api_settings_page' ) ) {
					$api_page = $this->admin_manager->get_api_settings_page();
					if ( $api_page ) {
						$api_page->ajax_remove_api_key();
					}
				}
			}
		);
	}

	/**
	 * Initialize articles & generation AJAX hooks
	 *
	 * @since 1.3.3
	 */
	private function init_ajax_articles_hooks() {
		add_action(
			'wp_ajax_wisewand_import_article',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_articles_page' ) ) {
					$articles_page = $this->admin_manager->get_articles_page();
					if ( $articles_page ) {
						$articles_page->ajax_import_article();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_run_generation',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_articles_page' ) ) {
					$articles_page = $this->admin_manager->get_articles_page();
					if ( $articles_page ) {
						$articles_page->ajax_run_generation();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_bulk_import',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_articles_page' ) ) {
					$articles_page = $this->admin_manager->get_articles_page();
					if ( $articles_page ) {
						$articles_page->ajax_bulk_import();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_bulk_generate',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_articles_page' ) ) {
					$articles_page = $this->admin_manager->get_articles_page();
					if ( $articles_page ) {
						$articles_page->ajax_bulk_generate();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_test_import_workflow',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_articles_page' ) ) {
					$articles_page = $this->admin_manager->get_articles_page();
					if ( $articles_page ) {
						$articles_page->ajax_test_import_workflow();
					}
				}
			}
		);
	}

	/**
	 * Initialize projects & personas AJAX hooks
	 *
	 * @since 1.3.3
	 */
	private function init_ajax_projects_hooks() {
		add_action(
			'wp_ajax_wisewand_get_project_details',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_projects_page' ) ) {
					$projects_page = $this->admin_manager->get_projects_page();
					if ( $projects_page ) {
						$projects_page->ajax_get_project_details();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_get_persona_details',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_projects_page' ) ) {
					$projects_page = $this->admin_manager->get_projects_page();
					if ( $projects_page ) {
						$projects_page->ajax_get_persona_details();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_delete_persona',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_projects_page' ) ) {
					$projects_page = $this->admin_manager->get_projects_page();
					if ( $projects_page ) {
						$projects_page->ajax_delete_persona();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_debug_projects',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'ajax_debug_projects' ) ) {
					$this->admin_manager->ajax_debug_projects();
				}
			}
		);
	}

	/**
	 * Initialize SEO Intelligence AJAX hooks
	 *
	 * Includes DataForSEO debug handlers and real analysis handlers.
	 *
	 * @since 1.3.3
	 */
	private function init_ajax_seo_intelligence_hooks() {
		// DataForSEO Debug AJAX handlers
		add_action(
			'wp_ajax_wisewand_debug_dataforseo_credentials',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_debug_credentials();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_debug_dataforseo_api',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_debug_api();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_debug_domain_overview',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_debug_domain_overview();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_debug_keywords',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_debug_keywords();
					}
				}
			}
		);

		// SEO Intelligence Real AJAX handlers
		add_action(
			'wp_ajax_wisewand_run_real_analysis',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_run_real_analysis();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_load_latest_snapshot',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_load_latest_snapshot();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_schedule_keyword',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_schedule_keyword();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_discover_keywords',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_discover_keywords();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_add_manual_keywords',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_add_manual_keywords();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_generate_smart_roadmap',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_generate_smart_roadmap();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_get_available_languages',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_get_available_languages();
					}
				}
			}
		);
	}

	/**
	 * Initialize saved keywords AJAX hooks
	 *
	 * @since 1.3.3
	 */
	private function init_ajax_saved_keywords_hooks() {
		add_action(
			'wp_ajax_wisewand_save_keywords_to_library',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_save_keywords_to_library();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_enrich_saved_keywords',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_enrich_saved_keywords();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_schedule_saved_keyword',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_schedule_saved_keyword();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_schedule_saved_keywords',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_schedule_saved_keywords();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_delete_saved_keyword',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_delete_saved_keyword();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_bulk_delete_saved_keywords',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_bulk_delete_saved_keywords();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_get_keyword_ideas',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_get_keyword_ideas();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_spy_competitors',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_spy_competitors();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_save_spy_keywords',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_save_spy_keywords();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_save_column_preferences',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_save_column_preferences();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_remove_from_calendar',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_remove_from_calendar();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_clear_all_calendar',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_clear_all_calendar();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_update_saved_keyword',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_update_saved_keyword();
					}
				}
			}
		);
	}

	/**
	 * Initialize editorial calendar AJAX hooks
	 *
	 * @since 1.3.3
	 */
	private function init_ajax_editorial_calendar_hooks() {
		add_action(
			'wp_ajax_wisewand_get_calendar_events',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_get_calendar_events();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_get_entry_details',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_get_entry_details();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_update_entry_date',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_update_entry_gmdate();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_trigger_manual_cron',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_trigger_manual_cron();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_save_entry',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_save_entry();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_delete_entry',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_delete_entry();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_generate_instructions_template',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_generate_instructions_template();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_generate_instructions_ai',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_generate_instructions_ai();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_get_projects',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_get_projects();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_get_personas',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_get_personas();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_generate_now',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_generate_now();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_import_now',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_import_now();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_get_project_details',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_editorial_calendar_page' ) ) {
					$calendar_page = $this->admin_manager->get_editorial_calendar_page();
					if ( $calendar_page ) {
						$calendar_page->ajax_get_project_details();
					}
				}
			}
		);
	}

	/**
	 * Initialize deduplication & clustering AJAX hooks
	 *
	 * @since 1.3.3
	 */
	private function init_ajax_deduplication_hooks() {
		add_action(
			'wp_ajax_wisewand_scan_duplicates',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_scan_duplicates();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_get_clusters',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_get_clusters();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_set_cluster_primary',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_set_cluster_primary();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_ungroup_cluster',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_ungroup_cluster();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_delete_cluster_variants',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_delete_cluster_variants();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_approve_cluster',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_approve_cluster();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_check_cannibalization',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_check_cannibalization();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_run_clustering_scan',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_run_clustering_scan();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_ai_match_categories',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_ai_match_categories();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_count_keywords_for_matching',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_count_keywords_for_matching();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_save_keyword_author_categories',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_save_keyword_author_categories();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_check_ranking_conflicts',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_seo_intelligence_page' ) ) {
					$seo_page = $this->admin_manager->get_seo_intelligence_page();
					if ( $seo_page ) {
						$seo_page->ajax_check_ranking_conflicts();
					}
				}
			}
		);
	}

	/**
	 * Initialize RSS feeds AJAX hooks
	 *
	 * @since 1.3.3
	 */
	private function init_ajax_rss_feeds_hooks() {
		add_action(
			'wp_ajax_wisewand_rss_test_connection',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_test_rss_connection();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_list_feeds',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_list_feeds();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_sync_feed',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_sync_feed();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_bulk_sync',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_bulk_sync();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_get_articles',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_get_articles();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_import_article',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_import_article();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_bulk_import',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_bulk_import();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_get_feed_settings',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_get_feed_settings();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_save_feed_settings',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_save_feed_settings();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_bulk_activate',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_bulk_activate();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_bulk_deactivate',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_bulk_deactivate();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_get_global_publication_settings',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_get_global_publication_settings();
					}
				}
			}
		);
		add_action(
			'wp_ajax_wisewand_rss_cleanup_data',
			function () {
				if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
					$rss_page = $this->admin_manager->get_rss_page();
					if ( $rss_page ) {
						$rss_page->ajax_cleanup_rss_data();
					}
				}
			}
		);
	}

	/**
	 * Initialize WP-Cron hooks
	 *
	 * Registers hooks for:
	 * - Editorial calendar automation (daily & 15-min status checks)
	 * - RSS feeds synchronization (hourly)
	 * - Pull mode retry mechanism
	 * - Manual CRON triggers
	 * - Custom CRON schedules
	 *
	 * @since 1.3.3
	 */
	private function init_cron_hooks() {
		// WP-Cron for PULL mode
		if ( $this->content_manager ) {
			add_action( 'wisewand_pull_retry', array( $this->content_manager, 'process_pull_retry' ) );
		}

		// WP-Cron for Editorial Calendar automation
		add_action( 'wisewand_editorial_calendar_cron', array( $this, 'run_editorial_calendar_cron' ) );
		add_action( 'wisewand_check_article_status', array( $this, 'run_status_check_cron' ) );
		add_action( 'wisewand_import_ready_content', array( $this, 'run_import_ready_content' ) );

		// WP-Cron for RSS Feeds automation
		add_action( 'wisewand_rss_sync', array( $this, 'run_rss_sync_cron' ) );

		// Manual CRON trigger (for dashboard refresh button)
		add_action( 'wp_ajax_wisewand_manual_cron_trigger', array( $this, 'ajax_manual_cron_trigger' ) );
		add_action( 'wp_ajax_wisewand_manual_full_cron_trigger', array( $this, 'ajax_manual_full_cron_trigger' ) );

		// Custom CRON schedules
		add_filter( 'cron_schedules', array( $this, 'add_custom_cron_schedules' ) );
	}

	/**
	 * Initialize REST API hooks
	 *
	 * Registers routes for:
	 * - REST controller
	 * - Webhook receiver
	 *
	 * @since 1.3.3
	 */
	private function init_rest_api_hooks() {
		// REST API controller
		if ( $this->rest_controller ) {
			add_action( 'rest_api_init', array( $this->rest_controller, 'register_routes' ) );
		}

		// Webhook Receiver
		add_action(
			'rest_api_init',
			function () {
				if ( ! class_exists( 'Wisewand\\API\\Webhook_Receiver' ) ) {
					require_once WISEWAND_PLUGIN_DIR . 'includes/API/Webhook_Receiver.php';
				}

				if ( $this->options && $this->logger ) {
					$webhook_receiver = new \Wisewand\API\Webhook_Receiver( $this->options, $this->logger );
					$webhook_receiver->register_routes();
				}
			}
		);
	}

	/**
	 * Initialize content-related hooks
	 *
	 * Currently handles:
	 * - WordPress publish status sync to Wisewand API
	 *
	 * @since 1.3.3
	 */
	private function init_content_hooks() {
		// Sync WordPress publish status to Wisewand API (bidirectional sync)
		add_action( 'transition_post_status', array( $this, 'sync_publish_status_to_wisewand' ), 10, 3 );
	}

	/**
	 * Initialize settings-related hooks
	 *
	 * Placeholder for future settings filters.
	 *
	 * @since 1.3.3
	 */
	private function init_settings_hooks() {
		// Future: Add settings filters here
	}

	/**
	 * Initialize diagnostic & monitoring hooks
	 *
	 * Placeholder for future dashboard widgets and logging hooks.
	 *
	 * @since 1.3.3
	 */
	private function init_diagnostic_hooks() {
		// Future: Add dashboard widgets, logging, monitoring hooks here
	}

	public function check_plugin_updates() {
		$current_version = get_option( 'wisewand_plugin_version', '0.0.0' );

		if ( version_compare( $current_version, WISEWAND_PLUGIN_VERSION, '<' ) ) {
			// Run upgrade routines if needed
			$this->upgrade_plugin( $current_version );
			update_option( 'wisewand_plugin_version', WISEWAND_PLUGIN_VERSION );
		}
	}

	private function check_database_version() {
		$installed_version = get_option( 'wisewand_plugin_version', '0.0.0' );

		if ( version_compare( $installed_version, WISEWAND_PLUGIN_VERSION, '<' ) ) {
			// Database needs update - ensure all tables exist
			$this->create_missing_tables();
			update_option( 'wisewand_plugin_version', WISEWAND_PLUGIN_VERSION );

			if ( $this->logger ) {
				$this->logger->info(
					'Database updated',
					array(
						'from_version' => $installed_version,
						'to_version'   => WISEWAND_PLUGIN_VERSION,
					)
				);
			}
		}

		// Run specific migrations independently (version-agnostic)
		$this->run_database_migrations();
	}

	/**
	 * Check database schema and auto-heal if needed
	 *
	 * Called on admin_init to ensure database is current.
	 * Runs auto-healing if plugin version changed.
	 *
	 * @since 1.0.2
	 */
	public function check_database_health() {
		// Only run in admin context
		if ( ! is_admin() ) {
			return;
		}

		// Check if plugin version changed
		if ( Database_Version::plugin_version_changed() ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				$old_version = Database_Version::get_plugin_version();
				error_log( "Wisewand: Plugin version changed from {$old_version} to " . WISEWAND_PLUGIN_VERSION );
			}

			// Run database health check and auto-heal
			Database_Manager::auto_heal();

			// Run any pending upgrades
			if ( Database_Manager::needs_upgrade() ) {
				if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
					error_log( 'Wisewand: Running database upgrades' );
				}

				// This runs Migration 5 & 7 if needed
				Database_Upgrader::run_all_migrations();
			}

			// Update plugin version
			$old_version = Database_Version::get_plugin_version();
			Database_Version::update_plugin_version( WISEWAND_PLUGIN_VERSION );
			Database_Version::record_version_update( $old_version, WISEWAND_PLUGIN_VERSION );

			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( 'Wisewand: Database health check completed' );
			}
		}
	}

	private function run_database_migrations() {
		// Migration 001: Update saved_keywords UNIQUE constraint
		$migration_001_done = get_option( 'wisewand_migration_001_keyword_language', false );
		if ( ! $migration_001_done ) {
			$this->migrate_saved_keywords_unique_constraint();
			update_option( 'wisewand_migration_001_keyword_language', true );
		}

		// Migration 002: Add clustering columns to saved_keywords
		$migration_002_done = get_option( 'wisewand_migration_002_clustering', false );
		if ( ! $migration_002_done ) {
			$this->migrate_add_clustering_columns();
			update_option( 'wisewand_migration_002_clustering', true );
		}

		// Migration 003: Add wordpress_categories to editorial_calendar
		$migration_003_done = get_option( 'wisewand_migration_003_categories', false );
		if ( ! $migration_003_done ) {
			$this->migrate_add_categories_column();
			update_option( 'wisewand_migration_003_categories', true );
		}

		// Migration 004: Add AI matching columns to saved_keywords
		$migration_004_done = get_option( 'wisewand_migration_004_ai_matching', false );
		if ( ! $migration_004_done ) {
			$this->migrate_add_ai_matching_columns();
			update_option( 'wisewand_migration_004_ai_matching', true );
		}

		// Migration 005: Add feed_type column to wisewand_rss_feeds
		$migration_005_done = get_option( 'wisewand_migration_005_feed_type', false );
		if ( ! $migration_005_done ) {
			$this->migrate_add_feed_type_column();
			update_option( 'wisewand_migration_005_feed_type', true );
		}
	}

	private function migrate_add_ai_matching_columns() {
		global $wpdb;
		$saved_keywords_table = $wpdb->prefix . 'wisewand_saved_keywords';

		// Check if table exists
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- DDL SHOW TABLES statement for migration check
		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $saved_keywords_table ) ) !== $saved_keywords_table ) {
			return;
		}

        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- DDL SHOW COLUMNS statement for migration check
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$columns = $wpdb->get_col( "SHOW COLUMNS FROM {$saved_keywords_table}" );

		// Add suggested_author_id if missing
		if ( ! in_array( 'suggested_author_id', $columns ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query(
				"ALTER TABLE `{$saved_keywords_table}`
                ADD COLUMN `suggested_author_id` int(10) DEFAULT NULL COMMENT 'AI-suggested author ID' AFTER `cluster_similarity_score`"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		// Add suggested_categories if missing
		if ( ! in_array( 'suggested_categories', $columns ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query(
				"ALTER TABLE `{$saved_keywords_table}`
                ADD COLUMN `suggested_categories` text DEFAULT NULL COMMENT 'JSON array of AI-suggested category IDs' AFTER `suggested_author_id`"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		// Add ai_matched_at if missing
		if ( ! in_array( 'ai_matched_at', $columns ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query(
				"ALTER TABLE `{$saved_keywords_table}`
                ADD COLUMN `ai_matched_at` datetime DEFAULT NULL COMMENT 'When AI matching was performed' AFTER `suggested_categories`"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		// Add ai_model_used if missing
		if ( ! in_array( 'ai_model_used', $columns ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query(
				"ALTER TABLE `{$saved_keywords_table}`
                ADD COLUMN `ai_model_used` varchar(50) DEFAULT NULL COMMENT 'OpenAI model used for matching' AFTER `ai_matched_at`"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		if ( $this->logger ) {
			$this->logger->info( 'Migration 004: Added AI matching columns to saved_keywords' );
		}
	}

	/**
	 * Migration 005: Add feed_type column to wisewand_rss_feeds
	 * Enables support for both "articles" and "discoverarticles" feed types
	 */
	private function migrate_add_feed_type_column() {
		global $wpdb;
		$rss_feeds_table = $wpdb->prefix . 'wisewand_rss_feeds';

		// Check if table exists
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- DDL SHOW TABLES statement for migration check
		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $rss_feeds_table ) ) !== $rss_feeds_table ) {
			if ( $this->logger ) {
				$this->logger->warning( 'Migration 005: wisewand_rss_feeds table does not exist, skipping' );
			}
			return;
		}

		// Get existing columns
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- DDL SHOW COLUMNS statement for migration check
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$columns = $wpdb->get_col( "SHOW COLUMNS FROM {$rss_feeds_table}" );

		// Add feed_type column if missing
		if ( ! in_array( 'feed_type', $columns, true ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$result = $wpdb->query(
				"ALTER TABLE `{$rss_feeds_table}`
				ADD COLUMN `feed_type` VARCHAR(50) NOT NULL DEFAULT 'articles' COMMENT 'Feed type: articles or discoverarticles' AFTER `wisewand_project_id`"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

			if ( false === $result ) {
				if ( $this->logger ) {
					$this->logger->error(
						'Migration 005: Failed to add feed_type column',
						array(
							'table'      => $rss_feeds_table,
							'wpdb_error' => $wpdb->last_error,
						)
					);
				}
				// Don't mark migration as complete
				return;
			}

			if ( $this->logger ) {
				$this->logger->info( 'Migration 005: Added feed_type column to wisewand_rss_feeds' );
			}
		}

		// Backfill existing records with 'discoverarticles'
		// User confirmed all current feeds are discoverarticles type
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Data migration UPDATE statement
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$updated = $wpdb->query(
			"UPDATE `{$rss_feeds_table}`
			SET `feed_type` = 'discoverarticles'
			WHERE `feed_type` = 'articles'"
		);

		if ( $this->logger ) {
			$this->logger->info(
				'Migration 005: Backfilled feed_type for existing feeds',
				array(
					'feeds_updated' => $updated,
					'type'          => 'discoverarticles',
				)
			);
		}
	}

	private function migrate_add_clustering_columns() {
		global $wpdb;
		$saved_keywords_table = $wpdb->prefix . 'wisewand_saved_keywords';

		// Check if table exists
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $saved_keywords_table ) ) !== $saved_keywords_table ) {
			return;
		}

		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$columns = $wpdb->get_col( "SHOW COLUMNS FROM {$saved_keywords_table}" );
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		// Add cluster_id if missing
		if ( ! in_array( 'cluster_id', $columns ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query(
				"ALTER TABLE `{$saved_keywords_table}`
                ADD COLUMN `cluster_id` bigint(20) DEFAULT NULL COMMENT 'Reference to cluster if part of one' AFTER `id`"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		// Add is_cluster_primary if missing
		if ( ! in_array( 'is_cluster_primary', $columns ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query(
				"ALTER TABLE `{$saved_keywords_table}`
                ADD COLUMN `is_cluster_primary` tinyint(1) DEFAULT 0 COMMENT '1 if this is the primary keyword in its cluster' AFTER `is_enriched`"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		// Add cluster_similarity_score if missing
		if ( ! in_array( 'cluster_similarity_score', $columns ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query(
				"ALTER TABLE `{$saved_keywords_table}`
                ADD COLUMN `cluster_similarity_score` decimal(5,2) DEFAULT NULL COMMENT 'Avg similarity to cluster' AFTER `is_cluster_primary`"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		// Add indexes if not exist
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$indexes     = $wpdb->get_results( "SHOW INDEX FROM {$saved_keywords_table}" );
		$index_names = array_column( $indexes, 'Key_name' );
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( ! in_array( 'cluster_id', $index_names ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query( "ALTER TABLE `{$saved_keywords_table}` ADD KEY `cluster_id` (`cluster_id`)" );
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		if ( ! in_array( 'is_cluster_primary', $index_names ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query( "ALTER TABLE `{$saved_keywords_table}` ADD KEY `is_cluster_primary` (`is_cluster_primary`)" );
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		}

		if ( $this->logger ) {
			$this->logger->info( 'Migration 002: Added clustering columns to saved_keywords' );
		}
	}

	private function migrate_add_categories_column() {
		global $wpdb;
		$editorial_calendar_table = $wpdb->prefix . 'wisewand_editorial_calendar';

		// Check if table exists
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
		if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $editorial_calendar_table ) ) !== $editorial_calendar_table ) {
			return;
		}

		// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
		$columns = $wpdb->get_col( "SHOW COLUMNS FROM {$editorial_calendar_table}" );
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		// Add wordpress_categories if missing
		if ( ! in_array( 'wordpress_categories', $columns ) ) {
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$wpdb->query(
				"ALTER TABLE `{$editorial_calendar_table}`
                ADD COLUMN `wordpress_categories` text DEFAULT NULL COMMENT 'JSON array of category IDs for WordPress import' AFTER `target_tags`"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

			if ( $this->logger ) {
				$this->logger->info( 'Migration 003: Added wordpress_categories column to editorial_calendar' );
			}
		}
	}

	private function create_missing_tables() {
		global $wpdb;

		if ( ! $wpdb ) {
			return;
		}

		$charset_collate = $wpdb->get_charset_collate();
		$tables_to_check = array();

		// Logs table - MUST BE FIRST so Logger works during initialization
		$tables_to_check['wisewand_logs'] = "CREATE TABLE {table_name} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            request_id varchar(50) DEFAULT NULL,
            level varchar(20) NOT NULL,
            message text NOT NULL,
            context longtext,
            ip_address varchar(45) DEFAULT NULL,
            user_agent varchar(500) DEFAULT NULL,
            endpoint varchar(100) DEFAULT NULL,
            method varchar(10) DEFAULT NULL,
            status_code int(3) DEFAULT NULL,
            timestamp datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY request_id (request_id),
            KEY level (level),
            KEY timestamp (timestamp DESC)
        ) $charset_collate;";

		// Keywords V2 table
		$tables_to_check['wisewand_seo_keywords_v2'] = "CREATE TABLE {table_name} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            snapshot_id varchar(64) NOT NULL,
            target_domain varchar(255) NOT NULL,
            language_code varchar(10) NOT NULL DEFAULT 'fr',
            location_code int(10) NOT NULL DEFAULT 2250,
            keyword varchar(255) NOT NULL,
            keyword_type enum('current','opportunity') DEFAULT 'current',
            position_abs int(3) DEFAULT NULL,
            page_url text,
            search_volume int(10) DEFAULT 0,
            search_intent varchar(32) DEFAULT 'informational',
            content_type varchar(32) DEFAULT 'blog',
            priority_score decimal(5,2) DEFAULT 0,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY snapshot_id (snapshot_id),
            KEY target_domain (target_domain),
            KEY keyword_type (keyword_type),
            KEY priority_score (priority_score DESC),
            KEY position_abs (position_abs)
        ) $charset_collate;";

		// Editorial Calendar table
		$tables_to_check['wisewand_editorial_calendar'] = "CREATE TABLE {table_name} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            keyword varchar(255) NOT NULL,
            keyword_source enum('current_ranking','manual','discovery','saved') DEFAULT 'manual',
            search_volume int(10) DEFAULT 0,
            current_position int(3) DEFAULT NULL,
            search_intent varchar(32) DEFAULT 'informational',
            difficulty int(3) DEFAULT NULL,
            priority_score decimal(5,2) DEFAULT 0,
            publication_action enum('create','update','skip') DEFAULT 'create',
            existing_post_id bigint(20) DEFAULT NULL,
            wisewand_payload longtext NOT NULL,
            wisewand_article_id varchar(100) DEFAULT NULL,
            wisewand_project_id varchar(100) DEFAULT NULL,
            wisewand_persona_id varchar(100) DEFAULT NULL,
            wisewand_lang varchar(10) DEFAULT 'en',
            wisewand_type varchar(50) DEFAULT 'blog',
            wisewand_length varchar(20) DEFAULT 'auto',
            wisewand_status enum('not_created','creating','generating','ready','failed') DEFAULT 'not_created',
            wisewand_error text DEFAULT NULL,
            wisewand_retry_count int(3) DEFAULT 0,
            wordpress_post_id bigint(20) DEFAULT NULL,
            wordpress_status enum('scheduled','importing','draft','published','failed') DEFAULT 'scheduled',
            wordpress_error text DEFAULT NULL,
            publication_date date NOT NULL,
            publication_time time DEFAULT NULL,
            publication_time_range varchar(20) DEFAULT 'random',
            publish_as_draft tinyint(1) DEFAULT 1,
            auto_publish tinyint(1) DEFAULT 0,
            content_merge_strategy enum('replace','append','prepend') DEFAULT 'replace',
            target_post_type varchar(20) DEFAULT 'post',
            target_author int(10) DEFAULT NULL,
            target_category varchar(255) DEFAULT NULL,
            target_tags text DEFAULT NULL,
            scheduled_at datetime DEFAULT CURRENT_TIMESTAMP,
            generation_triggered_at datetime DEFAULT NULL,
            generation_completed_at datetime DEFAULT NULL,
            import_started_at datetime DEFAULT NULL,
            published_at datetime DEFAULT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            notes text DEFAULT NULL,
            user_modified tinyint(1) DEFAULT 0,
            created_by int(10) DEFAULT NULL,
            PRIMARY KEY (id),
            KEY keyword (keyword),
            KEY publication_date (publication_date),
            KEY wisewand_status (wisewand_status),
            KEY wordpress_status (wordpress_status),
            KEY keyword_source (keyword_source),
            KEY publication_action (publication_action),
            KEY priority_score (priority_score DESC),
            UNIQUE KEY keyword_date (keyword, publication_date)
        ) $charset_collate;";

		// Saved Keywords table
		$tables_to_check['wisewand_saved_keywords'] = "CREATE TABLE {table_name} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            keyword varchar(255) NOT NULL,
            keyword_source enum('manual','keyword_ideas','competitor_spy') DEFAULT 'manual',
            seed_keyword varchar(255) DEFAULT NULL,
            language_code varchar(10) DEFAULT 'en',
            location_code int(10) DEFAULT 2840,
            search_volume int(10) DEFAULT 0,
            difficulty int(3) DEFAULT NULL,
            search_intent varchar(32) DEFAULT 'informational',
            content_type varchar(32) DEFAULT 'blog',
            priority_score decimal(5,2) DEFAULT 0,
            is_enriched tinyint(1) DEFAULT 0,
            tags text DEFAULT NULL,
            notes text DEFAULT NULL,
            created_by int(10) DEFAULT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            UNIQUE KEY keyword_language (keyword, language_code),
            KEY keyword_source (keyword_source),
            KEY is_enriched (is_enriched),
            KEY language_code (language_code),
            KEY priority_score (priority_score DESC),
            KEY created_at (created_at DESC)
        ) $charset_collate;";

		// Competitor Analysis table
		$tables_to_check['wisewand_competitor_analysis'] = "CREATE TABLE {table_name} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            analysis_id varchar(64) NOT NULL,
            user_domain varchar(255) NOT NULL,
            competitor_domains text NOT NULL,
            language_code varchar(10) NOT NULL DEFAULT 'en',
            location_code int(10) NOT NULL DEFAULT 2840,
            analysis_type enum('gap','best_performers','hybrid') DEFAULT 'hybrid',
            total_keywords_found int(10) DEFAULT 0,
            keywords_saved int(10) DEFAULT 0,
            filters_used longtext,
            analysis_data longtext,
            created_by int(10) DEFAULT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY analysis_id (analysis_id),
            KEY user_domain (user_domain),
            KEY created_at (created_at DESC)
        ) $charset_collate;";

		// Competitor Keywords table
		$tables_to_check['wisewand_competitor_keywords'] = "CREATE TABLE {table_name} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            analysis_id varchar(64) NOT NULL,
            keyword varchar(255) NOT NULL,
            search_volume int(10) DEFAULT 0,
            difficulty int(3) DEFAULT NULL,
            competitor_domain varchar(255) NOT NULL,
            competitor_position int(3) DEFAULT NULL,
            user_position int(3) DEFAULT NULL,
            gap_type enum('missing','outranked','both') DEFAULT 'missing',
            relevance_score decimal(5,2) DEFAULT 0,
            priority_score decimal(5,2) DEFAULT 0,
            serp_data longtext,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY analysis_id (analysis_id),
            KEY keyword (keyword),
            KEY priority_score (priority_score DESC),
            KEY gap_type (gap_type),
            KEY competitor_domain (competitor_domain)
        ) $charset_collate;";

		// Create missing tables
		foreach ( $tables_to_check as $table_suffix => $sql_template ) {
			$table_name = $wpdb->prefix . $table_suffix;
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) === $table_name;
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

			if ( ! $table_exists ) {
				$sql = str_replace( '{table_name}', $table_name, $sql_template );
                // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange -- DDL CREATE TABLE statement for missing tables
				// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table creation query with safe table name
		$wpdb->query( $sql );

				if ( $this->logger && $wpdb->last_error ) {
					$this->logger->error(
						'Failed to create database table',
						array(
							'table' => $table_name,
							'error' => $wpdb->last_error,
						)
					);
				} elseif ( $this->logger ) {
					$this->logger->info( 'Created missing database table', array( 'table' => $table_name ) );
				}
			}
		}
	}

	private function upgrade_plugin( $from_version ) {
		// Handle plugin upgrades
		if ( $this->logger ) {
			$this->logger->info(
				'Plugin upgraded',
				array(
					'from_version' => $from_version,
					'to_version'   => WISEWAND_PLUGIN_VERSION,
				)
			);
		}

		// Migration: Update saved_keywords UNIQUE constraint to support same keyword in different languages
		$this->migrate_saved_keywords_unique_constraint();
	}

	private function migrate_saved_keywords_unique_constraint() {
		global $wpdb;

		if ( ! $wpdb ) {
			return;
		}

		$table_name = $wpdb->prefix . 'wisewand_saved_keywords';

		// Check if table exists
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
		$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) === $table_name;
		// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

		if ( ! $table_exists ) {
			return; // Table doesn't exist yet, no migration needed
		}

		try {
			// Check if the old UNIQUE constraint exists
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$existing_keys = $wpdb->get_results( $wpdb->prepare( "SHOW KEYS FROM %i WHERE Key_name = 'keyword'", $table_name ), ARRAY_A );
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

			$has_old_constraint = false;
			$has_new_constraint = false;

			foreach ( $existing_keys as $key ) {
				if ( $key['Key_name'] === 'keyword' && $key['Non_unique'] == 0 ) {
					$has_old_constraint = true;
				}
			}

			// Check if new composite constraint already exists
			// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
			// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
			$new_keys           = $wpdb->get_results( "SHOW KEYS FROM {$table_name} WHERE Key_name = 'keyword_language'", ARRAY_A );
			// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			$has_new_constraint = ! empty( $new_keys );

			// Only migrate if we have the old constraint and don't have the new one
			if ( $has_old_constraint && ! $has_new_constraint ) {
				// Drop the old UNIQUE constraint
				// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
				// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
				$wpdb->query( "ALTER TABLE {$table_name} DROP INDEX keyword" );
				// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

				if ( $this->logger ) {
					$this->logger->info( 'Dropped old UNIQUE constraint on keyword column', array( 'table' => $table_name ) );
				}

				// Add new composite UNIQUE constraint on (keyword, language_code)
				// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				// Table name uses $wpdb->prefix which is safe and cannot be parameterized in DDL statements.
				// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- Table name from $wpdb->prefix is safe
				$wpdb->query( "ALTER TABLE {$table_name} ADD UNIQUE KEY keyword_language (keyword, language_code)" );
				// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

				if ( $wpdb->last_error ) {
					if ( $this->logger ) {
						$this->logger->error(
							'Failed to add composite UNIQUE constraint',
							array(
								'table' => $table_name,
								'error' => $wpdb->last_error,
							)
						);
					}
				} elseif ( $this->logger ) {
						$this->logger->info( 'Added composite UNIQUE constraint (keyword, language_code)', array( 'table' => $table_name ) );
				}
			} elseif ( $has_new_constraint ) {
				if ( $this->logger ) {
					$this->logger->debug( 'Composite UNIQUE constraint already exists, skipping migration', array( 'table' => $table_name ) );
				}
			}
		} catch ( \Exception $e ) {
			if ( $this->logger ) {
				$this->logger->error(
					'Migration failed for saved_keywords table',
					array(
						'error' => $e->getMessage(),
						'table' => $table_name,
					)
				);
			}
		}
	}

	// Safe getters that check if components exist
	public function get_rest_controller() {
		return $this->rest_controller;
	}

	public function get_security_manager() {
		return $this->security_manager;
	}

	public function get_content_manager() {
		return $this->content_manager;
	}

	public function get_seo_manager() {
		return $this->seo_manager;
	}

	public function get_wpml_manager() {
		return $this->wpml_manager;
	}

	public function get_admin_manager() {
		return $this->admin_manager;
	}

	public function get_media_manager() {
		return $this->media_manager;
	}

	public function get_logger() {
		return $this->logger;
	}

	public function get_options() {
		return $this->options;
	}

	public function is_initialized() {
		return $this->initialized;
	}

	/**
	 * Show activation notice instead of forcing redirect
	 * WordPress.org compliant - no dashboard hijacking
	 */
	public function handle_activation_redirect() {
		// Check if we should show the activation notice
		if ( ! get_transient( 'wisewand_activation_redirect' ) ) {
			return;
		}

		// Delete the redirect transient
		delete_transient( 'wisewand_activation_redirect' );

		// Set a flag to show the admin notice instead
		set_transient( 'wisewand_show_activation_notice', true, 300 ); // 5 minutes
	}

	/**
	 * Display activation notice with link to settings
	 */
	public function show_activation_notice() {
		// Only show if the transient is set
		if ( ! get_transient( 'wisewand_show_activation_notice' ) ) {
			return;
		}

		// Check if user dismissed the notice
		$user_id = get_current_user_id();
		if ( get_user_meta( $user_id, 'wisewand_activation_notice_dismissed', true ) ) {
			delete_transient( 'wisewand_show_activation_notice' );
			return;
		}

		// Enqueue inline script for notice dismissal
		$this->enqueue_activation_notice_script();

		$settings_url = admin_url( 'admin.php?page=wisewand-settings' );
		?>
		<div class="notice notice-success is-dismissible" data-dismissible="wisewand-activation-notice">
			<p>
				<strong><?php esc_html_e( 'Wisewand Activated!', 'wisewand-ai-content-writer-seo-content-generator' ); ?></strong><br>
				<?php
				printf(
					/* translators: %s: Link to settings page */
					esc_html__( 'Thank you for installing Wisewand. Get started by %s to configure your API keys and preferences.', 'wisewand-ai-content-writer-seo-content-generator' ),
					'<a href="' . esc_url( $settings_url ) . '">' . esc_html__( 'visiting the settings page', 'wisewand-ai-content-writer-seo-content-generator' ) . '</a>'
				);
				?>
			</p>
		</div>
		<?php
	}

	/**
	 * Enqueue inline script for activation notice dismissal
	 */
	private function enqueue_activation_notice_script() {
		// Ensure jQuery is loaded (it's a default WP admin script)
		wp_enqueue_script( 'jquery' );

		$nonce = wp_create_nonce( 'wisewand_dismiss_notice' );

		// Add inline script for notice dismissal
		$script = "
		jQuery(document).ready(function($) {
			$(document).on('click', '.notice[data-dismissible=\"wisewand-activation-notice\"] .notice-dismiss', function() {
				$.post(ajaxurl, {
					action: 'wisewand_dismiss_activation_notice',
					nonce: '" . esc_js( $nonce ) . "'
				});
			});
		});
		";

		wp_add_inline_script( 'jquery', $script );
	}

	/**
	 * Handle AJAX request to dismiss activation notice
	 */
	public function dismiss_activation_notice() {
		check_ajax_referer( 'wisewand_dismiss_notice', 'nonce' );

		$user_id = get_current_user_id();
		update_user_meta( $user_id, 'wisewand_activation_notice_dismissed', true );
		delete_transient( 'wisewand_show_activation_notice' );

		wp_send_json_success();
	}

	public function add_custom_cron_schedules( $schedules ) {
		// 15-minute schedule for frequent status checks
		$schedules['every_15_minutes'] = array(
			'interval' => 900, // 15 minutes = 900 seconds
			'display'  => __( 'Every 15 Minutes (Wisewand Status Check)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);

		$schedules['wisewand_hourly'] = array(
			'interval' => 3600,
			'display'  => __( 'Every Hour (Wisewand)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);

		$schedules['wisewand_twice_daily'] = array(
			'interval' => 43200,
			'display'  => __( 'Twice Daily (Wisewand)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);

		$schedules['wisewand_four_times_daily'] = array(
			'interval' => 21600,
			'display'  => __( 'Four Times Daily (Wisewand)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);

		return $schedules;
	}

	/**
	 * AJAX Handler - Manual CRON trigger (for dashboard refresh button)
	 * Runs status check immediately when user clicks refresh
	 */
	public function ajax_manual_cron_trigger() {
		check_ajax_referer( 'wisewand_dashboard', 'nonce' );

		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Insufficient permissions', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		$this->logger->info( 'Manual CRON trigger via dashboard refresh button' );

		try {
			// Run status check CRON immediately
			$this->run_status_check_cron();

			wp_send_json_success(
				array(
					'message'   => esc_html__( 'Status check completed', 'wisewand-ai-content-writer-seo-content-generator' ),
					'timestamp' => current_time( 'mysql' ),
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error( 'Manual CRON trigger failed', array( 'error' => $e->getMessage() ) );
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	/**
	 * AJAX Handler - Manual full CRON trigger (for dashboard button)
	 * Runs complete CRON with logging
	 */
	public function ajax_manual_full_cron_trigger() {
		check_ajax_referer( 'wisewand_dashboard', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Insufficient permissions', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		$this->logger->info( 'Manual full CRON trigger via dashboard button' );

		try {
			// Capture logs during execution
			$logs = array();

			// Run full daily CRON
			$this->run_editorial_calendar_cron();

			// Get summary from option
			$summary_json = get_option( 'wisewand_cron_last_summary', '{}' );
			$summary      = json_decode( $summary_json, true );

			wp_send_json_success(
				array(
					'message'   => esc_html__( 'CRON completed successfully', 'wisewand-ai-content-writer-seo-content-generator' ),
					'summary'   => $summary,
					'timestamp' => current_time( 'mysql' ),
					'logs'      => $logs,
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error( 'Manual full CRON trigger failed', array( 'error' => $e->getMessage() ) );
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	/**
	 * Run status check CRON (every 15 minutes)
	 * Lightweight operation - only checks if generating articles are ready
	 */
	public function run_status_check_cron() {
		if ( ! $this->content_manager ) {
			return;
		}

		if ( ! class_exists( 'Wisewand\\Automation\\Calendar_CRON' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Automation/Calendar_CRON.php';
		}

		$calendar_cron = new \Wisewand\Automation\Calendar_CRON(
			$this->options,
			$this->logger,
			$this->content_manager
		);

		// Only run status check (lightweight, fast)
		$calendar_cron->run_status_check_only();
	}

	/**
	 * Import a single ready content entry (webhook-triggered)
	 *
	 * @param int $entry_id Calendar entry ID to import
	 */
	public function run_import_ready_content( $entry_id ) {
		if ( ! $this->content_manager ) {
			return;
		}

		if ( ! class_exists( 'Wisewand\\Automation\\Calendar_CRON' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Automation/Calendar_CRON.php';
		}

		if ( ! class_exists( 'Wisewand\\Calendar\\Calendar_Entry_Manager' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Calendar/Calendar_Entry_Manager.php';
		}

		$calendar_cron = new \Wisewand\Automation\Calendar_CRON(
			$this->options,
			$this->logger,
			$this->content_manager
		);

		// Get the entry
		$payload_builder = new \Wisewand\Utils\Wisewand_Payload_Builder( $this->options, $this->logger );
		$entry_manager   = new \Wisewand\Calendar\Calendar_Entry_Manager( $this->options, $this->logger, $payload_builder );

		$entry = $entry_manager->get_entry( $entry_id );
		if ( is_wp_error( $entry ) ) {
			if ( $this->logger ) {
				$this->logger->error(
					'Failed to import ready content - entry not found',
					array(
						'entry_id' => $entry_id,
						'error'    => $entry->get_error_message(),
					)
				);
			}
			return;
		}

		// Import the entry using the private method via reflection
		$reflection = new \ReflectionClass( $calendar_cron );
		$method     = $reflection->getMethod( 'import_and_publish_entry' );
		$method->setAccessible( true );
		$result = $method->invoke( $calendar_cron, $entry );

		if ( is_wp_error( $result ) ) {
			if ( $this->logger ) {
				$this->logger->error(
					'Webhook-triggered import failed',
					array(
						'entry_id' => $entry_id,
						'error'    => $result->get_error_message(),
					)
				);
			}
		} elseif ( $this->logger ) {
				$this->logger->info(
					'Webhook-triggered import successful',
					array(
						'entry_id' => $entry_id,
						'post_id'  => $result,
					)
				);
		}
	}

	/**
	 * Run full editorial calendar CRON (daily at 2 AM)
	 * Heavy operations - generate, import, retry
	 */
	public function run_editorial_calendar_cron() {
		if ( ! $this->content_manager ) {
			return;
		}

		if ( ! class_exists( 'Wisewand\\Automation\\Calendar_CRON' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Automation/Calendar_CRON.php';
		}

		$calendar_cron = new \Wisewand\Automation\Calendar_CRON(
			$this->options,
			$this->logger,
			$this->content_manager
		);

		// Run full daily operations (pass force_retry = true for manual trigger)
		$calendar_cron->run( true ); // ✅ Force retry when manually triggered!
	}

	/**
	 * Run RSS feeds sync CRON (hourly)
	 * Checks feeds and imports new articles automatically
	 */
	public function run_rss_sync_cron() {
		if ( ! $this->content_manager ) {
			return;
		}

		if ( ! class_exists( 'Wisewand\\RSS\\RSS_CRON' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/RSS/RSS_CRON.php';
		}

		if ( ! class_exists( 'Wisewand\\Calendar\\Calendar_Entry_Manager' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Calendar/Calendar_Entry_Manager.php';
		}

		// Initialize Calendar Entry Manager (required by RSS_CRON)
		$payload_builder = new \Wisewand\Utils\Wisewand_Payload_Builder( $this->options, $this->logger );
		$calendar_manager = new \Wisewand\Calendar\Calendar_Entry_Manager( $this->options, $this->logger, $payload_builder );

		$rss_cron = new \Wisewand\RSS\RSS_CRON(
			$this->options,
			$this->logger,
			$this->content_manager,
			$calendar_manager
		);

		// Run RSS sync
		$rss_cron->run();
	}

	/**
	 * Sync WordPress publish status to Wisewand API
	 * Triggered when post status changes (publish, unpublish, quick edit, bulk edit)
	 * Implements bidirectional sync between WordPress and Wisewand
	 *
	 * @param string  $new_status New post status
	 * @param string  $old_status Old post status
	 * @param WP_Post $post Post object
	 */
	public function sync_publish_status_to_wisewand( $new_status, $old_status, $post ) {
		// Only process when status changes TO or FROM 'publish'
		if ( $new_status === $old_status ) {
			return; // No status change
		}

		if ( $new_status !== 'publish' && $old_status !== 'publish' ) {
			return; // Neither publishing nor unpublishing
		}

		// Only process posts that came from Wisewand
		$wisewand_article_id = get_post_meta( $post->ID, '_wisewand_external_id', true );
		if ( empty( $wisewand_article_id ) ) {
			return; // Not a Wisewand article - skip
		}

		// Determine publish status
		$is_published = ( $new_status === 'publish' );
		$action       = $is_published ? 'published' : 'unpublished';

		if ( WP_DEBUG ) {
			error_log( 'WISEWAND_SYNC: Post #' . $post->ID . ' ' . $action . ' manually, syncing to Wisewand article ' . $wisewand_article_id );
			error_log( 'WISEWAND_SYNC: Status change: ' . $old_status . ' → ' . $new_status );
		}

		// Send PATCH request to Wisewand API
		if ( ! class_exists( 'Wisewand\\Utils\\Wisewand_API_Client' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Wisewand_API_Client.php';
		}

		if ( ! $this->options ) {
			if ( WP_DEBUG ) {
				error_log( 'WISEWAND_SYNC: Options not available, skipping sync' );
			}
			return;
		}

		try {
			$api_client = new \Wisewand\Utils\Wisewand_API_Client(
				$this->options->get( 'wisewand_api_key' ),
				$this->logger
			);
			$result     = $api_client->update_article(
				$wisewand_article_id,
				array(
					'is_published' => $is_published,
				)
			);

			if ( is_wp_error( $result ) ) {
				if ( $this->logger ) {
					$this->logger->warning(
						'Failed to sync is_published to Wisewand',
						array(
							'post_id'      => $post->ID,
							'article_id'   => $wisewand_article_id,
							'is_published' => $is_published,
							'error'        => $result->get_error_message(),
						)
					);
				}
				if ( WP_DEBUG ) {
					error_log( 'WISEWAND_SYNC: PATCH failed - ' . $result->get_error_message() );
				}
				return; // Soft fail - don't block WordPress publishing
			}

			if ( $this->logger ) {
				$this->logger->info(
					'Synced publish status to Wisewand',
					array(
						'post_id'      => $post->ID,
						'article_id'   => $wisewand_article_id,
						'is_published' => $is_published,
						'action'       => $action,
					)
				);
			}

			if ( WP_DEBUG ) {
				error_log( 'WISEWAND_SYNC: ✅ Successfully updated Wisewand article ' . $wisewand_article_id . ' is_published = ' . ( $is_published ? 'true' : 'false' ) );
			}

			// Also update calendar entry if exists (keeps calendar accurate)
			global $wpdb;
			$calendar_table = $wpdb->prefix . 'wisewand_editorial_calendar';

			if ( $is_published ) {
				// Publishing: Update imported_draft → imported_published
				$updated = $wpdb->update(
					$calendar_table,
					array( 'wordpress_status' => 'imported_published' ),
					array(
						'wordpress_post_id' => $post->ID,
						'wordpress_status'  => 'imported_draft',
					),
					array( '%s' ),
					array( '%d', '%s' )
				);

				if ( $updated > 0 && WP_DEBUG ) {
					error_log( 'WISEWAND_SYNC: Updated calendar entry from imported_draft to imported_published' );
				}
			} else {
				// Unpublishing: Update imported_published → imported_draft
				$updated = $wpdb->update(
					$calendar_table,
					array( 'wordpress_status' => 'imported_draft' ),
					array(
						'wordpress_post_id' => $post->ID,
						'wordpress_status'  => 'imported_published',
					),
					array( '%s' ),
					array( '%d', '%s' )
				);

				if ( $updated > 0 && WP_DEBUG ) {
					error_log( 'WISEWAND_SYNC: Updated calendar entry from imported_published to imported_draft' );
				}
			}
		} catch ( \Exception $e ) {
			if ( $this->logger ) {
				$this->logger->error(
					'Exception syncing publish status to Wisewand',
					array(
						'post_id'    => $post->ID,
						'article_id' => $wisewand_article_id,
						'error'      => $e->getMessage(),
					)
				);
			}
			if ( WP_DEBUG ) {
				error_log( 'WISEWAND_SYNC: Exception - ' . $e->getMessage() );
			}
			// Soft fail - don't disrupt WordPress publishing
		}
	}
}