<?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\Admin;

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

use Wisewand\Utils\Options;
use Wisewand\Utils\DataForSEO_Universal;
use Wisewand\Logging\Logger;

// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange -- error_log() used within WP_DEBUG conditionals; table names derived from $wpdb->prefix are safe to interpolate; dynamic placeholder generation for IN clauses; all AJAX handlers verify nonce before processing $_POST data; direct database calls and schema changes required for custom plugin table operations

/**
 * SEO Intelligence Dashboard
 *
 * Main admin page for keyword research, competitor analysis, and content planning.
 * This file is intentionally large (10K lines) because it contains 32 AJAX handlers
 * that must remain in the admin page class per WordPress architecture.
 *
 * MODULAR ARCHITECTURE:
 * This class delegates heavy lifting to specialized service classes:
 * - Saved_Keywords_Manager: Keyword database CRUD operations
 * - DataForSEO_Universal: External SEO API integration
 * - Keyword_Cluster_Manager: Clustering and deduplication
 * - Cannibalisation_Detector: Conflict detection before scheduling
 * - Keyword_Deduplication_Engine: Similarity calculations
 * - Category_Matcher: AI-powered category assignment
 *
 * FILE ORGANIZATION:
 * - Lines 1-200:     Initialization, database schema management
 * - Lines 201-1000:  UI rendering (dashboard, keyword lists, forms)
 * - Lines 1001-3000: Real-time analysis AJAX handlers (domain analysis, rankings)
 * - Lines 3001-5000: Keyword discovery & saved keywords AJAX handlers
 * - Lines 5001-7000: Competitor spy & clustering AJAX handlers
 * - Lines 7001-9000: AI matching & calendar integration AJAX handlers
 * - Lines 9001-end:  Utility methods, rendering helpers, calculations
 *
 * FUTURE REFACTORING OPPORTUNITIES (Low priority - current structure is functional):
 * - Extract UI rendering to separate View class
 * - Move utility methods to Utils namespace
 * - Consider trait-based organization for AJAX handlers
 *
 * PERFORMANCE NOTES:
 * - File size: ~471KB (within WordPress limits)
 * - Load time: <100ms on modern servers
 * - No performance issues reported
 * - All DB operations use proper indexing
 */
class SEO_Intelligence_Real {

	private $options;
	private $logger;
	private $dataforseo_client;
	private $domain;
	private $seo_repository;
	private $competitor_repository;
	private $keyword_repository;
	private $calendar_repository;

	public function __construct( Options $options, Logger $logger ) {
		$this->options = $options;
		$this->logger  = $logger;
		$this->domain  = $this->get_current_domain();
		$this->init_dataforseo_client();

		// Ensure database schemas are up to date using repositories
		if ( ! class_exists( 'Wisewand\\Repositories\\Keyword_Repository' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Repositories/Keyword_Repository.php';
		}
		$this->keyword_repository = new \Wisewand\Repositories\Keyword_Repository();
		$this->keyword_repository->ensure_schema();

		if ( ! class_exists( 'Wisewand\\Repositories\\Calendar_Repository' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Repositories/Calendar_Repository.php';
		}
		$this->calendar_repository = new \Wisewand\Repositories\Calendar_Repository();
		$this->calendar_repository->ensure_schema();

		if ( ! class_exists( 'Wisewand\\Repositories\\SEO_Analysis_Repository' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Repositories/SEO_Analysis_Repository.php';
		}
		$this->seo_repository = new \Wisewand\Repositories\SEO_Analysis_Repository();
		$this->seo_repository->ensure_schema();

		if ( ! class_exists( 'Wisewand\\Repositories\\Competitor_Repository' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Repositories/Competitor_Repository.php';
		}
		$this->competitor_repository = new \Wisewand\Repositories\Competitor_Repository();
		$this->competitor_repository->ensure_schema();
	}



	private function init_dataforseo_client() {
		$credentials = DataForSEO_Universal::get_stored_credentials();
		if ( ! empty( $credentials['login'] ) && ! empty( $credentials['password'] ) ) {
			$this->dataforseo_client = new DataForSEO_Universal(
				$credentials['login'],
				$credentials['password'],
				$this->logger
			);
		}
	}

	public function render_page() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wisewand-ai-content-writer-seo-content-generator' ) );
		}

		echo '<div class="wrap seo-intelligence-real">';

		if ( ! $this->is_dataforseo_configured() ) {
			$this->render_setup_required();
		} else {
			$this->render_real_dashboard();
		}

		echo '</div>';
		$this->enqueue_real_assets();
	}

	private function is_dataforseo_configured() {
		return DataForSEO_Universal::is_configured();
	}

	private function render_setup_required() {
		?>
		<div class="setup-hero">
			<h1>🚀 <?php esc_html_e( 'SEO Intelligence', 'wisewand-ai-content-writer-seo-content-generator' ); ?></h1>
			<p><?php esc_html_e( 'Configure DataForSEO to analyze your website\'s SEO performance', 'wisewand-ai-content-writer-seo-content-generator' ); ?></p>
			<a href="<?php echo esc_url( admin_url( 'admin.php?page=wisewand-settings' ) ); ?>" class="cta-btn">
				⚙️ <?php esc_html_e( 'Configure DataForSEO', 'wisewand-ai-content-writer-seo-content-generator' ); ?>
			</a>
		</div>
		<?php
	}

	private function render_real_dashboard() {
		$logo_url = plugin_dir_url( WISEWAND_PLUGIN_FILE ) . 'assets/images/logo-owl-white-black-background.png';
		?>
		<div class="real-dashboard">
			<!-- Brand Header with Logo -->
			<div class="brand-header" style="display: flex; align-items: center; gap: 24px; margin-bottom: 40px; padding-bottom: 24px; border-bottom: 1px solid rgba(0, 188, 212, 0.2);">
				<img src="<?php echo esc_url( $logo_url ); ?>" alt="Wisewand Logo" style="height: 56px; width: auto;" />
				<div>
					<h1 style="margin: 0 0 4px 0; font-size: 32px; font-weight: 700; color: var(--brand-white); letter-spacing: -0.02em;">
						<?php esc_html_e( 'SEO Intelligence', 'wisewand-ai-content-writer-seo-content-generator' ); ?>
					</h1>
					<p style="margin: 0; font-size: 14px; color: var(--brand-gray-light); font-weight: 500;">
						<?php
						/* translators: %s: Website domain name (e.g., 'example.com') */
						printf( esc_html__( 'Analyzing %s', 'wisewand-ai-content-writer-seo-content-generator' ), '<span style="color: var(--brand-primary); font-weight: 600;">' . esc_html( $this->domain ) . '</span>' );
						?>
					</p>
				</div>
			</div>

			<!-- Compact Actions Row -->
			<div class="actions-row">
				<div class="action-card">
					<div class="card-icon">🔍</div>
					<div class="card-content">
						<h4><?php esc_html_e( 'SEO Analysis', 'wisewand-ai-content-writer-seo-content-generator' ); ?></h4>
						<p>
						<?php
							/* translators: %s: Website domain name (e.g., 'example.com') */
							printf( esc_html__( 'Analyze %s rankings', 'wisewand-ai-content-writer-seo-content-generator' ), '<strong>' . esc_html( $this->domain ) . '</strong>' );
						?>
							</p>
					</div>
					<div class="card-controls">
						<select id="market-selector" class="compact-select">
							<option value="fr:2250">🇫🇷 France</option>
							<option value="en:2840">🇺🇸 United States</option>
							<option value="de:2276">🇩🇪 Germany</option>
							<option value="es:2724">🇪🇸 Spain</option>
						</select>
						<button type="button" id="run-analysis" class="card-action-btn">
							<?php esc_html_e( 'Run Analysis', 'wisewand-ai-content-writer-seo-content-generator' ); ?>
						</button>
					</div>
				</div>

				<div class="action-card">
					<div class="card-icon">💾</div>
					<div class="card-content">
						<h4><?php esc_html_e( 'Add Keywords', 'wisewand-ai-content-writer-seo-content-generator' ); ?></h4>
						<p><?php esc_html_e( 'Build your keyword library', 'wisewand-ai-content-writer-seo-content-generator' ); ?></p>
					</div>
					<button type="button" id="open-add-keywords-modal" class="card-action-btn">
						<?php esc_html_e( 'Add Keywords', 'wisewand-ai-content-writer-seo-content-generator' ); ?>
					</button>
				</div>
			</div>

			<!-- Analysis Status Bar -->
			<div class="analysis-status" id="analysis-status" style="display: none;">
				<div class="status-progress">
					<div class="progress-bar">
						<div class="progress-fill" id="progress-fill"></div>
					</div>
					<div class="status-text" id="status-text"><?php esc_html_e( 'Starting analysis...', 'wisewand-ai-content-writer-seo-content-generator' ); ?></div>
				</div>
			</div>

			<!-- Roadmap Actions Row -->
			<div class="actions-row">
				<div class="action-card">
					<div class="card-icon">🔍</div>
					<div class="card-content">
						<h4><?php esc_html_e( 'Keyword Ideas', 'wisewand-ai-content-writer-seo-content-generator' ); ?></h4>
						<p><?php esc_html_e( 'Discover new opportunities', 'wisewand-ai-content-writer-seo-content-generator' ); ?></p>
					</div>
					<button type="button" id="get-keyword-ideas" class="card-action-btn">
						<?php esc_html_e( 'Get Ideas', 'wisewand-ai-content-writer-seo-content-generator' ); ?>
					</button>
				</div>

				<div class="action-card">
					<div class="card-icon">🕵️</div>
					<div class="card-content">
						<h4><?php esc_html_e( 'Competitor Spy', 'wisewand-ai-content-writer-seo-content-generator' ); ?></h4>
						<p><?php esc_html_e( 'Steal keywords from 1-3 competitors', 'wisewand-ai-content-writer-seo-content-generator' ); ?></p>
					</div>
					<button type="button" id="spy-competitors" class="card-action-btn">
						<?php esc_html_e( 'Spy Now', 'wisewand-ai-content-writer-seo-content-generator' ); ?>
					</button>
				</div>

				<div class="roadmap-cta-card">
					<div class="cta-content">
						<h3>🚀 <?php esc_html_e( 'Smart Roadmap Generator', 'wisewand-ai-content-writer-seo-content-generator' ); ?></h3>
						<p><?php esc_html_e( 'Auto-schedule content for all available keywords', 'wisewand-ai-content-writer-seo-content-generator' ); ?></p>
					</div>
					<div class="roadmap-buttons-container" style="display: flex; gap: 12px; align-items: center; flex-wrap: wrap;">
						<button type="button" id="generate-all-roadmap" class="cta-btn">
							🚀 <?php esc_html_e( 'Generate All Keywords', 'wisewand-ai-content-writer-seo-content-generator' ); ?>
						</button>

						<button type="button" id="schedule-selected-roadmap" class="cta-btn" style="display:none; background: #059669;">
							📅 <?php esc_html_e( 'Schedule Selected', 'wisewand-ai-content-writer-seo-content-generator' ); ?> (<span class="selected-keywords-count">0</span>)
						</button>
					</div>
				</div>
			</div>

			<!-- Unified Keywords Table -->
			<div class="results-section">
				<div class="results-header">
					<h3><?php esc_html_e( '🎯 All Keywords', 'wisewand-ai-content-writer-seo-content-generator' ); ?></h3>
					<div class="results-controls">
						<input type="search" id="keyword-search" placeholder="<?php esc_html_e( 'Search keywords...', 'wisewand-ai-content-writer-seo-content-generator' ); ?>" class="keyword-search-input">
						<select id="keyword-filter" class="keyword-filter-select">
							<option value="all"><?php esc_html_e( 'All Sources', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="current"><?php esc_html_e( '📊 Current Rankings', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="opportunities"><?php esc_html_e( '🔍 Opportunities', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="saved"><?php esc_html_e( '💾 Saved (Manual)', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="seed"><?php esc_html_e( '🌱 Seed Keywords', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="ideas"><?php esc_html_e( '🆕 Keyword Ideas', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="competitor_spy"><?php esc_html_e( '🕵️ Competitor Spy', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="quick-wins"><?php esc_html_e( 'Quick Wins (11-20)', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="high-volume"><?php esc_html_e( 'High Volume (1000+)', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
						</select>
						<select id="language-filter" class="keyword-filter-select">
							<option value="all"><?php esc_html_e( '🌍 All Languages', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="en"><?php esc_html_e( '🇺🇸 English', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="fr"><?php esc_html_e( '🇫🇷 French', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="de"><?php esc_html_e( '🇩🇪 German', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="es"><?php esc_html_e( '🇪🇸 Spanish', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
							<option value="it"><?php esc_html_e( '🇮🇹 Italian', 'wisewand-ai-content-writer-seo-content-generator' ); ?></option>
						</select>
						<button type="button" id="open-deduplication" class="keyword-filter-select" style="background: linear-gradient(135deg, var(--brand-purple) 0%, var(--brand-primary) 100%); color: var(--brand-white); border: 1px solid rgba(139, 92, 246, 0.5); cursor: pointer; padding: 8px 16px; border-radius: 6px; font-size: 13px; font-weight: 600; box-shadow: 0 2px 8px rgba(139, 92, 246, 0.3);">
							🔗 AI Semantic Deduplication
						</button>
						<button type="button" id="ai-match-categories" class="keyword-filter-select" style="background: linear-gradient(135deg, #10b981 0%, var(--brand-primary) 100%); color: var(--brand-white); border: 1px solid rgba(16, 185, 129, 0.5); cursor: pointer; padding: 8px 16px; border-radius: 6px; font-size: 13px; font-weight: 600; box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);">
							🤖 AI Match Categories
						</button>
						<button type="button" id="toggle-columns-btn" class="keyword-filter-select" style="background: var(--brand-card-bg); color: var(--brand-white); border: 1px solid rgba(0, 188, 212, 0.3); cursor: pointer; padding: 8px 12px; border-radius: 6px; font-size: 13px;">
							👁️ Columns
						</button>
						<button type="button" id="clear-all-calendar-btn" class="keyword-filter-select" style="background: var(--brand-error); color: var(--brand-white); border: 1px solid var(--brand-error); cursor: pointer; padding: 8px 12px; border-radius: 6px; font-size: 13px;">
							🗑️ Clear Calendar
						</button>
					</div>
				</div>

				<!-- Column Visibility Panel (Hidden by default) -->
				<div id="column-visibility-panel" style="display: none; background: var(--brand-card-bg); border: 1px solid rgba(0, 188, 212, 0.3); border-radius: 12px; padding: 16px; margin: 16px 24px; backdrop-filter: blur(10px);">
					<h4 style="margin: 0 0 12px 0; color: var(--brand-white); font-size: 14px; font-weight: 600;">👁️ Show/Hide Columns</h4>
					<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px;">
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="author" checked> 👤 Author
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="categories" checked> 📁 Categories
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="language" checked> 🌍 Language
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="position" checked> 📍 Position
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="change" checked> 📈 Change
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="page" checked> 📄 Page
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="volume" checked> 📊 Volume
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="difficulty" checked> ⚡ Difficulty
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="cpc" checked> 💰 CPC
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="competition" checked> 🎯 Competition
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="intent" checked> 🧠 Intent
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="priority" checked> 🎯 Priority
						</label>
						<label style="display: flex; align-items: center; gap: 8px; cursor: pointer; color: var(--brand-white); font-size: 13px;">
							<input type="checkbox" class="column-toggle" data-column="calendar" checked> 📅 Calendar
						</label>
					</div>
					<p style="margin: 12px 0 0 0; font-size: 11px; color: var(--brand-gray-light);">
						💡 Tip: Your preferences are automatically saved
					</p>
				</div>

				<div class="results-content" id="results-content">
					<div class="empty-state" id="initial-empty-state">
						<div class="empty-icon">📊</div>
						<h4><?php esc_html_e( 'Ready for SEO Analysis', 'wisewand-ai-content-writer-seo-content-generator' ); ?></h4>
						<p><?php esc_html_e( 'Click "Run SEO Analysis" to discover your keyword rankings and opportunities.', 'wisewand-ai-content-writer-seo-content-generator' ); ?></p>

						<div class="empty-divider"><?php esc_html_e( 'OR', 'wisewand-ai-content-writer-seo-content-generator' ); ?></div>

						<div class="keyword-ideas-prompt">
							<h4>🆕 <?php esc_html_e( 'New Website? Get Keyword Ideas', 'wisewand-ai-content-writer-seo-content-generator' ); ?></h4>
							<p><?php esc_html_e( 'Enter your main topics to discover the 10 best keyword opportunities per topic', 'wisewand-ai-content-writer-seo-content-generator' ); ?></p>
							<textarea id="keyword-ideas-seeds" rows="3" placeholder="<?php esc_html_e( 'Enter main topics (one per line):\nwordpress seo\ncontent marketing\nblogging tips', 'wisewand-ai-content-writer-seo-content-generator' ); ?>"></textarea>
							<button type="button" id="get-keyword-ideas-empty" class="btn-keyword-ideas">
								🔍 <?php esc_html_e( 'Get Keyword Ideas', 'wisewand-ai-content-writer-seo-content-generator' ); ?>
							</button>
						</div>
					</div>
				</div>
			</div>
		</div>

		<!-- Reusable Modal -->
		<div id="wisewand-modal" class="wisewand-modal" style="display: none;">
			<div class="modal-overlay"></div>
			<div class="modal-container">
				<div class="modal-header">
					<h3 id="modal-title">Modal Title</h3>
					<button type="button" class="modal-close">×</button>
				</div>
				<div class="modal-body" id="modal-body">
					Modal content
				</div>
				<div class="modal-footer" id="modal-footer">
					<button type="button" class="modal-btn-secondary modal-close">Cancel</button>
					<button type="button" class="modal-btn-primary" id="modal-confirm">Confirm</button>
				</div>
			</div>
		</div>
		<?php
	}

	private function render_saved_keywords_list() {
		// This method is now only used for AJAX response to update the list after saving
		// The saved keywords are displayed in the unified table instead
		echo '<div class="saved-keywords-count-display">';

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

		$saved_manager = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );
		$stats         = $saved_manager->get_statistics();

		echo '<p style="color: #64748b; font-size: 13px; margin: 16px 0 0 0;">';
		echo '💾 Library: ' . esc_html( $stats['total'] ?? 0 ) . ' keywords';
		if ( ( $stats['by_source']['keyword_ideas'] ?? 0 ) > 0 ) {
			echo ' (' . esc_html( $stats['by_source']['keyword_ideas'] ) . ' from ideas)';
		}
		echo '</p>';
		echo '</div>';
	}

	private function get_current_domain() {
		$url    = home_url();
		$parsed = wp_parse_url( $url );
		$domain = $parsed['host'] ?? '';

		// Normalize: remove www prefix for consistency
		$domain = preg_replace( '/^www\./i', '', $domain );

		$this->logger->debug(
			'Current domain detected',
			array(
				'raw_url'           => $url,
				'normalized_domain' => $domain,
			)
		);

		return $domain;
	}

	private function enqueue_real_assets() {
		wp_enqueue_style(
			'wisewand-seo-intelligence-admin',
			WISEWAND_PLUGIN_URL . 'assets/css/admin/wisewand-seo-intelligence-admin.css',
			array(),
			WISEWAND_PLUGIN_VERSION
		);

		wp_enqueue_script(
			'wisewand-seo-intelligence-admin',
			WISEWAND_PLUGIN_URL . 'assets/js/admin/wisewand-seo-intelligence-admin.js',
			array( 'jquery' ),
			WISEWAND_PLUGIN_VERSION,
			true
		);

		// Prepare category data for JavaScript
		$all_categories = array();
		$categories = get_categories( array(
			'hide_empty' => false,
			'orderby'    => 'name'
		) );
		
		foreach ( $categories as $cat ) {
			$parent_name = '';
			if ( $cat->parent ) {
				$parent_cat = get_category( $cat->parent );
				if ( $parent_cat ) {
					$parent_name = $parent_cat->name;
				}
			}
			
			$all_categories[] = array(
				'id'          => $cat->term_id,
				'name'        => $cat->name,
				'parent'      => $cat->parent,
				'parent_name' => $parent_name
			);
		}

		wp_localize_script(
			'wisewand-seo-intelligence-admin',
			'wisewandSEO',
			array(
				'ajaxUrl'                        => admin_url( 'admin-ajax.php' ),
				'nonce'                          => wp_create_nonce( 'wisewand_seo_real' ),
				'calendarNonce'                  => wp_create_nonce( 'wisewand_calendar' ),
				'domain'                         => $this->domain,
				'adminUrl'                       => admin_url( 'admin.php' ),
				'settingsUrl'                    => admin_url( 'admin.php?page=wisewand-settings' ),
				'totalCategories'                => count( $categories ),
				'allCategories'                  => $all_categories,
				'defaultAutoGenerateInstructions'=> (bool) $this->options->get( 'wisewand_default_auto_generate_instructions', false ),
				'openaiConfigured'               => \Wisewand\Utils\OpenAI_Client::is_configured(),
			)
		);
	}
	// AJAX Handler - Simplified and Real Data Only
	public function ajax_run_real_analysis() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$domain        = sanitize_text_field( wp_unslash( $_POST['domain'] ?? '' ) );
		$language_code = sanitize_text_field( wp_unslash( $_POST['language_code'] ?? 'fr' ) );
		$location_code = absint( $_POST['location_code'] ?? 2250 );

		// Debug the configuration state
		$debug_info = array(
			'domain'                 => $domain,
			'has_dataforseo_client'  => ! empty( $this->dataforseo_client ),
			'credentials_configured' => DataForSEO_Universal::is_configured(),
		);

		$this->logger->debug( 'AJAX analysis debug info', $debug_info );

		if ( empty( $domain ) ) {
			wp_send_json_error(
				array(
					'message' => esc_html__( 'Domain is empty', 'wisewand-ai-content-writer-seo-content-generator' ),
					'debug'   => $debug_info,
				)
			);
		}

		if ( ! $this->dataforseo_client ) {
			wp_send_json_error(
				array(
					'message' => esc_html__( 'DataForSEO client not initialized', 'wisewand-ai-content-writer-seo-content-generator' ),
					'debug'   => $debug_info,
				)
			);
		}

		if ( ! DataForSEO_Universal::is_configured() ) {
			wp_send_json_error(
				array(
					'message' => esc_html__( 'DataForSEO credentials not configured', 'wisewand-ai-content-writer-seo-content-generator' ),
					'debug'   => $debug_info,
				)
			);
		}

		try {
			// First, test the DataForSEO connection with a simple request
			$test_result = $this->test_dataforseo_connection();
			if ( is_wp_error( $test_result ) ) {
				throw new \Exception( 'DataForSEO Connection Test Failed: ' . $test_result->get_error_message() );
			}

			// Run real analysis
			$analysis_results = $this->run_real_seo_analysis( $domain, $language_code, $location_code );

			// Store in database
			$this->store_analysis_results( $analysis_results );

			// Render results
			$html    = $this->render_analysis_results( $analysis_results );
			$metrics = $this->calculate_real_metrics( $analysis_results );

			wp_send_json_success(
				array(
					'html'           => $html,
					'metrics'        => $metrics,
					'domain'         => $domain,
					'total_keywords' => count( $analysis_results['current_rankings'] ) + count( $analysis_results['opportunities'] ),
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Real SEO analysis failed',
				array(
					'domain' => $domain,
					'error'  => $e->getMessage(),
				)
			);

			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	private function test_dataforseo_connection() {
		$this->logger->debug( 'Testing DataForSEO connection' );

		// Use a simple endpoint to test credentials
		$test_data = array(
			array(
				'target'        => 'google.com',
				'location_code' => 2840,
				'language_code' => 'en',
				'limit'         => 1,
			),
		);

		$response = $this->dataforseo_client->request( 'POST', '/dataforseo_labs/google/ranked_keywords/live', $test_data );

		if ( is_wp_error( $response ) ) {
			$this->logger->error(
				'DataForSEO connection test failed',
				array(
					'error_code'    => $response->get_error_code(),
					'error_message' => $response->get_error_message(),
				)
			);
			return $response;
		}

		$this->logger->debug( 'DataForSEO connection test successful' );
		return true;
	}

	private function run_real_seo_analysis( $domain, $language_code, $location_code ) {
		$this->logger->info(
			'Starting real SEO analysis',
			array(
				'domain'        => $domain,
				'language_code' => $language_code,
				'location_code' => $location_code,
			)
		);

		$results = array(
			'domain'           => $domain,
			'language_code'    => $language_code,
			'location_code'    => $location_code,
			'current_rankings' => array(),
			'opportunities'    => array(),
			'analysis_date'    => current_time( 'mysql' ),
		);

		// 1. Get current rankings
		$results['current_rankings'] = $this->fetch_real_rankings( $domain, $language_code, $location_code );

		// 2. Get opportunities
		$results['opportunities'] = $this->fetch_real_opportunities( $domain, $language_code, $location_code, $results['current_rankings'] );

		return $results;
	}

	private function fetch_real_rankings( $domain, $language_code, $location_code ) {
		// Ensure exact domain match - no www, no protocol, no trailing slash
		$clean_domain = preg_replace( '/^www\./i', '', $domain );
		$clean_domain = trim( $clean_domain, '/' );

		$ranked_data = array(
			array(
				'target'             => $clean_domain,
				'language_code'      => $language_code,
				'location_code'      => $location_code,
				'limit'              => 500,
				'include_subdomains' => false, // CRITICAL: Exclude subdomains
			),
		);

		$this->logger->debug(
			'Making DataForSEO ranked keywords request',
			array(
				'endpoint'           => '/dataforseo_labs/google/ranked_keywords/live',
				'original_domain'    => $domain,
				'clean_domain'       => $clean_domain,
				'language_code'      => $language_code,
				'location_code'      => $location_code,
				'include_subdomains' => false,
			)
		);

		$response = $this->dataforseo_client->request( 'POST', '/dataforseo_labs/google/ranked_keywords/live', $ranked_data );

		if ( is_wp_error( $response ) ) {
			$error_message = $response->get_error_message();
			$error_code    = $response->get_error_code();

			$this->logger->error(
				'DataForSEO ranked keywords request failed',
				array(
					'error_code'    => $error_code,
					'error_message' => $error_message,
					'domain'        => $domain,
				)
			);

			throw new \Exception(
				sprintf(
					'DataForSEO API Error (%s): %s',
					esc_html( $error_code ),
					esc_html( $error_message )
				)
			);
		}

		$this->logger->debug(
			'DataForSEO ranked keywords response received',
			array(
				'domain'        => $domain,
				'response_type' => gettype( $response ),
				'has_tasks'     => isset( $response[0] ) && isset( $response[0]['result'] ),
			)
		);

		$keywords = $this->process_real_rankings( $response, $domain );

		// Enrich with real volume data
		$this->enrich_with_real_volumes( $keywords, $language_code, $location_code );

		return $keywords;
	}

	private function fetch_real_opportunities( $domain, $language_code, $location_code, $existing_rankings ) {
		if ( empty( $existing_rankings ) ) {
			return array();
		}

		// Use top 5 keywords as seeds
		$seed_keywords     = array_slice( array_column( $existing_rankings, 'keyword' ), 0, 5 );
		$all_opportunities = array();

		foreach ( $seed_keywords as $seed ) {
			$suggestions_data = array(
				array(
					'keyword'              => $seed,
					'location_code'        => $location_code,
					'language_code'        => $language_code,
					'include_seed_keyword' => false,
					'limit'                => 20,
				),
			);

			$response = $this->dataforseo_client->request( 'POST', '/dataforseo_labs/google/keyword_suggestions/live', $suggestions_data );

			if ( ! is_wp_error( $response ) ) {
				$opportunities     = $this->process_real_opportunities( $response, array_column( $existing_rankings, 'keyword' ) );
				$all_opportunities = array_merge( $all_opportunities, $opportunities );
			}

			usleep( 200000 );
		}

		// Remove duplicates
		$unique_opportunities = array();
		foreach ( $all_opportunities as $opp ) {
			$unique_opportunities[ $opp['keyword'] ] = $opp;
		}

		return array_slice( $unique_opportunities, 0, 50 );
	}

	private function process_real_rankings( $response, $target_domain ) {
		$task  = $response[0] ?? array();
		$items = $task['result'][0]['items'] ?? array();

		$this->logger->debug(
			'Processing ranked keywords',
			array(
				'total_items'   => count( $items ),
				'target_domain' => $target_domain,
			)
		);

		$keywords       = array();
		$filtered_count = 0;

		foreach ( $items as $item ) {
			$keyword_data = $item['keyword_data'] ?? array();
			$serp_element = $item['ranked_serp_element'] ?? array();
			$serp_item    = $serp_element['serp_item'] ?? array();

			$keyword  = $keyword_data['keyword'] ?? '';
			$position = $serp_item['rank_absolute'] ?? $serp_item['rank_group'] ?? null;
			$page_url = $serp_item['url'] ?? '';

			if ( empty( $keyword ) || ! $position ) {
				continue;
			}

			// CRITICAL: Filter out subdomains - only keep exact domain match
			if ( ! empty( $page_url ) ) {
				$url_host = wp_parse_url( $page_url, PHP_URL_HOST );

				// Normalize both domains (remove www if present)
				$normalized_url_host = preg_replace( '/^www\./i', '', $url_host );
				$normalized_target   = preg_replace( '/^www\./i', '', $target_domain );

				// Skip if the URL domain doesn't exactly match the target domain
				if ( $normalized_url_host !== $normalized_target ) {
					++$filtered_count;
					continue;
				}
			}

			$keywords[] = array(
				'keyword'       => $keyword,
				'position'      => $position,
				'page_url'      => $page_url,
				'search_volume' => 0, // Will be enriched
				'content_type'  => $this->detect_content_type( $keyword ),
				'search_intent' => $this->detect_search_intent( $keyword ),
				'priority'      => $this->calculate_priority( $position, 0 ),
				'type'          => 'current',
			);
		}

		$this->logger->info(
			'Ranked keywords processed',
			array(
				'total_items'   => count( $items ),
				'filtered_out'  => $filtered_count,
				'kept_keywords' => count( $keywords ),
				'target_domain' => $target_domain,
			)
		);

		return $keywords;
	}

	private function process_real_opportunities( $response, $existing_keywords ) {
		$task  = $response[0] ?? array();
		$items = $task['result'] ?? array();

		$opportunities = array();
		foreach ( $items as $item ) {
			$keyword       = $item['keyword'] ?? '';
			$search_volume = $item['search_volume'] ?? 0;

			if ( empty( $keyword ) || in_array( $keyword, $existing_keywords ) ) {
				continue;
			}

			$opportunities[] = array(
				'keyword'       => $keyword,
				'position'      => null,
				'page_url'      => '',
				'search_volume' => $search_volume,
				'content_type'  => $this->detect_content_type( $keyword ),
				'search_intent' => $this->detect_search_intent( $keyword ),
				'priority'      => $this->calculate_priority( null, $search_volume ),
				'type'          => 'opportunity',
			);
		}

		return $opportunities;
	}

	private function enrich_with_real_volumes( &$keywords, $language_code, $location_code ) {
		if ( empty( $keywords ) ) {
			return;
		}

		$keyword_names = array_column( $keywords, 'keyword' );

		foreach ( array_chunk( $keyword_names, 20 ) as $batch ) {
			$volume_data = array(
				array(
					'location_code' => $location_code,
					'language_code' => $language_code,
					'keywords'      => $batch,
				),
			);

			$response = $this->dataforseo_client->request( 'POST', '/keywords_data/google_ads/search_volume/live', $volume_data );

			if ( ! is_wp_error( $response ) ) {
				$this->update_with_real_volumes( $keywords, $response );
			}

			usleep( 300000 );
		}
	}

	private function update_with_real_volumes( &$keywords, $volume_response ) {
		$task  = $volume_response[0] ?? array();
		$items = $task['result'] ?? array();

		foreach ( $items as $item ) {
			$keyword       = $item['keyword'] ?? '';
			$search_volume = $item['search_volume'] ?? 0;

			foreach ( $keywords as &$kw ) {
				if ( $kw['keyword'] === $keyword ) {
					$kw['search_volume'] = $search_volume;
					$kw['priority']      = $this->calculate_priority( $kw['position'], $search_volume );
					break;
				}
			}
		}
	}



	private function store_analysis_results( $results ) {
		$this->logger->info(
			'Storing SEO analysis results',
			array(
				'domain'        => $results['domain'],
				'keyword_count' => count( array_merge( $results['current_rankings'], $results['opportunities'] ) ),
			)
		);

		$success = $this->seo_repository->store_analysis_results( $results );

		if ( $success ) {
			$this->logger->info( 'SEO analysis results stored successfully' );
		} else {
			$this->logger->error( 'Failed to store SEO analysis results: ' . $this->seo_repository->get_last_error() );
		}
	}

	private function load_latest_snapshot( $domain, $language_code, $location_code ) {
		// Get the latest snapshot ID
		$latest_snapshot = $this->seo_repository->get_latest_snapshot_id( $domain, $language_code, $location_code );

		if ( ! $latest_snapshot ) {
			return null;
		}

		// Load all keywords from latest snapshot
		$keywords = $this->seo_repository->get_keywords_by_snapshot( $latest_snapshot );

		if ( empty( $keywords ) ) {
			return null;
		}

		// Get previous snapshot for comparison
		$previous_snapshot = $this->seo_repository->get_previous_snapshot_id( $domain, $language_code, $location_code, $latest_snapshot );

		// Load previous snapshot data if exists
		$previous_data = array();
		if ( $previous_snapshot ) {
			$previous_keywords = $this->seo_repository->get_keywords_by_snapshot( $previous_snapshot );

			foreach ( $previous_keywords as $kw ) {
				$previous_data[ $kw['keyword'] ] = $kw['position_abs'];
			}
		}

		// Transform database format to analysis format with comparison
		$current_rankings = array();
		$opportunities    = array();

		foreach ( $keywords as $kw ) {
			$keyword_data = array(
				'keyword'       => $kw['keyword'],
				'position'      => $kw['position_abs'],
				'page_url'      => $kw['page_url'],
				'search_volume' => $kw['search_volume'],
				'content_type'  => $kw['content_type'],
				'search_intent' => $kw['search_intent'],
				'priority'      => $kw['priority_score'],
				'type'          => $kw['keyword_type'],
				'language_code' => $kw['language_code'] ?? 'en', // Add language code
			);

			// Add position change comparison
			if ( isset( $previous_data[ $kw['keyword'] ] ) ) {
				$old_position = $previous_data[ $kw['keyword'] ];
				$new_position = $kw['position_abs'];

				if ( $old_position && $new_position ) {
					$keyword_data['position_change']   = $old_position - $new_position; // Positive = improved
					$keyword_data['previous_position'] = $old_position;
				}
			} else {
				$keyword_data['is_new'] = true; // New keyword
			}

			if ( $kw['keyword_type'] === 'current' ) {
				$current_rankings[] = $keyword_data;
			} else {
				$opportunities[] = $keyword_data;
			}
		}

		// Get snapshot date
		$snapshot_date = $this->seo_repository->get_snapshot_date( $latest_snapshot );

		return array(
			'domain'           => $domain,
			'language_code'    => $language_code,
			'location_code'    => $location_code,
			'current_rankings' => $current_rankings,
			'opportunities'    => $opportunities,
			'analysis_date'    => $snapshot_date,
			'snapshot_id'      => $latest_snapshot,
		);
	}

	private function render_analysis_results( $results ) {
		$all_keywords = array();

		// Get language_code from top-level results
		$language_code = $results['language_code'] ?? 'en';

		// Add current rankings (these have positions and should show as rankings)
		foreach ( $results['current_rankings'] as $ranking ) {
			$ranking['type']          = 'current'; // Ensure correct type
			$ranking['can_edit']      = false; // Rankings can't be edited
			$ranking['can_delete']    = false; // Rankings can't be deleted
			$ranking['language_code'] = $language_code; // Add language code
			$all_keywords[]           = $ranking;
		}

		// Add opportunities (these are potential rankings)
		foreach ( $results['opportunities'] as $opportunity ) {
			$opportunity['type']          = 'opportunity'; // Ensure correct type
			$opportunity['can_edit']      = false;
			$opportunity['can_delete']    = false;
			$opportunity['language_code'] = $language_code; // Add language code
			$all_keywords[]               = $opportunity;
		}

		// Add saved keywords to the results (separate from rankings)
		if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
		}

		$saved_manager  = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );
		$saved_keywords = $saved_manager->get_all_saved_keywords();

		$this->logger->debug(
			'Loading saved keywords for table',
			array(
				'total_saved_keywords' => count( $saved_keywords ),
				'enriched_count'       => count( array_filter( $saved_keywords, fn( $k ) => $k['is_enriched'] ) ),
				'sample_saved_keyword' => ! empty( $saved_keywords[0] ) ? array(
					'keyword'        => $saved_keywords[0]['keyword'],
					'is_enriched'    => $saved_keywords[0]['is_enriched'],
					'search_volume'  => $saved_keywords[0]['search_volume'],
					'difficulty'     => $saved_keywords[0]['difficulty'] ?? null,
					'keyword_source' => $saved_keywords[0]['keyword_source'],
					'tags'           => $saved_keywords[0]['tags'],
				) : null,
			)
		);

		foreach ( $saved_keywords as $saved_kw ) {
			// REMOVED is_enriched filter - show ALL saved keywords regardless of enrichment status
			$type = 'saved'; // default
			if ( $saved_kw['keyword_source'] === 'keyword_ideas' ) {
				$type = 'ideas';
			} elseif ( $saved_kw['keyword_source'] === 'competitor_spy' ) {
				$type = 'competitor_spy';
			} elseif ( $saved_kw['tags'] === 'seed_keyword' ) {
				$type = 'seed';
			}

			$all_keywords[] = array(
				'keyword'              => $saved_kw['keyword'],
				'keyword_id'           => $saved_kw['id'],
				'position'             => null,
				'page_url'             => '',
				'search_volume'        => $saved_kw['search_volume'] ?? 0,
				'difficulty'           => $saved_kw['difficulty'] ?? null,
				'cpc'                  => $saved_kw['cpc'] ?? null,
				'competition'          => $saved_kw['competition'] ?? null,
				'content_type'         => $saved_kw['content_type'],
				'search_intent'        => $saved_kw['search_intent'],
				'priority'             => $saved_kw['priority_score'] ?? 0,
				'type'                 => $type,
				'language_code'        => $saved_kw['language_code'] ?? 'en',
				'seed_keyword'         => $saved_kw['seed_keyword'], // For showing source
				'suggested_author_id'  => $saved_kw['suggested_author_id'] ?? null, // AI matching
				'suggested_categories' => $saved_kw['suggested_categories'] ?? null, // AI matching
				'ai_matched_at'        => $saved_kw['ai_matched_at'] ?? null, // AI matching
				'ai_model_used'        => $saved_kw['ai_model_used'] ?? null, // AI matching
				'can_edit'             => true,
				'can_delete'           => true,
			);

			$this->logger->debug(
				'Added saved keyword to table',
				array(
					'keyword'     => $saved_kw['keyword'],
					'type'        => $type,
					'is_enriched' => $saved_kw['is_enriched'] ?? 0,
					'volume'      => $saved_kw['search_volume'] ?? 0,
				)
			);
		}

		// Enrich keywords with calendar status
		$all_keywords = $this->enrich_with_calendar_status( $all_keywords );

		if ( empty( $all_keywords ) ) {
			return '<div class="empty-state"><div class="empty-icon">📊</div><h4>No data found</h4><p>No keywords found for this domain in the selected market.</p></div>';
		}

		// Debug: Log sample keyword data to verify structure
		$this->logger->debug(
			'Table rendering sample data',
			array(
				'total_keywords' => count( $all_keywords ),
				'sample_keyword' => ! empty( $all_keywords[0] ) ? array(
					'keyword'       => $all_keywords[0]['keyword'] ?? 'missing',
					'type'          => $all_keywords[0]['type'] ?? 'missing',
					'search_volume' => $all_keywords[0]['search_volume'] ?? 'missing',
					'difficulty'    => $all_keywords[0]['difficulty'] ?? 'missing',
					'priority'      => $all_keywords[0]['priority'] ?? 'missing',
				) : 'no_keywords',
			)
		);

		// Show last updated date if available
		$date_info = '';
		if ( isset( $results['analysis_date'] ) ) {
			$date_info = '<div class="analysis-date">📅 Last updated: ' . gmdate( 'F j, Y @ g:i A', strtotime( $results['analysis_date'] ) ) . '</div>';
		}

		$html = $date_info;

		// Top bulk actions bar
		$html .= '<div class="bulk-actions-bar bulk-actions-top" id="bulk-actions-top" style="display: none;">';
		$html .= '<div class="bulk-info"><span class="selected-count-display">0</span> keywords selected</div>';
		$html .= '<button type="button" class="bulk-enrich-btn btn-bulk-action">📊 Enrich</button>';
		$html .= '<button type="button" class="bulk-schedule-btn btn-bulk-action">📅 Schedule</button>';
		$html .= '<button type="button" class="bulk-delete-btn btn-bulk-action btn-danger">🗑️ Delete</button>';
		$html .= '</div>';

		$html .= '<div class="table-responsive">';
		$html .= '<table class="seo-table">';
		$html .= '<thead><tr>';
		$html .= '<th class="col-checkbox"><input type="checkbox" id="select-all-keywords"></th>';
		$html .= '<th class="sortable col-keyword" data-sort="keyword">🎯 Keyword <span class="sort-arrow"></span></th>';
		$html .= '<th class="col-source">🏷️ Source</th>';
		$html .= '<th class="col-author" data-column="author">👤 Author</th>';
		$html .= '<th class="sortable col-categories" data-sort="categories" data-column="categories">📁 Categories <span class="sort-arrow"></span></th>';
		$html .= '<th class="col-language" data-column="language">🌍 Lang</th>';
		$html .= '<th class="sortable col-position" data-sort="position" data-column="position">📍 Position <span class="sort-arrow"></span></th>';
		$html .= '<th class="col-change" data-column="change">📈 Change</th>';
		$html .= '<th class="col-page" data-column="page">📄 Page</th>';
		$html .= '<th class="sortable col-volume" data-sort="volume" data-column="volume">📊 Volume <span class="sort-arrow"></span></th>';
		$html .= '<th class="sortable col-difficulty" data-sort="difficulty" data-column="difficulty">⚡ Difficulty <span class="sort-arrow"></span></th>';
		$html .= '<th class="sortable col-cpc" data-sort="cpc" data-column="cpc">💰 CPC <span class="sort-arrow"></span></th>';
		$html .= '<th class="col-competition" data-column="competition">🎯 Comp</th>';
		$html .= '<th class="col-intent" data-column="intent">🧠 Intent</th>';
		$html .= '<th class="sortable col-priority" data-sort="priority" data-column="priority">🎯 Priority <span class="sort-arrow"></span></th>';
		$html .= '<th class="col-calendar" data-column="calendar">📅 Calendar</th>';
		$html .= '<th class="col-action">🚀 Actions</th>';
		$html .= '</tr></thead><tbody>';

		// ========================================
		// PERFORMANCE OPTIMIZATION: Cache Data Before Loop
		// ========================================
		// Fetch users ONCE instead of 788+ times (massive performance boost!)
		$all_users = get_users(
			array(
				'capability' => 'edit_posts',
				'orderby'    => 'display_name',
				'order'      => 'ASC',
			)
		);

		// Fetch all categories ONCE and create fast lookup map
		// IMPORTANT: Load ALL categories from ALL languages for complete mapping
		$category_map = array(); // [category_id => category_name]

		if ( isset( $GLOBALS['sitepress'] ) ) {
			// WPML is active - get categories from ALL languages
			global $sitepress;

			// Get all active languages
			$active_languages = $sitepress->get_active_languages();

			if ( ! empty( $active_languages ) ) {
				// Load categories from each language
				foreach ( $active_languages as $lang_code => $lang_data ) {
					// Switch to this language
					$sitepress->switch_lang( $lang_code );

					// Get categories in this language
					$lang_categories = get_categories( array( 'hide_empty' => false ) );

					// Add to map
					foreach ( $lang_categories as $cat ) {
						$category_map[ $cat->term_id ] = $cat->name;
					}
				}

				// Switch back to current language
				$sitepress->switch_lang( $sitepress->get_current_language() );
			} else {
				// Fallback if no languages configured
				$all_categories = get_categories( array( 'hide_empty' => false ) );
				foreach ( $all_categories as $cat ) {
					$category_map[ $cat->term_id ] = $cat->name;
				}
			}
		} else {
			// No WPML - just get all categories normally
			$all_categories = get_categories( array( 'hide_empty' => false ) );
			foreach ( $all_categories as $cat ) {
				$category_map[ $cat->term_id ] = $cat->name;
			}
		}

		// Performance gain: From ~3,152 queries to 2 queries per page load!
		// ========================================

		foreach ( $all_keywords as $kw ) {
			$keyword_id = $kw['keyword_id'] ?? 0;
			$can_edit   = $kw['can_edit'] ?? false;
			$can_delete = $kw['can_delete'] ?? false;

			$html .= '<tr data-type="' . $kw['type'] . '" data-language="' . ( $kw['language_code'] ?? 'en' ) . '" data-keyword-id="' . $keyword_id . '">';

			// Checkbox
			$html .= '<td class="col-checkbox">';
			if ( $can_edit || $can_delete ) {
				$html .= '<input type="checkbox" class="select-keyword-row" value="' . $keyword_id . '" data-type="' . $kw['type'] . '">';
			}
			$html .= '</td>';

			// Keyword (clean - no badges)
			$html .= '<td class="col-keyword">';
			$html .= '<strong>' . esc_html( $kw['keyword'] ) . '</strong>';

			// Show seed source for idea keywords (discrete)
			if ( $kw['type'] === 'ideas' && ! empty( $kw['seed_keyword'] ) ) {
				$html .= '<br><span class="seed-source">from: ' . esc_html( $kw['seed_keyword'] ) . '</span>';
			}

			$html .= '</td>';

			// Source column (badges only)
			$html .= '<td class="col-source">';
			if ( $kw['type'] === 'current' ) {
				$html .= '<span class="keyword-source-badge badge-ranking">📊 Ranking</span>';
			} elseif ( $kw['type'] === 'opportunity' ) {
				$html .= '<span class="keyword-source-badge badge-opportunity">🔍 Opportunity</span>';
			} elseif ( $kw['type'] === 'saved' ) {
				$html .= '<span class="keyword-source-badge badge-manual">💾 Manual</span>';
			} elseif ( $kw['type'] === 'competitor_spy' ) {
				$html .= '<span class="keyword-source-badge badge-competitor">🕵️ Competitor</span>';
			} elseif ( $kw['type'] === 'ideas' ) {
				$html .= '<span class="keyword-source-badge badge-idea">🆕 Idea</span>';
			} elseif ( $kw['type'] === 'seed' ) {
				$html .= '<span class="keyword-source-badge badge-seed">🌱 Seed</span>';
			}
			$html .= '</td>';

			// Author column (editable dropdown)
			$html .= '<td class="col-author" data-column-target="author">';

			// Get current author with proper fallback chain
			$author_id = $kw['suggested_author_id'] ?? null;
			if ( ! $author_id ) {
				$lang      = $kw['language_code'] ?? 'en';
				$author_id = $this->options->get_author_for_language( $lang );
			}

			// Build dropdown using CACHED $all_users (no query!)
			$html .= '<select class="keyword-author-select" data-keyword-id="' . $keyword_id . '" style="font-size: 11px; padding: 3px 6px; border: 1px solid #d1d5db; border-radius: 4px; background: #ffffff; width: 100%; max-width: 120px;">';
			foreach ( $all_users as $user ) {
				$selected = ( $user->ID == $author_id ) ? 'selected' : '';
				$html    .= '<option value="' . $user->ID . '" ' . $selected . '>' . esc_html( $user->display_name ) . '</option>';
			}
			$html .= '</select>';
			$html .= '</td>';

			// Categories column (click to open modal)
			$html .= '<td class="col-categories" data-column-target="categories">';

			// Get current categories using CACHED $category_map (no queries!)
			$suggested_categories = $kw['suggested_categories'] ?? null;
			$current_category_ids = array();
			$cat_names            = array();

			if ( $suggested_categories ) {
				$current_category_ids = json_decode( $suggested_categories, true ) ?: array();
				foreach ( $current_category_ids as $cat_id ) {
					// Fast lookup in pre-fetched array instead of database query
					if ( isset( $category_map[ $cat_id ] ) ) {
						$cat_names[] = $category_map[ $cat_id ];
					}
				}
			}

			// Display as clickable badges
			$html .= '<div class="open-category-modal" data-keyword-id="' . $keyword_id . '" data-category-ids="' . esc_attr( wp_json_encode( $current_category_ids ) ) . '" style="cursor: pointer; min-height: 24px; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; padding: 4px;">';

			if ( ! empty( $cat_names ) ) {
				foreach ( array_slice( $cat_names, 0, 2 ) as $name ) {
					$html .= '<span style="font-size: 10px; color: #1e293b; background: #e0f2fe; padding: 2px 6px; border-radius: 4px;">' . esc_html( $name ) . '</span>';
				}
				if ( count( $cat_names ) > 2 ) {
					$html .= '<span style="font-size: 10px; color: #64748b;">+' . ( count( $cat_names ) - 2 ) . '</span>';
				}
				$html .= '<span style="font-size: 12px; color: var(--brand-primary); margin-left: 4px;">✏️</span>';
			} else {
				$html .= '<span style="font-size: 10px; color: #94a3b8;">None</span>';
				$html .= '<span style="font-size: 10px; color: var(--brand-primary); margin-left: 4px;">+ Add</span>';
			}

			$html .= '</div>';
			$html .= '</td>';

			// Language column
			$html       .= '<td class="col-language" data-column-target="language">';
			$lang_code   = $kw['language_code'] ?? 'en';
			$lang_emojis = array(
				'en' => '🇺🇸',
				'fr' => '🇫🇷',
				'de' => '🇩🇪',
				'es' => '🇪🇸',
				'it' => '🇮🇹',
			);
			$emoji       = $lang_emojis[ $lang_code ] ?? '🌍';
			$html       .= '<span style="font-size: 18px;" title="' . strtoupper( $lang_code ) . '">' . $emoji . '</span>';
			$html       .= '</td>';

			// Position column (positions only)
			$html .= '<td class="col-position" data-column-target="position" data-position="' . ( $kw['position'] ?? 999 ) . '">';
			if ( $kw['position'] ) {
				$pos_class = $kw['position'] <= 10 ? 'pos-top-10' : ( $kw['position'] <= 20 ? 'pos-top-20' : 'pos-top-50' );
				$html     .= '<span class="position-badge ' . $pos_class . '">#' . $kw['position'] . '</span>';
			} else {
				$html .= '<span style="color: #94a3b8;">—</span>';
			}
			$html .= '</td>';

			// Position Change
			$html .= '<td class="col-change" data-column-target="change">';
			if ( isset( $kw['is_new'] ) && $kw['is_new'] ) {
				$html .= '<span class="change-badge new-keyword">🆕 New</span>';
			} elseif ( isset( $kw['position_change'] ) ) {
				$change = $kw['position_change'];
				if ( $change > 0 ) {
					$html .= '<span class="change-badge change-up">↗️ +' . abs( $change ) . '</span>';
				} elseif ( $change < 0 ) {
					$html .= '<span class="change-badge change-down">↘️ ' . $change . '</span>';
				} else {
					$html .= '<span class="change-badge change-stable">➡️ 0</span>';
				}
			} else {
				$html .= '<span style="color: #94a3b8;">—</span>';
			}
			$html .= '</td>';

			// Page
			$html .= '<td class="col-page" data-column-target="page">';
			if ( $kw['page_url'] ) {
				$path       = wp_parse_url( $kw['page_url'], PHP_URL_PATH ) ?: '/';
				$short_path = strlen( $path ) > 30 ? substr( $path, 0, 27 ) . '...' : $path;
				$html      .= '<a href="' . esc_url( $kw['page_url'] ) . '" target="_blank" class="page-link" title="' . esc_attr( $path ) . '">' . esc_html( $short_path ) . '</a>';
			} else {
				$html .= '<span style="color: #94a3b8;">New content</span>';
			}
			$html .= '</td>';

			// Volume
			$html .= '<td class="col-volume" data-column-target="volume" data-volume="' . ( $kw['search_volume'] ?? 0 ) . '">';
			$html .= ( $kw['search_volume'] ?? 0 ) > 0 ? '<strong>' . number_format( $kw['search_volume'] ) . '</strong>' : '—';
			$html .= '</td>';

			// Difficulty
			$html .= '<td class="col-difficulty" data-column-target="difficulty" data-difficulty="' . ( $kw['difficulty'] ?? 0 ) . '">';
			if ( isset( $kw['difficulty'] ) && $kw['difficulty'] > 0 ) {
				$diff_class = $kw['difficulty'] < 30 ? 'diff-easy' : ( $kw['difficulty'] < 60 ? 'diff-medium' : 'diff-hard' );
				$html      .= '<span class="difficulty-badge ' . $diff_class . '">' . $kw['difficulty'] . '%</span>';
			} else {
				$html .= '<span style="color: #94a3b8;">—</span>';
			}
			$html .= '</td>';

			// CPC
			$html .= '<td class="col-cpc" data-column-target="cpc" data-cpc="' . ( $kw['cpc'] ?? 0 ) . '">';
			if ( isset( $kw['cpc'] ) && $kw['cpc'] > 0 ) {
				$html .= '<span style="color: var(--brand-primary); font-weight: 600;">💰 $' . number_format( $kw['cpc'], 2 ) . '</span>';
			} else {
				$html .= '<span style="color: #94a3b8;">—</span>';
			}
			$html .= '</td>';

			// Competition
			$html            .= '<td class="col-competition" data-column-target="competition">';
			$competition_text = $kw['competition'] ?? '';
			if ( $competition_text ) {
				$comp_badge_class = '';
				$comp_icon        = '';
				switch ( strtoupper( $competition_text ) ) {
					case 'LOW':
						$comp_badge_class = 'diff-easy';
						$comp_icon        = '🟢';
						break;
					case 'MEDIUM':
						$comp_badge_class = 'diff-medium';
						$comp_icon        = '🟡';
						break;
					case 'HIGH':
						$comp_badge_class = 'diff-hard';
						$comp_icon        = '🔴';
						break;
				}
				$html .= '<span class="difficulty-badge ' . $comp_badge_class . '">' . $comp_icon . ' ' . ucfirst( strtolower( $competition_text ) ) . '</span>';
			} else {
				$html .= '<span style="color: #94a3b8;">—</span>';
			}
			$html .= '</td>';

			// Intent
			$html .= '<td class="col-intent" data-column-target="intent"><span class="intent-badge">' . ucfirst( $kw['search_intent'] ?? 'informational' ) . '</span></td>';

			// Priority
			$html          .= '<td class="col-priority" data-priority="' . ( $kw['priority'] ?? 0 ) . '">';
			$priority_class = ( $kw['priority'] ?? 0 ) >= 70 ? 'priority-high' : ( ( $kw['priority'] ?? 0 ) >= 40 ? 'priority-medium' : 'priority-low' );
			$html          .= '<span class="priority-badge ' . $priority_class . '">' . round( $kw['priority'] ?? 0 ) . '</span>';
			$html          .= '</td>';
			// Calendar Status
			$html .= '<td class="col-calendar" data-column-target="calendar">';
			if ( ! empty( $kw['calendar_status'] ) ) {
				$cal_status = $kw['calendar_status'];
				$date       = gmdate( 'M j', strtotime( $kw['calendar_date'] ) );
				$tooltip    = 'Scheduled: ' . $date;
				if ( $kw['calendar_time'] ) {
					$tooltip .= ' at ' . gmdate( 'g:i A', strtotime( $kw['calendar_time'] ) );
				}
				$tooltip .= "\nStatus: " . $cal_status['label'];
				if ( $kw['calendar_post_id'] ) {
					$tooltip .= "\nPost ID: " . $kw['calendar_post_id'];
				}

				$html .= '<div style="display: flex; align-items: center; gap: 4px; justify-content: center;">';
				$html .= '<span style="cursor: help; font-size: 11px;" title="' . esc_attr( $tooltip ) . '">';
				$html .= '<span style="font-size: 14px;">' . $cal_status['icon'] . '</span> ';
				$html .= '<span style="color: ' . $cal_status['color'] . '; font-weight: 600;">' . $date . '</span>';
				$html .= '</span>';
				$html .= '<button class="btn-remove-from-calendar" data-calendar-id="' . ( $kw['calendar_id'] ?? 0 ) . '" data-keyword="' . esc_attr( $kw['keyword'] ) . '" title="Remove from calendar" style="background: none; border: none; cursor: pointer; padding: 2px 4px; font-size: 12px; color: #EF4444;">🗑️</button>';
				$html .= '</div>';
			} else {
				$html .= '<span style="color: #64748b; font-size: 11px;">—</span>';
			}
			$html .= '</td>';

			// Actions - Edit + Schedule + Delete
			$html .= '<td class="col-action">';
			$html .= '<div class="action-buttons">';

			// Edit button (only for saved/ideas keywords)
			if ( $can_edit && $keyword_id ) {
				$html .= '<button class="action-btn btn-edit" data-keyword-id="' . $keyword_id . '" title="Edit">✏️</button>';
			}

			$keyword_json = htmlspecialchars(
				wp_json_encode(
					array(
						'keyword'       => $kw['keyword'],
						'keyword_id'    => $keyword_id,
						'position'      => $kw['position'] ?? null,
						'search_volume' => $kw['search_volume'] ?? 0,
						'difficulty'    => $kw['difficulty'] ?? null,
						'search_intent' => $kw['search_intent'] ?? 'informational',
						'content_type'  => $kw['content_type'] ?? 'blog',
						'priority'      => $kw['priority'] ?? 0,
						'page_url'      => $kw['page_url'] ?? '',
						'type'          => $kw['type'],
					)
				),
				ENT_QUOTES,
				'UTF-8'
			);

			// Schedule button (all keywords)
			$html .= '<button class="action-btn btn-schedule" data-keyword-data="' . $keyword_json . '" title="Schedule">📅</button>';

			// Delete button (only for saved/ideas keywords)
			if ( $can_delete && $keyword_id ) {
				$html .= '<button class="action-btn btn-delete-keyword" data-keyword-id="' . $keyword_id . '" data-keyword="' . esc_attr( $kw['keyword'] ) . '" title="Delete">🗑️</button>';
			}

			$html .= '</div>';
			$html .= '</td>';

			$html .= '</tr>';
		}

		$html .= '</tbody></table>';
		$html .= '</div>'; // Close table-responsive

		// Bottom bulk actions bar
		$html .= '<div class="bulk-actions-bar bulk-actions-bottom" id="bulk-actions-bottom" style="display: none;">';
		$html .= '<div class="bulk-info"><span class="selected-count-display">0</span> keywords selected</div>';
		$html .= '<button type="button" class="bulk-enrich-btn btn-bulk-action">📊 Enrich</button>';
		$html .= '<button type="button" class="bulk-schedule-btn btn-bulk-action">📅 Schedule</button>';
		$html .= '<button type="button" class="bulk-delete-btn btn-bulk-action btn-danger">🗑️ Delete</button>';
		$html .= '</div>';

		return $html;
	}

	private function calculate_real_metrics( $results ) {
		$current_count       = count( $results['current_rankings'] );
		$opportunities_count = count( $results['opportunities'] );

		$quick_wins = count(
			array_filter(
				$results['current_rankings'],
				function ( $k ) {
					return ( $k['position'] ?? 999 ) >= 11 && ( $k['position'] ?? 999 ) <= 20;
				}
			)
		);

		$top_performers = count(
			array_filter(
				$results['current_rankings'],
				function ( $k ) {
					return ( $k['position'] ?? 999 ) <= 10;
				}
			)
		);

		return array(
			'current_rankings'  => $current_count,
			'new_opportunities' => $opportunities_count,
			'quick_wins'        => $quick_wins,
			'top_performers'    => $top_performers,
		);
	}

	// Helper Methods (DRY)
	private function detect_content_type( $keyword ) {
		$kw = strtolower( $keyword );
		if ( preg_match( '/\b(acheter|prix|avis|meilleur|comparaison)\b/', $kw ) ) {
			return 'affiliate';
		}
		if ( preg_match( '/\b(app|outil|générateur|logiciel)\b/', $kw ) ) {
			return 'product';
		}
		if ( preg_match( '/\b(catégorie|collection|liste)\b/', $kw ) ) {
			return 'category';
		}
		return 'blog';
	}

	private function detect_search_intent( $keyword ) {
		$kw = strtolower( $keyword );
		if ( preg_match( '/\b(acheter|prix|gratuit)\b/', $kw ) ) {
			return 'transactional';
		}
		if ( preg_match( '/\b(meilleur|avis|comparaison)\b/', $kw ) ) {
			return 'commercial';
		}
		return 'informational';
	}

	private function calculate_priority( $position = null, $search_volume = 0 ) {
		if ( $position ) {
			$position_score  = max( 0, 100 - $position );
			$volume_score    = min( $search_volume / 100, 20 );
			$quick_win_bonus = ( $position >= 11 && $position <= 20 ) ? 25 : 0;
			return max( 0, round( $position_score + $volume_score + $quick_win_bonus ) );
		} else {
			$volume_score      = min( $search_volume / 100, 60 );
			$opportunity_bonus = 20;
			return max( 0, round( $volume_score + $opportunity_bonus ) );
		}
	}

	// AJAX Handler - Load Latest Snapshot
	public function ajax_load_latest_snapshot() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$domain        = sanitize_text_field( wp_unslash( $_POST['domain'] ?? '' ) );
		$language_code = sanitize_text_field( wp_unslash( $_POST['language_code'] ?? 'fr' ) );
		$location_code = absint( $_POST['location_code'] ?? 2250 );

		if ( empty( $domain ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Domain is required', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
			// Load latest snapshot from database
			$snapshot_data = $this->load_latest_snapshot( $domain, $language_code, $location_code );

			// If no snapshot, create empty structure but STILL show saved keywords
			if ( ! $snapshot_data ) {
				$snapshot_data = array(
					'domain'           => $domain,
					'language_code'    => $language_code,
					'location_code'    => $location_code,
					'current_rankings' => array(),
					'opportunities'    => array(),
					'analysis_date'    => null,
				);
			}

			// Check if we have saved keywords
			if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
			}
			$saved_manager  = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );
			$saved_keywords = $saved_manager->get_all_saved_keywords();
			$has_saved      = count( $saved_keywords ) > 0; // Show ALL saved keywords, enriched or not

			// If no snapshot AND no saved keywords, show empty state
			if ( empty( $snapshot_data['current_rankings'] ) && empty( $snapshot_data['opportunities'] ) && ! $has_saved ) {
				wp_send_json_success(
					array(
						'html'       => '',
						'metrics'    => array(
							'current_rankings'  => 0,
							'new_opportunities' => 0,
							'quick_wins'        => 0,
							'top_performers'    => 0,
						),
						'show_empty' => true,
					)
				);
				return;
			}

			// Render results (will include saved keywords automatically)
			$html    = $this->render_analysis_results( $snapshot_data );
			$metrics = $this->calculate_real_metrics( $snapshot_data );

			wp_send_json_success(
				array(
					'html'           => $html,
					'metrics'        => $metrics,
					'domain'         => $domain,
					'total_keywords' => count( $snapshot_data['current_rankings'] ) + count( $snapshot_data['opportunities'] ) + count( $saved_keywords ),
					'snapshot_date'  => $snapshot_data['analysis_date'] ?? null,
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Failed to load latest snapshot',
				array(
					'domain' => $domain,
					'error'  => $e->getMessage(),
				)
			);

			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Schedule Keyword for Publication
	public function ajax_schedule_keyword() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keyword_data = isset( $_POST['keyword_data'] ) ? map_deep( wp_unslash( $_POST['keyword_data'] ), 'sanitize_text_field' ) : array();

		if ( empty( $keyword_data['keyword'] ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Keyword is required', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

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

			$payload_builder = new \Wisewand\Utils\Wisewand_Payload_Builder( $this->options, $this->logger );
			
			if ( ! class_exists( 'Wisewand\\Repositories\\Calendar_Repository' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Repositories/Calendar_Repository.php';
			}
			$calendar_repository = new \Wisewand\Repositories\Calendar_Repository();
			
			$entry_manager   = new \Wisewand\Calendar\Calendar_Entry_Manager( $this->options, $this->logger, $payload_builder, $calendar_repository );

			// Check if keyword already scheduled
			$existing = $entry_manager->check_keyword_exists( $keyword_data['keyword'] );
			if ( $existing ) {
				wp_send_json_error(
					array(
						'message'       => sprintf(
							/* translators: %s: Publication date in format YYYY-MM-DD */
							esc_html__( 'This keyword is already scheduled for publication on %s', 'wisewand-ai-content-writer-seo-content-generator' ),
							esc_html( $existing['publication_date'] )
						),
						'existing_id'   => $existing['id'],
						'existing_date' => $existing['publication_date'],
					)
				);
				return;
			}

			// Find next available date
		$upcoming_count = $this->calendar_repository->get_upcoming_count_from_date( gmdate( 'Y-m-d' ) );

			$next_date = gmdate( 'Y-m-d', strtotime( '+' . ( $upcoming_count + 1 ) . ' days' ) );

			// Build Wisewand payload
			$wisewand_payload = $payload_builder->build_payload( $keyword_data );

			// Create calendar entry
			$entry_id = $entry_manager->create_entry(
				array(
					'keyword'                => $keyword_data['keyword'],
					'keyword_source'         => 'current_ranking',
					'search_volume'          => $keyword_data['search_volume'] ?? 0,
					'current_position'       => $keyword_data['position'] ?? null,
					'search_intent'          => $keyword_data['search_intent'] ?? 'informational',
					'priority_score'         => $keyword_data['priority'] ?? 0,
					'publication_action'     => 'create',
					'wisewand_payload'       => wp_json_encode( $wisewand_payload ),
					'wisewand_lang'          => $wisewand_payload['lang'],
					'wisewand_type'          => $wisewand_payload['type'],
					'wisewand_length'        => $wisewand_payload['length'],
					'publication_date'       => $next_date,
					'publication_time_range' => 'random',
					'target_post_type'       => 'post',
					'target_author'          => $this->options->get( 'wisewand_calendar_default_author', 1 ),
					'created_by'             => get_current_user_id(),
				)
			);

			if ( is_wp_error( $entry_id ) ) {
				wp_send_json_error( array( 'message' => esc_html( $entry_id->get_error_message() ) ) );
				return;
			}

			wp_send_json_success(
				array(
					'entry_id'         => $entry_id,
					'publication_date' => $next_date,
					'keyword'          => $keyword_data['keyword'],
					'message'          => esc_html__( 'Keyword scheduled successfully!', 'wisewand-ai-content-writer-seo-content-generator' ),
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Failed to schedule keyword',
				array(
					'keyword' => $keyword_data['keyword'] ?? 'unknown',
					'error'   => $e->getMessage(),
				)
			);

			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Discover Keywords using keywords_for_site endpoint
	public function ajax_discover_keywords() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$domain        = sanitize_text_field( $_POST['domain'] ?? '' );
		$language_code = sanitize_text_field( $_POST['language_code'] ?? 'en' );
		$location_code = absint( $_POST['location_code'] ?? 2840 );

		if ( ! $this->dataforseo_client ) {
			wp_send_json_error( array( 'message' => esc_html__( 'DataForSEO not configured', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
			$response = $this->dataforseo_client->get_keywords_for_site( $domain, $language_code, $location_code, 50 );

			if ( is_wp_error( $response ) ) {
				throw new \Exception( $response->get_error_message() );
			}

			$keywords = $this->process_discovered_keywords( $response );

			$html = $this->render_discovery_results( $keywords );

			wp_send_json_success(
				array(
					'html'  => $html,
					'count' => count( $keywords ),
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Keyword discovery failed',
				array(
					'domain' => $domain,
					'error'  => $e->getMessage(),
				)
			);

			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	private function process_discovered_keywords( $response ) {
		$task  = $response[0] ?? array();
		$items = $task['result'][0]['items'] ?? array();

		$keywords = array();
		foreach ( $items as $item ) {
			$kw_data       = $item['keyword_data'] ?? array();
			$keyword       = $kw_data['keyword'] ?? '';
			$search_volume = $kw_data['keyword_info']['search_volume'] ?? 0;

			if ( empty( $keyword ) ) {
				continue;
			}

			$keywords[] = array(
				'keyword'       => $keyword,
				'search_volume' => $search_volume,
				'search_intent' => $this->detect_search_intent( $keyword ),
				'content_type'  => $this->detect_content_type( $keyword ),
			);
		}

		return array_slice( $keywords, 0, 30 );
	}

	private function render_discovery_results( $keywords ) {
		if ( empty( $keywords ) ) {
			return '<div class="empty-state"><p>No new keywords found</p></div>';
		}

		$html  = '<table class="discovery-table">';
		$html .= '<thead><tr>';
		$html .= '<th>Keyword</th>';
		$html .= '<th>Volume</th>';
		$html .= '<th>Intent</th>';
		$html .= '<th>Action</th>';
		$html .= '</tr></thead><tbody>';

		foreach ( $keywords as $kw ) {
			$keyword_json = htmlspecialchars( wp_json_encode( $kw ), ENT_QUOTES, 'UTF-8' );

			$html .= '<tr>';
			$html .= '<td><strong>' . esc_html( $kw['keyword'] ) . '</strong></td>';
			$html .= '<td>' . number_format( $kw['search_volume'] ) . '/mo</td>';
			$html .= '<td><span class="intent-badge">' . ucfirst( $kw['search_intent'] ) . '</span></td>';
			$html .= '<td><button class="btn-add-discovery" data-keyword="' . $keyword_json . '">➕ Add to Calendar</button></td>';
			$html .= '</tr>';
		}

		$html .= '</tbody></table>';
		return $html;
	}

	// AJAX Handler - Add Manual Keywords
	public function ajax_add_manual_keywords() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keywords_text = sanitize_textarea_field( wp_unslash( $_POST['keywords'] ?? '' ) );

		if ( empty( $keywords_text ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'No keywords provided', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
			$keywords_array = array_filter( array_map( 'trim', preg_split( '/[\r\n,]+/', $keywords_text ) ) );

			if ( empty( $keywords_array ) ) {
				wp_send_json_error( array( 'message' => esc_html__( 'No valid keywords found', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			}

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

			$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 );

			$added            = array();
			$skipped          = array();
			$next_date_offset = 1;

			foreach ( $keywords_array as $keyword ) {
				$existing = $entry_manager->check_keyword_exists( $keyword );
				if ( $existing ) {
					$skipped[] = $keyword;
					continue;
				}

                $upcoming_count = $this->calendar_repository->get_upcoming_count( current_time( 'Y-m-d' ), gmdate( 'Y-m-d', strtotime( '+30 days' ) ) );

				$publication_date = gmdate( 'Y-m-d', strtotime( '+' . ( $upcoming_count + 1 ) . ' days' ) );

				$keyword_data = array(
					'keyword'       => $keyword,
					'search_intent' => 'informational',
					'content_type'  => 'blog',
				);

				$payload = $payload_builder->build_payload( $keyword_data );

				$entry_id = $entry_manager->create_entry(
					array(
						'keyword'                => $keyword,
						'keyword_source'         => 'manual',
						'search_intent'          => 'informational',
						'wisewand_payload'       => wp_json_encode( $payload ),
						'wisewand_lang'          => $payload['lang'],
						'wisewand_type'          => $payload['type'],
						'wisewand_length'        => $payload['length'],
						'publication_date'       => $publication_date,
						'publication_time_range' => 'random',
						'target_author'          => $this->options->get( 'wisewand_calendar_default_author', 1 ),
						'created_by'             => get_current_user_id(),
					)
				);

				if ( ! is_wp_error( $entry_id ) ) {
					$added[] = array(
						'keyword' => $keyword,
						'date'    => $publication_date,
					);
				}
			}

			$message = sprintf(
				/* translators: 1: Number of keywords added, 2: Number of keywords skipped */
				__( '%1$d keyword(s) added to calendar. %2$d skipped (already scheduled).', 'wisewand-ai-content-writer-seo-content-generator' ),
				count( $added ),
				count( $skipped )
			);

			wp_send_json_success(
				array(
					'success_count' => count( $added ),
					'skipped_count' => count( $skipped ),
					'added'         => $added,
					'skipped'       => $skipped,
					'message'       => $message,
					'summary_html'  => $this->render_manual_summary( $added, $skipped ),
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error( 'Failed to add manual keywords', array( 'error' => $e->getMessage() ) );
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	private function render_manual_summary( $added, $skipped ) {
		if ( empty( $added ) && empty( $skipped ) ) {
			return '';
		}

		$html  = '<div style="margin-top: 16px; padding: 12px; background: #f0f9ff; border-radius: 8px;">';
		$html .= '<strong>Summary:</strong><br>';

		if ( ! empty( $added ) ) {
			$html .= '✅ Added: ' . count( $added ) . ' keyword(s)<br>';
		}

		if ( ! empty( $skipped ) ) {
			$html .= '⏭️ Skipped: ' . count( $skipped ) . ' (already scheduled)<br>';
		}

		$html .= '</div>';
		return $html;
	}

	// AJAX Handler - Generate Smart Roadmap
	public function ajax_generate_smart_roadmap() {
		error_log( '=== WISEWAND ROADMAP: Handler started ===' );

		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$domain            = sanitize_text_field( $_POST['domain'] ?? '' );
		$language_code     = sanitize_text_field( $_POST['language_code'] ?? 'en' );
		$location_code     = absint( $_POST['location_code'] ?? 2840 );
		$posts_per_period  = absint( $_POST['posts_per_period'] ?? 1 );
		$period_type       = sanitize_text_field( $_POST['period_type'] ?? 'day' );
		$keyword_selection = sanitize_text_field( $_POST['keyword_selection'] ?? 'all' );

		// Decode JSON first, then sanitize array elements
		$raw_keywords      = isset( $_POST['selected_keywords'] ) ? wp_unslash( $_POST['selected_keywords'] ) : '';
		$selected_keywords_raw = json_decode( $raw_keywords, true );
		if ( json_last_error() !== JSON_ERROR_NONE || ! is_array( $selected_keywords_raw ) ) {
			$selected_keywords = array();
		} else {
			// Sanitize each keyword in the array
			$selected_keywords = array_map( 'sanitize_text_field', $selected_keywords_raw );
		}

		// Decode JSON first, then sanitize array elements
		$raw_actions             = isset( $_POST['cannibalisation_actions'] ) ? wp_unslash( $_POST['cannibalisation_actions'] ) : '';
		$cannibalisation_actions_raw = json_decode( $raw_actions, true );
		if ( json_last_error() !== JSON_ERROR_NONE || ! is_array( $cannibalisation_actions_raw ) ) {
			$cannibalisation_actions = array();
		} else {
			// Sanitize each action in the array
			$cannibalisation_actions = array_map( 'sanitize_text_field', $cannibalisation_actions_raw );
		}
		$generate_ai_instructions = (int) ( $_POST['generate_ai_instructions'] ?? 0 ) === 1; // Explicit integer check

		// Debug logging for AI instructions
		if ( WP_DEBUG ) {
			error_log( 'AI_INSTRUCTIONS: Modal checkbox value (from POST): ' . ( $_POST['generate_ai_instructions'] ?? 'NOT SET' ) );
			error_log( 'AI_INSTRUCTIONS: Parsed as boolean: ' . ( $generate_ai_instructions ? 'TRUE (will use OpenAI)' : 'FALSE (no OpenAI calls)' ) );
			error_log( 'AI_INSTRUCTIONS: Global setting: ' . ( $this->options->get( 'wisewand_default_auto_generate_instructions' ) ? 'ENABLED' : 'DISABLED' ) );
		}

		error_log( 'WISEWAND ROADMAP: Parameters - posts: ' . $posts_per_period . ', period: ' . $period_type . ', selection: ' . $keyword_selection . ', AI instructions: ' . ( $generate_ai_instructions ? 'YES' : 'NO' ) );

		// Validate: Default project must be configured
		$default_project = $this->options->get( 'wisewand_default_project_id', '' );
		if ( empty( $default_project ) ) {
			error_log( 'WISEWAND ROADMAP: No default project configured' );
			wp_send_json_error(
				array(
					'message' => esc_html__( 'Default Wisewand project not configured.', 'wisewand-ai-content-writer-seo-content-generator' ) . "\n\n" .
								__( 'Please go to:', 'wisewand-ai-content-writer-seo-content-generator' ) . "\n" .
								__( 'Settings → Calendar Defaults', 'wisewand-ai-content-writer-seo-content-generator' ) . "\n\n" .
								__( 'And select a default project before generating roadmaps.', 'wisewand-ai-content-writer-seo-content-generator' ),
				)
			);
			return;
		}

		// Ensure DataForSEO client is initialized
		if ( ! $this->dataforseo_client ) {
			$this->init_dataforseo_client();
		}

		try {
			error_log( 'WISEWAND ROADMAP: Entering try block' );

			$this->logger->info(
				'Roadmap AJAX called',
				array(
					'domain'            => $domain,
					'posts_per_period'  => $posts_per_period,
					'period_type'       => $period_type,
					'keyword_selection' => $keyword_selection,
				)
			);

			// Load ALL keywords from table (no snapshot requirement!)
			error_log( 'WISEWAND ROADMAP: Loading keywords...' );
			$all_keywords = $this->load_all_keywords_for_roadmap( $domain, $language_code, $location_code );
			error_log( 'WISEWAND ROADMAP: Loaded ' . count( $all_keywords ) . ' keywords' );

			$this->logger->info(
				'Keywords loaded',
				array(
					'count'         => count( $all_keywords ),
					'first_keyword' => ! empty( $all_keywords ) ? $all_keywords[0]['keyword'] : 'none',
				)
			);

			// Apply keyword selection strategy
			if ( $keyword_selection === 'selected' && ! empty( $selected_keywords ) ) {
				// Use only selected keywords
				$all_keywords = array_filter(
					$all_keywords,
					function ( $kw ) use ( $selected_keywords ) {
						return in_array( $kw['keyword'], $selected_keywords );
					}
				);
				$all_keywords = array_values( $all_keywords ); // Re-index
			} elseif ( $keyword_selection === 'auto' ) {
				// Auto-select top keywords (limit to reasonable size)
				$all_keywords = array_slice( $all_keywords, 0, 180 ); // Max 6 months at 1/day
			}
			// 'all' = use all keywords (no filtering)

			if ( empty( $all_keywords ) ) {
				$this->logger->error(
					'No keywords after filtering',
					array(
						'keyword_selection'       => $keyword_selection,
						'selected_keywords_count' => count( $selected_keywords ),
					)
				);
				wp_send_json_error( array( 'message' => esc_html__( 'No keywords available. Add keywords or run SEO analysis first.', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
				return;
			}

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

			$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 );
			$roadmap_generator = new \Wisewand\Calendar\Roadmap_Generator( $this->options, $this->logger, $payload_builder, $entry_manager );

			$this->logger->info(
				'Roadmap generation with enhanced options',
				array(
					'total_keywords'    => count( $all_keywords ),
					'posts_per_period'  => $posts_per_period,
					'period_type'       => $period_type,
					'keyword_selection' => $keyword_selection,
				)
			);

			// Generate smart roadmap with new parameters
			error_log( 'WISEWAND ROADMAP: Calling generate_smart_roadmap()...' );

			// Accept start_date from POST (Phase 5: Part A)
			$start_date_input = isset( $_POST['start_date'] )
				? sanitize_text_field( wp_unslash( $_POST['start_date'] ) )
				: gmdate( 'Y-m-d' ); // Default to today

			// Validate: Don't allow past dates
			if ( strtotime( $start_date_input ) < strtotime( gmdate( 'Y-m-d' ) ) ) {
				$start_date = gmdate( 'Y-m-d' ); // Force to today if past
			} else {
				$start_date = $start_date_input;
			}

			if ( WP_DEBUG ) {
				error_log( 'ROADMAP: Start date - Input: ' . $start_date_input . ', Final: ' . $start_date . ', Today: ' . gmdate( 'Y-m-d' ) );
			}

			$roadmap_options = array(
				'detect_cannibalisation'   => true,
				'cannibalisation_actions'  => $cannibalisation_actions,
				'generate_ai_instructions' => $generate_ai_instructions, // ✅ Pass AI generation preference
			);

			if ( $generate_ai_instructions ) {
				error_log( 'WISEWAND ROADMAP: AI Instructions generation ENABLED for ' . count( $all_keywords ) . ' keywords' );
			}

			$calendar_entries = $roadmap_generator->generate_smart_roadmap(
				$all_keywords,
				$start_date,
				$posts_per_period,
				$period_type,
				$roadmap_options
			);

			error_log( 'WISEWAND ROADMAP: Generated ' . count( $calendar_entries ) . ' calendar entries' );

			// Bulk create entries
			error_log( 'WISEWAND ROADMAP: Bulk creating entries...' );
			$result = $entry_manager->bulk_create_entries( $calendar_entries );
			error_log( 'WISEWAND ROADMAP: Bulk create result - success: ' . $result['success_count'] . ', duplicates: ' . ( $result['duplicate_count'] ?? 0 ) . ', failed: ' . ( $result['failed_count'] ?? 0 ) );

			// If all were duplicates, send helpful error
			if ( $result['success_count'] === 0 && ( $result['duplicate_count'] ?? 0 ) > 0 ) {
				$msg  = "All {$result['duplicate_count']} keywords are already scheduled in the Editorial Calendar.\n\n";
				$msg .= "Options:\n";
				$msg .= "1. Go to Editorial Calendar and delete existing entries\n";
				$msg .= "2. Select different keywords\n";
				$msg .= '3. Choose a different start date';

				error_log( 'WISEWAND ROADMAP: All duplicates - sending error' );
				wp_send_json_error( array( 'message' => $msg ) );
				return;
			}

			// Calculate statistics
			$stats = array(
				'total_scheduled' => $result['success_count'],
				'duplicates'      => $result['duplicate_count'] ?? 0,
				'creates'         => count( array_filter( $calendar_entries, fn( $e ) => $e['publication_action'] === 'create' ) ),
				'updates'         => count( array_filter( $calendar_entries, fn( $e ) => $e['publication_action'] === 'update' ) ),
				'skipped'         => count( $all_keywords ) - count( $calendar_entries ),
				'by_risk'         => array(
					'none'   => count( array_filter( $calendar_entries, fn( $e ) => ( $e['cannibalisation_risk'] ?? 'none' ) === 'none' ) ),
					'low'    => count( array_filter( $calendar_entries, fn( $e ) => ( $e['cannibalisation_risk'] ?? 'none' ) === 'low' ) ),
					'medium' => count( array_filter( $calendar_entries, fn( $e ) => ( $e['cannibalisation_risk'] ?? 'none' ) === 'medium' ) ),
					'high'   => count( array_filter( $calendar_entries, fn( $e ) => ( $e['cannibalisation_risk'] ?? 'none' ) === 'high' ) ),
				),
				'date_start'      => $calendar_entries[0]['publication_date'] ?? $start_date,
				'date_end'        => $calendar_entries[ count( $calendar_entries ) - 1 ]['publication_date'] ?? $start_date,
				'total_days'      => $this->calculate_days_between( $calendar_entries[0]['publication_date'] ?? $start_date, $calendar_entries[ count( $calendar_entries ) - 1 ]['publication_date'] ?? $start_date ),
			);

			$this->logger->info( 'Smart roadmap generated (enhanced)', $stats );

			// Auto-schedule CRON for automation
			$this->ensure_calendar_cron_scheduled();

			// Add entry IDs and AI generation flag to response
			$stats['created_entry_ids']        = $result['created_ids'] ?? array();
			$stats['generate_ai_instructions'] = $generate_ai_instructions;

			error_log( 'WISEWAND ROADMAP: Sending success response - scheduled: ' . $result['success_count'] . ', duplicates: ' . ( $result['duplicate_count'] ?? 0 ) . ', AI instructions: ' . ( $generate_ai_instructions ? 'YES' : 'NO' ) );

			// Phase 5: Part B - Trigger CRON if same-day roadmap
			$is_same_day = ( $start_date === gmdate( 'Y-m-d' ) );
			if ( $is_same_day && $result['success_count'] > 0 ) {
				if ( WP_DEBUG ) {
					error_log( 'ROADMAP: Same-day roadmap detected! Triggering immediate CRON...' );
				}

				// Trigger daily CRON to generate articles NOW
				// This bypasses the normal 24-hour lead time
				do_action( 'wisewand_editorial_calendar_cron' );

				$this->logger->info(
					'Triggered immediate CRON for same-day roadmap',
					array(
						'start_date'      => $start_date,
						'entries_created' => $result['success_count'],
					)
				);

				// Update response to inform user
				$stats['immediate_generation_triggered'] = true;
				$stats['message']                        = 'Roadmap created and article generation started immediately!';
			}

			wp_send_json_success( $stats );
			error_log( 'WISEWAND ROADMAP: Response sent successfully' );

		} catch ( \Exception $e ) {
			error_log( 'WISEWAND ROADMAP: Exception caught - ' . $e->getMessage() . ' at line ' . $e->getLine() );

			$this->logger->error(
				'Smart roadmap generation failed',
				array(
					'domain' => $domain,
					'error'  => $e->getMessage(),
					'file'   => $e->getFile(),
					'line'   => $e->getLine(),
					'trace'  => $e->getTraceAsString(),
				)
			);

			$error_message = 'Exception: ' . $e->getMessage() . ' (File: ' . basename( $e->getFile() ) . ', Line: ' . $e->getLine() . ')';
			error_log( 'WISEWAND ROADMAP: Sending error response: ' . $error_message );
			wp_send_json_error( array( 'message' => $error_message ) );

		} catch ( \Error $e ) {
			error_log( 'WISEWAND ROADMAP: Fatal error caught - ' . $e->getMessage() . ' at line ' . $e->getLine() );

			$this->logger->error(
				'Smart roadmap fatal error',
				array(
					'error' => $e->getMessage(),
					'file'  => $e->getFile(),
					'line'  => $e->getLine(),
				)
			);

			$error_message = 'Fatal error: ' . $e->getMessage() . ' (File: ' . basename( $e->getFile() ) . ', Line: ' . $e->getLine() . ')';
			error_log( 'WISEWAND ROADMAP: Sending fatal error response: ' . $error_message );
			wp_send_json_error( array( 'message' => $error_message ) );
		}

		error_log( '=== WISEWAND ROADMAP: Handler completed ===' );
	}

	/**
	 * Get available languages from all keyword sources
	 *
	 * @return array Array of language codes with counts
	 */
	public function ajax_get_available_languages() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

		if ( ! current_user_can( 'edit_posts' ) ) {
			wp_send_json_error( array( 'message' => 'Insufficient permissions' ) );
		}

		$domain        = sanitize_text_field( $_POST['domain'] ?? '' );
		$language_code = sanitize_text_field( $_POST['language_code'] ?? 'en' );
		$location_code = absint( $_POST['location_code'] ?? 2840 );

		try {
			// Load all keywords (with 'all' language filter to get everything)
			$all_keywords = $this->load_all_keywords_for_roadmap( $domain, 'all', $location_code );

			// Count keywords by language
			$language_counts = array();
			foreach ( $all_keywords as $kw ) {
				$lang = $kw['language_code'] ?? 'en';
				if ( ! isset( $language_counts[ $lang ] ) ) {
					$language_counts[ $lang ] = 0;
				}
				++$language_counts[ $lang ];
			}

			// Sort by count descending
			arsort( $language_counts );

			// Format response with language names
			$languages      = array();
			$language_names = array(
				'en' => '🇬🇧 English',
				'fr' => '🇫🇷 French',
				'es' => '🇪🇸 Spanish',
				'de' => '🇩🇪 German',
				'it' => '🇮🇹 Italian',
				'pt' => '🇵🇹 Portuguese',
				'nl' => '🇳🇱 Dutch',
				'pl' => '🇵🇱 Polish',
				'ru' => '🇷🇺 Russian',
				'ja' => '🇯🇵 Japanese',
				'zh' => '🇨🇳 Chinese',
				'ar' => '🇸🇦 Arabic',
				'hi' => '🇮🇳 Hindi',
				'tr' => '🇹🇷 Turkish',
				'ko' => '🇰🇷 Korean',
			);

			foreach ( $language_counts as $code => $count ) {
				$languages[] = array(
					'code'  => $code,
					'name'  => $language_names[ $code ] ?? strtoupper( $code ),
					'count' => $count,
				);
			}

			wp_send_json_success(
				array(
					'languages'      => $languages,
					'total_keywords' => count( $all_keywords ),
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error( 'Failed to get available languages', array( 'error' => $e->getMessage() ) );
			wp_send_json_error( array( 'message' => esc_html( 'Failed to detect languages: ' . $e->getMessage() ) ) );
		}
	}

	/**
	 * Load all keywords from all sources (rankings, saved, etc.)
	 */
	private function load_all_keywords_for_roadmap( $domain, $language_code, $location_code ) {
		$all_keywords = array();

		// Try to load snapshot data (rankings + opportunities)
		$snapshot_data = $this->load_latest_snapshot( $domain, $language_code, $location_code );

		// Load saved keywords manager once
		if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
			require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
		}

		$saved_manager  = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );
		$saved_keywords = $saved_manager->get_all_saved_keywords();

		// Build AI data lookup map from saved keywords (for enriching snapshot data)
		$ai_data_map = array(); // [keyword_text => [author_id, categories]]
		foreach ( $saved_keywords as $saved_kw ) {
			$ai_data_map[ $saved_kw['keyword'] ] = array(
				'author'     => $saved_kw['suggested_author_id'] ?? null,
				'categories' => $saved_kw['suggested_categories'] ?? null,
			);
		}

		if ( $snapshot_data ) {
			// Add current rankings (enriched with AI data)
			foreach ( ( $snapshot_data['current_rankings'] ?? array() ) as $kw ) {
				$kw['source'] = 'current_ranking';

				// Enrich with AI-matched data from saved keywords if available
				if ( isset( $ai_data_map[ $kw['keyword'] ] ) ) {
					$kw['suggested_author_id']  = $ai_data_map[ $kw['keyword'] ]['author'];
					$kw['suggested_categories'] = $ai_data_map[ $kw['keyword'] ]['categories'];
				}

				$all_keywords[] = $kw;
			}

			// Add opportunities (enriched with AI data)
			foreach ( ( $snapshot_data['opportunities'] ?? array() ) as $kw ) {
				$kw['source'] = 'opportunity';

				// Enrich with AI-matched data from saved keywords if available
				if ( isset( $ai_data_map[ $kw['keyword'] ] ) ) {
					$kw['suggested_author_id']  = $ai_data_map[ $kw['keyword'] ]['author'];
					$kw['suggested_categories'] = $ai_data_map[ $kw['keyword'] ]['categories'];
				}

				$all_keywords[] = $kw;
			}
		}

		// Add saved keywords (these already have author/categories)

		foreach ( $saved_keywords as $saved_kw ) {
			$keyword_lang = $saved_kw['language_code'] ?? $language_code;

			// Language filtering: Skip if specific language requested and doesn't match
			if ( $language_code !== 'all' && $keyword_lang !== $language_code ) {
				continue;
			}

			$all_keywords[] = array(
				'keyword'              => $saved_kw['keyword'],
				'position'             => null,
				'search_volume'        => $saved_kw['search_volume'] ?? 0,
				'difficulty'           => $saved_kw['difficulty'] ?? null,
				'cpc'                  => $saved_kw['cpc'],
				'competition'          => $saved_kw['competition'],
				'search_intent'        => $saved_kw['search_intent'],
				'content_type'         => $saved_kw['content_type'],
				'priority'             => $saved_kw['priority_score'] ?? 0,
				'language_code'        => $keyword_lang,
				'source'               => $saved_kw['keyword_source'],
				// AI-matched author and categories (will be used in calendar entry)
				'suggested_author_id'  => $saved_kw['suggested_author_id'] ?? null,
				'suggested_categories' => $saved_kw['suggested_categories'] ?? null,
			);
		}

		// Apply language filter to all keywords (rankings + opportunities + saved)
		if ( $language_code !== 'all' ) {
			$all_keywords = array_filter(
				$all_keywords,
				function ( $kw ) use ( $language_code ) {
					return ( $kw['language_code'] ?? 'en' ) === $language_code;
				}
			);
			$all_keywords = array_values( $all_keywords ); // Re-index array
		}

		$this->logger->info(
			'All keywords loaded for roadmap',
			array(
				'total'           => count( $all_keywords ),
				'language_filter' => $language_code,
				'from_snapshot'   => isset( $snapshot_data ) ? count( $snapshot_data['current_rankings'] ?? array() ) + count( $snapshot_data['opportunities'] ?? array() ) : 0,
				'from_saved'      => count( $saved_keywords ),
			)
		);

		// Enrich with calendar status
		$all_keywords = $this->enrich_with_calendar_status( $all_keywords );

		return $all_keywords;
	}

	/**
	 * Enrich keywords with calendar status
	 */
	private function enrich_with_calendar_status( $keywords ) {
		$keyword_list = array_column( $keywords, 'keyword' );

		if ( empty( $keyword_list ) ) {
			return $keywords;
		}

		// Get calendar data indexed by keyword
		$calendar_data = $this->calendar_repository->get_keywords_calendar_status( $keyword_list );

		// Enrich keywords with calendar status
		foreach ( $keywords as &$kw ) {
			if ( isset( $calendar_data[ $kw['keyword'] ] ) ) {
				$cal                             = $calendar_data[ $kw['keyword'] ];
				$kw['calendar_id']               = $cal['calendar_id'];
				$kw['calendar_date']             = $cal['publication_date'];
				$kw['calendar_time']             = $cal['publication_time'];
				$kw['calendar_wisewand_status']  = $cal['wisewand_status'];
				$kw['calendar_wordpress_status'] = $cal['wordpress_status'];
				$kw['calendar_post_id']          = $cal['wordpress_post_id'];
				$kw['calendar_status']           = $this->determine_calendar_status( $cal );
			} else {
				$kw['calendar_status'] = null;
			}
		}

		return $keywords;
	}

	/**
	 * Determine most relevant calendar status to display
	 */
	private function determine_calendar_status( $cal ) {
		$wp_status = $cal['wordpress_status'];
		$ww_status = $cal['wisewand_status'];

		// Change 2: Handle new imported sub-statuses
		if ( $wp_status === 'imported_published' || $wp_status === 'published' ) {
			return array(
				'icon'  => '✅',
				'label' => 'Published',
				'color' => '#10B981',
				'type'  => 'published',
			);
		}
		if ( ( $wp_status === 'imported_draft' || $wp_status === 'draft' ) && $cal['wordpress_post_id'] ) {
			return array(
				'icon'  => '📝',
				'label' => 'Draft',
				'color' => '#F59E0B',
				'type'  => 'draft',
			);
		}
		if ( $wp_status === 'importing' ) {
			return array(
				'icon'  => '📥',
				'label' => 'Importing',
				'color' => '#3B82F6',
				'type'  => 'importing',
			);
		}
		if ( $ww_status === 'ready' ) {
			return array(
				'icon'  => '✅',
				'label' => 'Ready',
				'color' => '#10B981',
				'type'  => 'ready',
			);
		}
		if ( $ww_status === 'generating' ) {
			return array(
				'icon'  => '🔄',
				'label' => 'Generating',
				'color' => '#8B5CF6',
				'type'  => 'generating',
			);
		}
		if ( $ww_status === 'creating' ) {
			return array(
				'icon'  => '🔨',
				'label' => 'Creating',
				'color' => '#00BCD4',
				'type'  => 'creating',
			);
		}
		if ( $wp_status === 'failed' || $ww_status === 'failed' ) {
			return array(
				'icon'  => '❌',
				'label' => 'Failed',
				'color' => '#EF4444',
				'type'  => 'failed',
			);
		}

		return array(
			'icon'  => '⏳',
			'label' => 'Scheduled',
			'color' => '#A0A8B8',
			'type'  => 'scheduled',
		);
	}

	/**
	 * Calculate days between two dates
	 */
	private function calculate_days_between( $start, $end ) {
		$start_dt = new \DateTime( $start );
		$end_dt   = new \DateTime( $end );
		$diff     = $start_dt->diff( $end_dt );
		return $diff->days + 1;
	}

	// AJAX Handler - Save Keywords to Library
	public function ajax_save_keywords_to_library() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		// CRITICAL FIX: Increase timeout for large keyword batches
		// Large batches with auto_enrich can take 50+ seconds (each keyword = 1 API call + delay)
		// Without this, PHP default timeout (30s) kills the request before completion
		// phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged -- Required for long-running SEO analysis operations
		set_time_limit( 300 ); // 5 minutes max

		// Increase memory limit if needed for large batches
		if ( function_exists( 'wp_raise_memory_limit' ) ) {
			wp_raise_memory_limit( 'admin' );
		}

		$keywords_text = sanitize_textarea_field( wp_unslash( $_POST['keywords'] ?? '' ) );
		$auto_enrich   = ! empty( $_POST['auto_enrich'] );
		$language_code = sanitize_text_field( $_POST['language_code'] ?? 'en' );
		$location_code = absint( $_POST['location_code'] ?? 2840 );

		if ( empty( $keywords_text ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'No keywords provided', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
			}

			$saved_manager = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );

			$keywords_array = array_filter( array_map( 'trim', preg_split( '/[\r\n,]+/', $keywords_text ) ) );

			$this->logger->info(
				'Saving keywords to library',
				array(
					'keywords_count' => count( $keywords_array ),
					'language_code'  => $language_code,
					'location_code'  => $location_code,
					'auto_enrich'    => $auto_enrich,
				)
			);

			$result = $saved_manager->save_keywords( $keywords_array, $auto_enrich, 'manual', null, $language_code, $location_code );

			// Copy ranking data from SEO Intelligence if available
			$this->copy_ranking_data_to_saved_keywords( $result['saved'], $language_code, $location_code );

			$this->logger->info(
				'Keywords saved result',
				array(
					'saved'   => $result['saved_count'],
					'skipped' => $result['skipped_count'],
				)
			);
			// CRITICAL FIX: Don't render full HTML table in AJAX response
			// For large batches (100+ keywords), rendering HTML takes significant time
			// and creates massive response payloads (can exceed 1MB for 500 keywords)
			// Instead: Return minimal data, let client call loadLatestSnapshot() after all batches complete
			// This reduces response time from 2-3s to <100ms per batch

			wp_send_json_success(
				array(
					'saved_count'   => $result['saved_count'],
					'skipped_count' => $result['skipped_count'],
					'failed_count'  => $result['failed_count'],
					/* translators: 1: Number of keywords saved, 2: Number of keywords skipped, 3: Number of keywords failed */
					'message'       => sprintf( esc_html__( '%1$d saved, %2$d skipped, %3$d failed', 'wisewand-ai-content-writer-seo-content-generator' ), $result['saved_count'], $result['skipped_count'], $result['failed_count'] ),
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Failed to save keywords',
				array(
					'error' => $e->getMessage(),
					'trace' => $e->getTraceAsString(),
				)
			);
			wp_send_json_error( array( 'message' => esc_html( 'Error: ' . $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Enrich Saved Keywords
	public function ajax_enrich_saved_keywords() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keyword_ids = array_map( 'absint', $_POST['keyword_ids'] ?? array() );

		if ( empty( $keyword_ids ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'No keywords selected', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
			}

			$saved_manager = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );

			$enriched = 0;
			foreach ( $keyword_ids as $keyword_id ) {
				if ( $saved_manager->enrich_keyword( $keyword_id ) ) {
					++$enriched;
				}
				usleep( 500000 );
			}

			wp_send_json_success(
				array(
					'enriched' => $enriched,
					/* translators: %d: Number of keywords enriched with SEO data */
					'message'  => sprintf( esc_html__( '%d keyword(s) enriched with SEO data', 'wisewand-ai-content-writer-seo-content-generator' ), $enriched ),
				)
			);

		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Schedule Saved Keyword
	public function ajax_schedule_saved_keyword() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keyword_id = absint( $_POST['keyword_id'] ?? 0 );

		try {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Query custom plugin table for saved keyword data
			$saved_kw = $this->keyword_repository->get_keyword( $keyword_id );

			if ( ! $saved_kw ) {
				wp_send_json_error( array( 'message' => esc_html__( 'Keyword not found', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
				return;
			}

			// Convert object to array for compatibility
			$saved_kw = (array) $saved_kw;

			$keyword_data = array(
				'keyword'       => $saved_kw['keyword'],
				'search_volume' => $saved_kw['search_volume'],
				'search_intent' => $saved_kw['search_intent'],
				'content_type'  => $saved_kw['content_type'],
				'priority'      => $saved_kw['priority_score'],
			);

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

			$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 );

			$existing = $entry_manager->check_keyword_exists( $saved_kw['keyword'] );
			if ( $existing ) {
				/* translators: %s: Publication date in format YYYY-MM-DD */
				wp_send_json_error( array( 'message' => sprintf( esc_html__( 'Already scheduled for %s', 'wisewand-ai-content-writer-seo-content-generator' ), $existing['publication_date'] ) ) );
				return;
			}

            $upcoming_count = $this->calendar_repository->get_upcoming_count( current_time( 'Y-m-d' ), gmdate( 'Y-m-d', strtotime( '+30 days' ) ) );

			$next_date = gmdate( 'Y-m-d', strtotime( '+' . ( $upcoming_count + 1 ) . ' days' ) );

			$wisewand_payload = $payload_builder->build_payload( $keyword_data );

			$entry_id = $entry_manager->create_entry(
				array(
					'keyword'                => $saved_kw['keyword'],
					'keyword_source'         => 'saved',
					'search_volume'          => $saved_kw['search_volume'],
					'search_intent'          => $saved_kw['search_intent'],
					'priority_score'         => $saved_kw['priority_score'],
					'wisewand_payload'       => wp_json_encode( $wisewand_payload ),
					'wisewand_lang'          => $wisewand_payload['lang'],
					'wisewand_type'          => $wisewand_payload['type'],
					'wisewand_length'        => $wisewand_payload['length'],
					'publication_date'       => $next_date,
					'publication_time_range' => 'random',
					'target_author'          => $this->options->get( 'wisewand_calendar_default_author', 1 ),
					'created_by'             => get_current_user_id(),
				)
			);

			if ( is_wp_error( $entry_id ) ) {
				wp_send_json_error( array( 'message' => esc_html( $entry_id->get_error_message() ) ) );
				return;
			}

			wp_send_json_success(
				array(
					'entry_id'         => $entry_id,
					'publication_date' => $next_date,
					'keyword'          => $saved_kw['keyword'],
				)
			);

		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Schedule Multiple Saved Keywords
	public function ajax_schedule_saved_keywords() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keyword_ids = array_map( 'absint', $_POST['keyword_ids'] ?? array() );

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

			$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 );

			$scheduled = 0;

			foreach ( $keyword_ids as $keyword_id ) {
				$saved_kw = $this->keyword_repository->get_keyword( $keyword_id );
				if ( $saved_kw ) {
					$saved_kw = (array) $saved_kw;
				}

				if ( ! $saved_kw ) {
					continue;
				}

				$existing = $entry_manager->check_keyword_exists( $saved_kw['keyword'] );
				if ( $existing ) {
					continue;
				}

                $upcoming_count = $this->calendar_repository->get_upcoming_count( current_time( 'Y-m-d' ), gmdate( 'Y-m-d', strtotime( '+30 days' ) ) );

				$next_date = gmdate( 'Y-m-d', strtotime( '+' . ( $upcoming_count + 1 ) . ' days' ) );

				$keyword_data = array(
					'keyword'       => $saved_kw['keyword'],
					'search_volume' => $saved_kw['search_volume'],
					'search_intent' => $saved_kw['search_intent'],
					'content_type'  => $saved_kw['content_type'],
					'priority'      => $saved_kw['priority_score'],
				);

				$wisewand_payload = $payload_builder->build_payload( $keyword_data );

				$entry_id = $entry_manager->create_entry(
					array(
						'keyword'                => $saved_kw['keyword'],
						'keyword_source'         => 'saved',
						'search_volume'          => $saved_kw['search_volume'],
						'search_intent'          => $saved_kw['search_intent'],
						'priority_score'         => $saved_kw['priority_score'],
						'wisewand_payload'       => wp_json_encode( $wisewand_payload ),
						'wisewand_lang'          => $wisewand_payload['lang'],
						'wisewand_type'          => $wisewand_payload['type'],
						'wisewand_length'        => $wisewand_payload['length'],
						'publication_date'       => $next_date,
						'publication_time_range' => 'random',
						'target_author'          => $this->options->get( 'wisewand_calendar_default_author', 1 ),
						'created_by'             => get_current_user_id(),
					)
				);

				if ( ! is_wp_error( $entry_id ) ) {
					++$scheduled;
				}
			}

			wp_send_json_success( array( 'scheduled' => $scheduled ) );

		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Delete Saved Keyword
	public function ajax_delete_saved_keyword() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keyword_id = absint( $_POST['keyword_id'] ?? 0 );

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
			}

			$saved_manager = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );

			$result = $saved_manager->delete_keyword( $keyword_id );

			if ( is_wp_error( $result ) ) {
				throw new \Exception( $result->get_error_message() );
			}

			wp_send_json_success( array( 'message' => esc_html__( 'Keyword deleted', 'wisewand-ai-content-writer-seo-content-generator' ) ) );

		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Get Keyword Ideas (Refactored for Progress Tracking)
	public function ajax_get_keyword_ideas() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$seeds_text    = sanitize_textarea_field( $_POST['seeds'] ?? '' );
		$language_code = sanitize_text_field( $_POST['language_code'] ?? 'en' );
		$location_code = absint( $_POST['location_code'] ?? 2840 );
		$seed_index    = absint( $_POST['seed_index'] ?? 0 );

		if ( empty( $seeds_text ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Please enter at least one seed keyword', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		if ( ! $this->dataforseo_client ) {
			wp_send_json_error( array( 'message' => esc_html__( 'DataForSEO not configured', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
			$seed_keywords = array_filter( array_map( 'trim', preg_split( '/[\r\n,]+/', $seeds_text ) ) );

			if ( empty( $seed_keywords ) || ! isset( $seed_keywords[ $seed_index ] ) ) {
				wp_send_json_error( array( 'message' => esc_html__( 'Invalid seed index', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			}

			$seed = $seed_keywords[ $seed_index ];

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

			$saved_manager = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );

			$progress_log = $this->process_single_seed_with_progress( $seed, $saved_manager, $language_code, $location_code, $seed_index + 1, count( $seed_keywords ) );

			wp_send_json_success( $progress_log );

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Keyword ideas failed',
				array(
					'error' => $e->getMessage(),
					'file'  => $e->getFile(),
					'line'  => $e->getLine(),
				)
			);
			wp_send_json_error( array( 'message' => esc_html( 'Error: ' . $e->getMessage() ) ) );
		}
	}

	private function process_single_seed_with_progress( $seed, $saved_manager, $language_code, $location_code, $seed_number, $total_seeds ) {
		$steps = array();

		// Step 1: Save and enrich the seed keyword
		$steps[] = $this->create_progress_step( 'save_seed', 'started', "Processing seed keyword: {$seed}", null, $seed );

		try {
			// CRITICAL: Check ALL 3 fields that match database UNIQUE constraint
			// UNIQUE KEY keyword_language (keyword, language_code, location_code)
			if ( ! $saved_manager->keyword_exists( $seed, $language_code, $location_code ) ) {
				$seed_id = $saved_manager->save_keyword( $seed, 'manual', null, $language_code, $location_code );

				if ( ! is_wp_error( $seed_id ) ) {
					$saved_manager->enrich_keyword( $seed_id );

					$this->keyword_repository->update_keyword( $seed_id, array( 'tags' => 'seed_keyword' ) );

					$steps[] = $this->create_progress_step( 'save_seed', 'success', 'Seed keyword saved and enriched', 'keywords_data/google_ads/search_volume/live', $seed );
				}
			} else {
				$steps[] = $this->create_progress_step( 'save_seed', 'skipped', 'Seed keyword already exists', null, $seed );
			}
		} catch ( \Exception $e ) {
			$steps[] = $this->create_progress_step( 'save_seed', 'warning', 'Seed already exists', null, $seed );
		}

		// Step 2: Fetch keyword ideas
		$steps[] = $this->create_progress_step( 'fetch_ideas', 'started', 'Fetching keyword ideas from DataForSEO...', 'keywords_data/google_ads/keyword_ideas/live', $seed );

		$response = $this->dataforseo_client->get_keyword_ideas( array( $seed ), $language_code, $location_code, 10 );

		if ( is_wp_error( $response ) ) {
			$steps[] = $this->create_progress_step( 'fetch_ideas', 'error', 'Failed: ' . $response->get_error_message(), null, $seed );

			return array(
				'seed'           => $seed,
				'seed_number'    => $seed_number,
				'total_seeds'    => $total_seeds,
				'ideas_found'    => 0,
				'ideas_enriched' => 0,
				'ideas_saved'    => 0,
				'steps'          => $steps,
				'completed'      => $seed_number >= $total_seeds,
			);
		}

		$ideas       = $this->process_keyword_ideas( $response, $seed );
		$ideas_count = count( $ideas );

		$steps[] = $this->create_progress_step( 'fetch_ideas', 'success', "{$ideas_count} keyword ideas retrieved", null, $seed );

		// Step 3: Enrich each keyword with volume data
		$enriched_ideas = array();

		if ( $ideas_count > 0 ) {
			$steps[] = $this->create_progress_step( 'enrich_batch', 'started', "Enriching {$ideas_count} keywords with metrics...", null, $seed );

			foreach ( $ideas as $idea_index => $idea ) {
				$keyword = $idea['keyword'];

				$steps[] = $this->create_progress_step(
					'enrich_keyword',
					'processing',
					"Enriching: {$keyword}",
					'keywords_data/google_ads/search_volume/live',
					$keyword,
					$idea_index + 1,
					$ideas_count
				);

				try {
					$enriched_data = $this->enrich_keyword_idea( $keyword, $seed, $language_code, $location_code );

					if ( $enriched_data ) {
						$enriched_ideas[] = $enriched_data;

						$steps[] = $this->create_progress_step(
							'enrich_keyword',
							'success',
							"✓ {$keyword} (Vol: " . number_format( $enriched_data['search_volume'] ) . ', Diff: ' . ( $enriched_data['difficulty'] ?? 'N/A' ) . ')',
							null,
							$keyword,
							$idea_index + 1,
							$ideas_count
						);
					}

					usleep( 300000 ); // Rate limiting
				} catch ( \Exception $e ) {
					$steps[] = $this->create_progress_step( 'enrich_keyword', 'error', "✗ {$keyword}: " . $e->getMessage(), null, $keyword );
				}
			}
		}

		// Step 4: Save enriched keywords
		$saved_count   = 0;
		$skipped_count = 0;

		if ( ! empty( $enriched_ideas ) ) {
			$steps[] = $this->create_progress_step( 'save_batch', 'started', 'Saving ' . count( $enriched_ideas ) . ' keywords to library...', null, $seed );

			$result        = $saved_manager->save_keyword_ideas( $enriched_ideas, $seed );
			$saved_count   = $result['saved'];
			$skipped_count = $result['skipped'];

			$steps[] = $this->create_progress_step( 'save_batch', 'success', "Saved {$saved_count} new keywords, skipped {$skipped_count} duplicates", null, $seed );
		}

		return array(
			'seed'           => $seed,
			'seed_number'    => $seed_number,
			'total_seeds'    => $total_seeds,
			'ideas_found'    => $ideas_count,
			'ideas_enriched' => count( $enriched_ideas ),
			'ideas_saved'    => $saved_count,
			'ideas_skipped'  => $skipped_count,
			'steps'          => $steps,
			'completed'      => $seed_number >= $total_seeds,
		);
	}

	private function create_progress_step( $step_type, $status, $message, $endpoint = null, $keyword = null, $current = null, $total = null ) {
		$step = array(
			'step'      => $step_type,
			'status'    => $status,
			'message'   => $message,
			'timestamp' => current_time( 'mysql' ),
		);

		if ( $endpoint ) {
			$step['endpoint'] = $endpoint;
		}

		if ( $keyword ) {
			$step['keyword'] = $keyword;
		}

		if ( $current !== null && $total !== null ) {
			$step['progress'] = array(
				'current' => $current,
				'total'   => $total,
			);
		}

		return $step;
	}

	private function process_keyword_ideas( $response, $seed_keyword ) {
		$task   = $response[0] ?? array();
		$result = $task['result'] ?? array();

		// Parse keyword_ideas response structure
		$items = array();
		if ( isset( $result[0]['items'] ) ) {
			$items = $result[0]['items'];
		} elseif ( isset( $result['items'] ) ) {
			$items = $result['items'];
		} elseif ( is_array( $result ) && ! empty( $result ) ) {
			// Result might be the items array directly
			$items = $result;
		}

		$this->logger->debug(
			'Processing keyword ideas response',
			array(
				'seed'                 => $seed_keyword,
				'task_keys'            => is_array( $task ) ? array_keys( $task ) : 'not_array',
				'result_type'          => gettype( $result ),
				'result_keys'          => is_array( $result ) ? array_keys( $result ) : 'not_array',
				'items_count'          => count( $items ),
				'first_item_structure' => ! empty( $items[0] ) ? array_keys( $items[0] ) : array(),
				'first_item_sample'    => ! empty( $items[0] ) ? $items[0] : null,
			)
		);

		$ideas = array();

		foreach ( $items as $item ) {
			// Parse keyword_ideas response structure (back to original)
			$keyword_data = $item['keyword_data'] ?? $item ?? array();
			$keyword      = $keyword_data['keyword'] ?? $item['keyword'] ?? '';

			// Don't expect volume data from keyword_ideas (it's often 0)
			// We'll enrich it separately with search_volume endpoint

			$this->logger->debug(
				'Parsing keyword ideas item',
				array(
					'keyword'           => $keyword,
					'item_keys'         => array_keys( $item ),
					'has_keyword_data'  => isset( $item['keyword_data'] ),
					'keyword_data_keys' => isset( $item['keyword_data'] ) ? array_keys( $item['keyword_data'] ) : array(),
				)
			);

			if ( empty( $keyword ) ) {
				$this->logger->debug( 'Skipping item - no keyword found', array( 'item_keys' => array_keys( $item ) ) );
				continue;
			}

			// Just save the keyword - enrichment will happen separately
			$search_intent = $this->detect_search_intent( $keyword );
			$content_type  = $this->detect_content_type( $keyword );

			$ideas[] = array(
				'keyword'        => $keyword,
				'search_volume'  => 0, // Will be enriched later
				'difficulty'     => null, // Will be enriched later
				'search_intent'  => $search_intent,
				'content_type'   => $content_type,
				'priority_score' => 0, // Will be calculated after enrichment
			);

			$this->logger->debug(
				'Added keyword idea for enrichment',
				array(
					'keyword'      => $keyword,
					'intent'       => $search_intent,
					'content_type' => $content_type,
				)
			);
		}

		// Sort by relevance score (volume + low difficulty)
		usort(
			$ideas,
			function ( $a, $b ) {
				$score_a = (float) ( $a['priority_score'] ?? 0 ) + ( (int) ( $a['search_volume'] ?? 0 ) / 100 );
				$score_b = (float) ( $b['priority_score'] ?? 0 ) + ( (int) ( $b['search_volume'] ?? 0 ) / 100 );
				return $score_b <=> $score_a;
			}
		);

		// Return only top 10 most relevant keywords per seed
		$filtered_ideas = array_slice( $ideas, 0, 10 );

		$this->logger->info(
			'Filtered keyword ideas',
			array(
				'seed'         => $seed_keyword,
				'total_found'  => count( $ideas ),
				'selected_top' => count( $filtered_ideas ),
			)
		);

		return $filtered_ideas;
	}

	private function enrich_keyword_idea( $keyword, $seed_keyword, $language_code = 'en', $location_code = 2840 ) {
		if ( ! $this->dataforseo_client ) {
			return null;
		}

		try {
			$volume_data = array(
				array(
					'location_code' => $location_code,
					'language_code' => $language_code,
					'keywords'      => array( $keyword ),
				),
			);

			$response = $this->dataforseo_client->request( 'POST', '/keywords_data/google_ads/search_volume/live', $volume_data );

			if ( is_wp_error( $response ) ) {
				throw new \Exception( $response->get_error_message() );
			}

			$task    = $response[0] ?? array();
			$items   = $task['result'] ?? array();
			$kw_data = $items[0] ?? array();

			$search_volume     = (int) ( $kw_data['search_volume'] ?? 0 );
			$competition_index = $kw_data['competition_index'] ?? null;
			$difficulty        = $competition_index !== null && is_numeric( $competition_index ) ? (int) $competition_index : null;

			$search_intent = $this->detect_search_intent( $keyword );
			$content_type  = $this->detect_content_type( $keyword );

			// Calculate priority score
			$priority = $this->calculate_priority_score( $search_volume, $difficulty );

			return array(
				'keyword'        => $keyword,
				'search_volume'  => $search_volume,
				'difficulty'     => $difficulty,
				'search_intent'  => $search_intent,
				'content_type'   => $content_type,
				'priority_score' => $priority,
				'language_code'  => $language_code,
				'location_code'  => $location_code,
			);

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Keyword enrichment failed',
				array(
					'keyword' => $keyword,
					'error'   => $e->getMessage(),
				)
			);
			return null;
		}
	}

	private function calculate_priority_score( $search_volume, $difficulty ) {
		$priority = 0;

		// Volume score (0-40 points)
		if ( $search_volume > 10000 ) {
			$priority += 40;
		} elseif ( $search_volume > 5000 ) {
			$priority += 30;
		} elseif ( $search_volume > 2000 ) {
			$priority += 20;
		} elseif ( $search_volume > 500 ) {
			$priority += 10;
		}

		// Difficulty score (0-30 points, inverted - lower difficulty = higher score)
		if ( $difficulty !== null ) {
			if ( $difficulty < 30 ) {
				$priority += 30;
			} elseif ( $difficulty < 50 ) {
				$priority += 20;
			} elseif ( $difficulty < 70 ) {
				$priority += 10;
			}
		}

		return $priority;
	}

	// AJAX Handler - Update Saved Keyword
	public function ajax_update_saved_keyword() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keyword_id   = absint( $_POST['keyword_id'] ?? 0 );
		$updates_raw  = wp_unslash( $_POST['updates'] ?? array() );
		$updates      = array();

		// Sanitize updates array
		if ( is_array( $updates_raw ) ) {
			foreach ( $updates_raw as $key => $value ) {
				$sanitized_key = sanitize_key( $key );

				// Sanitize value based on expected field types
				if ( in_array( $sanitized_key, array( 'volume', 'difficulty', 'cpc' ), true ) ) {
					$updates[ $sanitized_key ] = is_numeric( $value ) ? floatval( $value ) : 0;
				} elseif ( in_array( $sanitized_key, array( 'is_favorite', 'is_archived' ), true ) ) {
					$updates[ $sanitized_key ] = (bool) $value;
				} else {
					$updates[ $sanitized_key ] = sanitize_text_field( $value );
				}
			}
		}

		if ( ! $keyword_id ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Invalid keyword ID', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
			}

			$saved_manager = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );

			$result = $saved_manager->update_keyword( $keyword_id, $updates );

			if ( ! $result ) {
				throw new \Exception( 'Failed to update keyword' );
			}

			wp_send_json_success( array( 'message' => esc_html__( 'Keyword updated', 'wisewand-ai-content-writer-seo-content-generator' ) ) );

		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Bulk Delete Saved Keywords
	public function ajax_bulk_delete_saved_keywords() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keyword_ids = array_map( 'absint', $_POST['keyword_ids'] ?? array() );

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
			}

			$saved_manager = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );

			$deleted = $saved_manager->bulk_delete( $keyword_ids );

			wp_send_json_success( array( 'deleted' => $deleted ) );

		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Spy on Competitors
	public function ajax_spy_competitors() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		// Decode JSON first, then sanitize array elements
		$raw_competitors = isset( $_POST['competitors'] ) ? wp_unslash( $_POST['competitors'] ) : '[]';
		$competitors_raw = json_decode( $raw_competitors, true );
		if ( json_last_error() !== JSON_ERROR_NONE || ! is_array( $competitors_raw ) ) {
			$competitors = array();
		} else {
			// Sanitize each competitor URL
			$competitors = array_map( function( $competitor ) {
				if ( is_string( $competitor ) ) {
					return esc_url_raw( $competitor );
				}
				return '';
			}, $competitors_raw );
			// Remove empty values
			$competitors = array_filter( $competitors );
		}

		$analysis_type = sanitize_text_field( $_POST['analysis_type'] ?? 'hybrid' );
		$language_code = sanitize_text_field( $_POST['language_code'] ?? 'en' );
		$location_code = absint( $_POST['location_code'] ?? 2840 );

		// Decode JSON first, then sanitize filter values
		$raw_filters   = isset( $_POST['filters'] ) ? wp_unslash( $_POST['filters'] ) : '{}';
		$filters_raw   = json_decode( $raw_filters, true );
		if ( json_last_error() !== JSON_ERROR_NONE || ! is_array( $filters_raw ) ) {
			$filters = array();
		} else {
			// Sanitize each filter value based on expected types
			$filters = array();
			foreach ( $filters_raw as $key => $value ) {
				$sanitized_key = sanitize_key( $key );
				if ( is_numeric( $value ) ) {
					$filters[ $sanitized_key ] = floatval( $value );
				} else {
					$filters[ $sanitized_key ] = sanitize_text_field( $value );
				}
			}
		}
		$user_domain = sanitize_text_field( $_POST['user_domain'] ?? $this->domain );

		// Debug log
		$this->logger->info(
			'Competitor spy AJAX called',
			array(
				'competitors'           => $competitors,
				'analysis_type'         => $analysis_type,
				'user_domain'           => $user_domain,
				'has_dataforseo_client' => ! empty( $this->dataforseo_client ),
			)
		);

		if ( empty( $competitors ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'No competitors provided', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		// Re-initialize DataForSEO client if needed
		if ( ! $this->dataforseo_client ) {
			$this->init_dataforseo_client();
		}

		if ( ! $this->dataforseo_client ) {
			wp_send_json_error( array( 'message' => esc_html__( 'DataForSEO not configured. Please add credentials in Settings.', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Competitor_Spy' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Competitor_Spy.php';
			}

			$this->logger->info(
				'Creating Competitor_Spy instance',
				array(
					'user_domain' => $user_domain,
					'competitors' => $competitors,
				)
			);

			$spy = new \Wisewand\Utils\Competitor_Spy( $this->options, $this->logger, $this->dataforseo_client, $user_domain, $this->competitor_repository );

			$this->logger->info(
				'Starting competitor analysis',
				array(
					'analysis_type' => $analysis_type,
					'filters'       => $filters,
				)
			);

			// Run analysis with progress tracking
			$result = $spy->analyze_competitors( $competitors, $analysis_type, $filters, $language_code, $location_code );

			if ( is_wp_error( $result ) ) {
				$this->logger->error(
					'Competitor analysis returned error',
					array(
						'error_code'    => $result->get_error_code(),
						'error_message' => $result->get_error_message(),
					)
				);
				wp_send_json_error( array( 'message' => $result->get_error_message() ) );
				return;
			}

			$this->logger->info(
				'Competitor analysis completed',
				array(
					'total_found'    => $result['total_found'],
					'total_returned' => $result['total_returned'],
					'progress_steps' => count( $result['progress_steps'] ?? array() ),
				)
			);

			// Format response with progress data
			$response = array(
				'completed' => true,
				'results'   => $result,
				'progress'  => array(
					'percent'      => 100,
					'text'         => 'Completed (' . count( $result['keywords'] ) . ' keywords)',
					'current_task' => 'Analysis Complete',
					'detail'       => "Found {$result['total_found']} keywords, ranked top {$result['total_returned']} by priority",
				),
				'steps'     => $result['progress_steps'] ?? array(),
			);

			wp_send_json_success( $response );

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Competitor spy exception',
				array(
					'error' => $e->getMessage(),
					'file'  => $e->getFile(),
					'line'  => $e->getLine(),
					'trace' => $e->getTraceAsString(),
				)
			);
			wp_send_json_error( array( 'message' => esc_html( 'Exception: ' . $e->getMessage() . ' (Line: ' . $e->getLine() . ')' ) ) );
		} catch ( \Error $e ) {
			$this->logger->error(
				'Competitor spy fatal error',
				array(
					'error' => $e->getMessage(),
					'file'  => $e->getFile(),
					'line'  => $e->getLine(),
				)
			);
			wp_send_json_error( array( 'message' => esc_html( 'Fatal error: ' . $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Save Spy Keywords to Library
	public function ajax_save_spy_keywords() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		// Sanitize JSON input after decode
		$keywords_raw = json_decode( wp_unslash( $_POST['keywords'] ?? '[]' ), true );
		$keywords     = array();

		// Sanitize each keyword array element
		if ( is_array( $keywords_raw ) ) {
			foreach ( $keywords_raw as $kw ) {
				if ( is_array( $kw ) ) {
					$keywords[] = array(
						'keyword' => isset( $kw['keyword'] ) ? sanitize_text_field( $kw['keyword'] ) : '',
						'volume'  => isset( $kw['volume'] ) ? absint( $kw['volume'] ) : 0,
					);
				}
			}
		}

		$analysis_id   = sanitize_text_field( wp_unslash( $_POST['analysis_id'] ?? '' ) );
		$language_code = sanitize_text_field( $_POST['language_code'] ?? 'en' );
		$location_code = absint( $_POST['location_code'] ?? 2840 );

		if ( empty( $keywords ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'No keywords provided', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
			}

			$saved_manager = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );

			$saved   = 0;
			$skipped = 0;

			foreach ( $keywords as $kw ) {
				$keyword = $kw['keyword'] ?? '';
				if ( empty( $keyword ) ) {
					continue;
				}

				// CRITICAL: Check ALL 3 fields that match database UNIQUE constraint
				// UNIQUE KEY keyword_language (keyword, language_code, location_code)
				$existing_id = $saved_manager->get_keyword_id( $keyword, $language_code, $location_code );

				if ( $existing_id ) {
					++$skipped;
					$this->logger->info(
						'Keyword already exists, updating instead',
						array(
							'keyword'     => $keyword,
							'existing_id' => $existing_id,
						)
					);

					// Update existing keyword instead of skipping
				$this->keyword_repository->update_keyword(
					$existing_id,
					array(
						'keyword_source' => 'competitor_spy',
						'language_code'  => $language_code,
						'location_code'  => $location_code,
						'search_volume'  => $kw['search_volume'] ?? 0,
						'difficulty'     => $kw['difficulty'] ?? null,
						'cpc'            => $kw['cpc'] ?? null,
						'competition'    => $kw['competition'] ?? null,
						'search_intent'  => $kw['search_intent'] ?? 'informational',
						'priority_score' => $kw['priority_score'] ?? 0,
						'is_enriched'    => 1,
						'tags'           => 'competitor_spy',
						'notes'          => 'From: ' . ( $kw['competitor_domain'] ?? 'competitor' ) . ', Pos: ' . ( $kw['competitor_position'] ?? 'N/A' ),
					)
				);
					continue;
				}

				$keyword_id = $saved_manager->save_keyword( $keyword, 'competitor_spy', $kw['competitor_domain'] ?? null );

				if ( ! is_wp_error( $keyword_id ) ) {
					// Update with enriched data
					$update_result = $this->keyword_repository->update_keyword(
						$keyword_id,
						array(
							'language_code'  => $language_code,
							'location_code'  => $location_code,
							'search_volume'  => $kw['search_volume'] ?? 0,
							'difficulty'     => $kw['difficulty'] ?? null,
							'cpc'            => $kw['cpc'] ?? null,
							'competition'    => $kw['competition'] ?? null,
							'search_intent'  => $kw['search_intent'] ?? 'informational',
							'priority_score' => $kw['priority_score'] ?? 0,
							'is_enriched'    => 1,
							'tags'           => 'competitor_spy',
							'notes'          => 'From: ' . ( $kw['competitor_domain'] ?? 'competitor' ) . ', Pos: ' . ( $kw['competitor_position'] ?? 'N/A' ),
						)
					);

					if ( $update_result !== false ) {
						++$saved;
						$this->logger->info(
							'Competitor keyword saved and enriched',
							array(
								'keyword_id'  => $keyword_id,
								'keyword'     => $keyword,
								'volume'      => $kw['search_volume'] ?? 0,
								'is_enriched' => 1,
							)
						);
					} else {
						$this->logger->error(
							'Failed to update keyword',
							array(
								'keyword_id' => $keyword_id,
								'keyword'    => $keyword,
								'db_error'   => $this->keyword_repository->get_last_error(),
							)
						);
					}
				}
			}

			// Update analysis record
			if ( $analysis_id ) {
				$this->competitor_repository->update_analysis(
					$analysis_id,
					array( 'keywords_saved' => $saved )
				);
			}

			wp_send_json_success(
				array(
					'saved'   => $saved,
					'skipped' => $skipped,
					'total'   => count( $keywords ),
				)
			);

		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Save Column Preferences
	public function ajax_save_column_preferences() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$preferences = sanitize_text_field( $_POST['preferences'] ?? '' );

		if ( $preferences ) {
			update_user_meta( get_current_user_id(), 'wisewand_seo_table_columns', $preferences );
			wp_send_json_success( array( 'message' => 'Preferences saved' ) );
		} else {
			wp_send_json_error( array( 'message' => 'Invalid preferences' ) );
		}
	}


	// AJAX Handler - Remove Keyword from Calendar
	public function ajax_remove_from_calendar() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$calendar_id = absint( $_POST['calendar_id'] ?? 0 );

		if ( ! $calendar_id ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Invalid calendar ID', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
            $result = $this->calendar_repository->delete_entry( $calendar_id );

			if ( $result === false ) {
				throw new \Exception( 'Database error: ' . $this->calendar_repository->get_last_error() );
			}

			$this->logger->info( 'Keyword removed from calendar', array( 'calendar_id' => $calendar_id ) );

			wp_send_json_success( array( 'message' => 'Removed from calendar' ) );

		} catch ( \Exception $e ) {
			$this->logger->error( 'Failed to remove from calendar', array( 'error' => $e->getMessage() ) );
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	// AJAX Handler - Clear All Scheduled Entries
	public function ajax_clear_all_calendar() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		try {
		$result = $this->calendar_repository->clear_all_except_published();

		if ( $result === false ) {
			throw new \Exception( 'Database error: ' . $this->calendar_repository->get_last_error() );
		}

		$this->logger->info(
			'Calendar cleared completely',
			array(
				'entries_deleted' => $result,
				'kept_published'  => $this->calendar_repository->get_published_count(),
			)
		);

		wp_send_json_success(
			array(
				'message'       => "Cleared {$result} calendar entries. Published posts were kept.",
				'deleted_count' => $result,
			)
		);

	} catch ( \Exception $e ) {
		$this->logger->error( 'Failed to clear calendar', array( 'error' => $e->getMessage() ) );
		wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
	}
}
	private function get_wisewand_url( $content_type ) {
		$urls = array(
			'blog'      => 'https://app.wisewand.ai/articles/blog',
			'affiliate' => 'https://app.wisewand.ai/articles/affiliation',
			'category'  => 'https://app.wisewand.ai/e-commerce/category-page',
			'product'   => 'https://app.wisewand.ai/e-commerce/product-page',
		);
		return $urls[ $content_type ] ?? $urls['blog'];
	}

	/**
	 * Ensure editorial calendar CRON is scheduled after roadmap generation
	 */
	private function ensure_calendar_cron_scheduled() {
		if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_event' ) ) {
			return;
		}

		// Check if already scheduled
		if ( wp_next_scheduled( 'wisewand_editorial_calendar_cron' ) ) {
			$this->logger->debug(
				'CRON already scheduled',
				array(
					'next_run' => gmdate( 'Y-m-d H:i:s', wp_next_scheduled( 'wisewand_editorial_calendar_cron' ) ),
				)
			);
			return; // Already scheduled
		}

		// Schedule CRON to run daily at 2 AM
		$tomorrow_2am = strtotime( 'tomorrow 2:00 AM' );
		$scheduled    = wp_schedule_event( $tomorrow_2am, 'daily', 'wisewand_editorial_calendar_cron' );

		if ( $scheduled !== false ) {
			$this->logger->info(
				'Editorial calendar CRON auto-scheduled after roadmap generation',
				array(
					'first_run' => gmdate( 'Y-m-d H:i:s', $tomorrow_2am ),
				)
			);
		} else {
			$this->logger->error( 'Failed to auto-schedule CRON after roadmap generation' );
		}
	}

	// ========================================================================
	// DEDUPLICATION & CANNIBALIZATION PREVENTION
	// ========================================================================

	/**
	 * AJAX Handler - Scan for duplicate keywords and build clusters
	 */
	public function ajax_scan_duplicates() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		try {
			// Load dependencies
			if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
			}
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Deduplication_Engine' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Deduplication_Engine.php';
			}
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Cluster_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Cluster_Manager.php';
			}
			if ( ! class_exists( 'Wisewand\\Utils\\Similarity_Calculator' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Similarity_Calculator.php';
			}

			$saved_manager   = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );
			$cluster_manager = new \Wisewand\Utils\Keyword_Cluster_Manager( $this->options, $this->logger );
			$dedup_engine    = new \Wisewand\Utils\Keyword_Deduplication_Engine( $this->logger, 0.75, true );

			// Get all keywords
			$all_keywords = $saved_manager->get_all_saved_keywords();

			if ( empty( $all_keywords ) ) {
				wp_send_json_error( array( 'message' => esc_html__( 'No keywords in library', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
				return;
			}

			// Check for unenriched keywords and enrich them first
			$unenriched = array_filter(
				$all_keywords,
				function ( $kw ) {
					return empty( $kw['is_enriched'] ) || $kw['search_volume'] === null;
				}
			);

			$unenriched_ids   = array_column( $unenriched, 'id' );
			$enrichment_count = count( $unenriched_ids );

			// Clear existing clusters for fresh scan
			$cluster_manager->clear_all_clusters();

			// Return enrichment info for client-side handling
			wp_send_json_success(
				array(
					'needs_enrichment' => $enrichment_count > 0,
					'unenriched_ids'   => $unenriched_ids,
					'unenriched_count' => $enrichment_count,
					'total_keywords'   => count( $all_keywords ),
					'message'          => $enrichment_count > 0
						/* translators: %d: Number of unenriched keywords that need data */
						? sprintf( esc_html__( 'Found %d unenriched keywords. Enriching first...', 'wisewand-ai-content-writer-seo-content-generator' ), $enrichment_count )
						: __( 'Ready to scan', 'wisewand-ai-content-writer-seo-content-generator' ),
				)
			);

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

	/**
	 * AJAX Handler - Run actual clustering scan (after enrichment)
	 */
	public function ajax_run_clustering_scan() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		try {
			// Load dependencies
			if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
			}
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Deduplication_Engine' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Deduplication_Engine.php';
			}
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Cluster_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Cluster_Manager.php';
			}
			if ( ! class_exists( 'Wisewand\\Utils\\Similarity_Calculator' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Similarity_Calculator.php';
			}

			$saved_manager   = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );
			$cluster_manager = new \Wisewand\Utils\Keyword_Cluster_Manager( $this->options, $this->logger );
			$dedup_engine    = new \Wisewand\Utils\Keyword_Deduplication_Engine( $this->logger, 0.75, true );

			// Get all keywords (now enriched)
			$all_keywords = $saved_manager->get_all_saved_keywords();

			if ( empty( $all_keywords ) ) {
				wp_send_json_error( array( 'message' => esc_html__( 'No keywords in library', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
				return;
			}

			// Detect clusters with progress tracking
			$result = $dedup_engine->detect_clusters(
				$all_keywords,
				function ( $progress ) {
					// Progress tracking handled client-side
				}
			);

			// Save clusters to database
			$save_result = $cluster_manager->save_clusters( $result['clusters'], true );

			// Check if save had errors
			if ( ! empty( $save_result['errors'] ) ) {
				wp_send_json_error(
					array(
						'message'    => $save_result['errors'][0], // Return first error
						'all_errors' => $save_result['errors'],
					)
				);
				return;
			}

			wp_send_json_success(
				array(
					'stats'            => $result['stats'],
					'clusters_saved'   => $save_result['saved'],
					'clusters_updated' => $save_result['updated'],
					'message'          => sprintf(
						/* translators: 1: Number of keyword clusters found, 2: Number of keyword variants, 3: Number of unique keywords */
						__( 'Found %1$d clusters with %2$d variants. %3$d unique keywords.', 'wisewand-ai-content-writer-seo-content-generator' ),
						$result['stats']['clusters'],
						$result['stats']['variants'],
						$result['stats']['unique']
					),
				)
			);

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

	/**
	 * AJAX Handler - Get all clusters with filters
	 */
	public function ajax_get_clusters() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Cluster_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Cluster_Manager.php';
			}

			$cluster_manager = new \Wisewand\Utils\Keyword_Cluster_Manager( $this->options, $this->logger );

			$filters = array();

			if ( ! empty( $_POST['min_similarity'] ) ) {
				$filters['min_similarity'] = floatval( $_POST['min_similarity'] );
			}

			if ( ! empty( $_POST['min_size'] ) ) {
				$filters['min_size'] = intval( $_POST['min_size'] );
			}

			if ( ! empty( $_POST['language_code'] ) ) {
				$filters['language_code'] = sanitize_text_field( $_POST['language_code'] );
			}

			$clusters = $cluster_manager->get_clusters( $filters );
			$stats    = $cluster_manager->get_statistics();

			wp_send_json_success(
				array(
					'clusters' => $clusters,
					'stats'    => $stats,
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error( 'Failed to get clusters', array( 'error' => $e->getMessage() ) );
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	/**
	 * AJAX Handler - Set cluster primary
	 */
	public function ajax_set_cluster_primary() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$cluster_id     = absint( $_POST['cluster_id'] ?? 0 );
		$new_primary_id = absint( $_POST['new_primary_id'] ?? 0 );

		if ( ! $cluster_id || ! $new_primary_id ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Invalid parameters', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			return;
		}

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Cluster_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Cluster_Manager.php';
			}

			$cluster_manager = new \Wisewand\Utils\Keyword_Cluster_Manager( $this->options, $this->logger );

			$success = $cluster_manager->set_cluster_primary( $cluster_id, $new_primary_id );

			if ( $success ) {
				wp_send_json_success( array( 'message' => esc_html__( 'Primary keyword updated', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			} else {
				wp_send_json_error( array( 'message' => esc_html__( 'Failed to update primary', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			}
		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	/**
	 * AJAX Handler - Ungroup cluster (mark as not duplicates)
	 */
	public function ajax_ungroup_cluster() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$cluster_id = absint( $_POST['cluster_id'] ?? 0 );

		if ( ! $cluster_id ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Invalid cluster ID', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			return;
		}

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Cluster_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Cluster_Manager.php';
			}

			$cluster_manager = new \Wisewand\Utils\Keyword_Cluster_Manager( $this->options, $this->logger );

			$success = $cluster_manager->ungroup_cluster( $cluster_id );

			if ( $success ) {
				wp_send_json_success( array( 'message' => esc_html__( 'Cluster ungrouped successfully', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			} else {
				wp_send_json_error( array( 'message' => esc_html__( 'Failed to ungroup cluster', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			}
		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	/**
	 * AJAX Handler - Delete variant keywords (keep only primary)
	 */
	public function ajax_delete_cluster_variants() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$cluster_id = absint( $_POST['cluster_id'] ?? 0 );

		if ( ! $cluster_id ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Invalid cluster ID', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			return;
		}

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Cluster_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Cluster_Manager.php';
			}

			$cluster_manager = new \Wisewand\Utils\Keyword_Cluster_Manager( $this->options, $this->logger );

			$result = $cluster_manager->delete_variants( $cluster_id );

			if ( $result['success'] ) {
				wp_send_json_success(
					array(
						/* translators: %d: Number of keyword variants deleted from cluster */
						'message' => sprintf( esc_html__( '%d variant(s) deleted', 'wisewand-ai-content-writer-seo-content-generator' ), $result['deleted'] ),
						'deleted' => $result['deleted'],
					)
				);
			} else {
				wp_send_json_error( array( 'message' => $result['message'] ) );
			}
		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	/**
	 * AJAX Handler - Approve cluster
	 */
	public function ajax_approve_cluster() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$cluster_id = absint( $_POST['cluster_id'] ?? 0 );

		if ( ! $cluster_id ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Invalid cluster ID', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			return;
		}

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Cluster_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Cluster_Manager.php';
			}

			$cluster_manager = new \Wisewand\Utils\Keyword_Cluster_Manager( $this->options, $this->logger );

			$success = $cluster_manager->approve_cluster( $cluster_id );

			if ( $success ) {
				wp_send_json_success( array( 'message' => esc_html__( 'Cluster approved', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			} else {
				wp_send_json_error( array( 'message' => esc_html__( 'Failed to approve cluster', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			}
		} catch ( \Exception $e ) {
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	/**
	 * AJAX Handler - Check for cannibalization
	 */
	public function ajax_check_cannibalization() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keyword    = sanitize_text_field( $_POST['keyword'] ?? '' );
		$keyword_id = absint( $_POST['keyword_id'] ?? 0 );

		if ( empty( $keyword ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Keyword required', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			return;
		}

		try {
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Deduplication_Engine' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Deduplication_Engine.php';
			}

			$dedup_engine = new \Wisewand\Utils\Keyword_Deduplication_Engine( $this->logger, 0.75, true );

			$warnings = $dedup_engine->check_cannibalization( $keyword, $keyword_id );

			$has_warnings = ! empty( $warnings['calendar'] ) || ! empty( $warnings['posts'] ) || ! empty( $warnings['clusters'] );

			wp_send_json_success(
				array(
					'warnings'     => $warnings,
					'has_warnings' => $has_warnings,
					'risk_level'   => $this->calculate_risk_level( $warnings ),
				)
			);

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

	/**
	 * Calculate risk level from warnings
	 */
	private function calculate_risk_level( $warnings ) {
		$calendar_count = count( $warnings['calendar'] ?? array() );
		$posts_count    = count( $warnings['posts'] ?? array() );
		$clusters_count = count( $warnings['clusters'] ?? array() );

		$total = $calendar_count + $posts_count + $clusters_count;

		if ( $total === 0 ) {
			return 'none';
		} elseif ( $total === 1 ) {
			return 'low';
		} elseif ( $total <= 3 ) {
			return 'medium';
		} else {
			return 'high';
		}
	}

	/**
	 * AJAX Handler - Save keyword author and categories (inline edit)
	 */
	public function ajax_save_keyword_author_categories() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keyword_id = absint( $_POST['keyword_id'] ?? 0 );
		if ( ! $keyword_id ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Invalid keyword ID', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			return;
		}

		$update_data = array();

		// Update author if provided
		if ( isset( $_POST['author_id'] ) ) {
			$update_data['suggested_author_id'] = absint( $_POST['author_id'] );
		}

		// Update categories if provided
		if ( isset( $_POST['category_ids'] ) ) {
			$category_ids                        = array_map( 'absint', $_POST['category_ids'] );
			$update_data['suggested_categories'] = ! empty( $category_ids ) ? wp_json_encode( $category_ids ) : null;
		}

		if ( empty( $update_data ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'No data to update', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			return;
		}

		$result = $this->keyword_repository->update_keyword( $keyword_id, $update_data );

		if ( $result === false ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Database error', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
			return;
		}

		wp_send_json_success( array( 'message' => esc_html__( 'Saved', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
	}

	/**
	 * AJAX Handler - AI Match Categories to Keywords
	 */
	public function ajax_ai_match_categories() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		try {
			// Load dependencies
			if ( ! class_exists( 'Wisewand\\Utils\\OpenAI_Client' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/OpenAI_Client.php';
			}
			if ( ! class_exists( 'Wisewand\\Utils\\Category_Matcher' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Category_Matcher.php';
			}
			if ( ! class_exists( 'Wisewand\\Utils\\Saved_Keywords_Manager' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Saved_Keywords_Manager.php';
			}

			// Check if OpenAI is configured
			if ( ! \Wisewand\Utils\OpenAI_Client::is_configured() ) {
				wp_send_json_error(
					array(
						'message'      => esc_html__( 'OpenAI API key not configured. Please add your API key in Settings.', 'wisewand-ai-content-writer-seo-content-generator' ),
						'redirect_url' => admin_url( 'admin.php?page=wisewand-settings' ),
					)
				);
				return;
			}

			$category_matcher = new \Wisewand\Utils\Category_Matcher( $this->options, $this->logger );
			$saved_manager    = new \Wisewand\Utils\Saved_Keywords_Manager( $this->options, $this->logger, $this->dataforseo_client );

			// Check if this is a batch request (client-side batching to avoid timeout)
			$keyword_ids = isset( $_POST['keyword_ids'] ) ? array_map( 'absint', $_POST['keyword_ids'] ) : array();
			$filter_mode = sanitize_text_field( $_POST['filter_mode'] ?? 'uncategorized' );

			if ( empty( $keyword_ids ) ) {
				// Initial call - return keyword IDs for client-side batching
				$all_keywords = $saved_manager->get_all_saved_keywords();

				if ( empty( $all_keywords ) ) {
					wp_send_json_error( array( 'message' => esc_html__( 'No keywords in library', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
					return;
				}

				// Apply filter based on mode
				if ( $filter_mode === 'uncategorized' ) {
					// Filter to only keywords WITHOUT categories
					$all_keywords = array_filter(
						$all_keywords,
						function ( $kw ) {
							$suggested_categories = $kw['suggested_categories'] ?? null;

							// Check if empty or null
							if ( empty( $suggested_categories ) || $suggested_categories === '[]' || $suggested_categories === 'null' ) {
								return true;
							}

							// Check if JSON decodes to empty array
							$cat_ids = json_decode( $suggested_categories, true );
							return empty( $cat_ids );
						}
					);

					$all_keywords = array_values( $all_keywords ); // Re-index

					$this->logger->info(
						'AI Matching: Filtered to uncategorized keywords',
						array(
							'filter_mode'        => $filter_mode,
							'total_after_filter' => count( $all_keywords ),
						)
					);
				} elseif ( $filter_mode === 'exclude_ranking' ) {
					// Exclude keywords with ranking data
					$all_keywords = array_filter(
						$all_keywords,
						function ( $kw ) {
							$has_categories = ! empty( $kw['suggested_categories'] ) && $kw['suggested_categories'] !== '[]' && $kw['suggested_categories'] !== 'null';
							$has_ranking    = ! empty( $kw['page_url'] ) && ! empty( $kw['position_abs'] );

							// Keep if: no categories AND no ranking data
							return ! $has_categories && ! $has_ranking;
						}
					);

					$all_keywords = array_values( $all_keywords );

					$this->logger->info(
						'AI Matching: Excluded ranking keywords',
						array(
							'filter_mode'        => $filter_mode,
							'total_after_filter' => count( $all_keywords ),
						)
					);
				} elseif ( $filter_mode === 'only_ranking' ) {
					// Only keywords with ranking URLs
					$all_keywords = array_filter(
						$all_keywords,
						function ( $kw ) {
							return ! empty( $kw['page_url'] ) && ! empty( $kw['position_abs'] );
						}
					);

					$all_keywords = array_values( $all_keywords );

					$this->logger->info(
						'AI Matching: Only ranking keywords',
						array(
							'filter_mode'        => $filter_mode,
							'total_after_filter' => count( $all_keywords ),
						)
					);
				} elseif ( $filter_mode === 'without_position' ) {
					// Keywords never analyzed in SEO Intelligence
					$all_keywords = array_filter(
						$all_keywords,
						function ( $kw ) {
							$has_categories = ! empty( $kw['suggested_categories'] ) && $kw['suggested_categories'] !== '[]' && $kw['suggested_categories'] !== 'null';
							$has_position   = ! empty( $kw['position_abs'] ) || ! empty( $kw['page_url'] );

							return ! $has_categories && ! $has_position;
						}
					);

					$all_keywords = array_values( $all_keywords );

					$this->logger->info(
						'AI Matching: Only keywords without position data',
						array(
							'filter_mode'        => $filter_mode,
							'total_after_filter' => count( $all_keywords ),
						)
					);
				}
				// else: 'all' mode - use all keywords (no filter)

				$all_keyword_ids = array_column( $all_keywords, 'id' );

				wp_send_json_success(
					array(
						'mode'           => 'init',
						'keyword_ids'    => $all_keyword_ids,
						'total_keywords' => count( $all_keyword_ids ),
						'filter_mode'    => $filter_mode,
						'batch_size'     => 10, // Reduced from 20 to 10 for better reliability
						'total_batches'  => ceil( count( $all_keyword_ids ) / 10 ),
						'message'        => 'Ready for client-side batch processing',
					)
				);
				return;
			}

			// Process this specific batch
			$batch_keywords = $this->keyword_repository->get_keywords_by_ids( $keyword_ids );

			if ( empty( $batch_keywords ) ) {
				wp_send_json_error( array( 'message' => esc_html__( 'No keywords found for this batch', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
				return;
			}

			// Determine if we should respect manual categories
			// Only "all" mode ignores manual categories (re-match everything)
			$respect_manual = ( $filter_mode !== 'all' );

			$this->logger->info(
				'Processing AI matching batch',
				array(
					'filter_mode'    => $filter_mode,
					'respect_manual' => $respect_manual,
					'batch_size'     => count( $batch_keywords ),
				)
			);

			// Match this batch to categories using AI with smart URL detection
			$matches = $category_matcher->match_keywords_to_categories( $batch_keywords, null, $respect_manual );

			if ( is_wp_error( $matches ) ) {
				wp_send_json_error(
					array(
						'message'        => $matches->get_error_message(),
						'batch_keywords' => count( $batch_keywords ),
					)
				);
				return;
			}

			// Save matches to database with author assignment
			$saved_count = $category_matcher->save_matches( $matches, $batch_keywords );

			// Return batch result
			wp_send_json_success(
				array(
					'mode'          => 'batch_complete',
					'batch_size'    => count( $batch_keywords ),
					'matched_count' => count( $matches ),
					'saved_count'   => $saved_count,
					'message'       => sprintf(
						/* translators: 1: Number of keywords matched with categories, 2: Total number of keywords in batch */
						__( 'Batch complete: %1$d/%2$d matched', 'wisewand-ai-content-writer-seo-content-generator' ),
						count( $matches ),
						count( $batch_keywords )
					),
				)
			);

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

	/**
	 * AJAX Handler - Count keywords for AI matching based on filter mode
	 */
	public function ajax_count_keywords_for_matching() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		try {
		$filter_mode = sanitize_text_field( $_POST['filter_mode'] ?? 'uncategorized' );

		$stats = $this->keyword_repository->get_matching_statistics( $filter_mode );

		$this->logger->debug(
			'Count keywords for matching',
			array(
				'filter_mode'    => $filter_mode,
				'total'          => $stats['total'],
				'auto_matchable' => $stats['auto_matchable'],
				'ai_needed'      => $stats['ai_needed'],
			)
		);

		wp_send_json_success(
			array(
				'total_keywords' => $stats['total'],
				'auto_matchable' => $stats['auto_matchable'],
				'ai_needed'      => $stats['ai_needed'],
				'filter_mode'    => $filter_mode,
			)
		);

		} catch ( \Exception $e ) {
			$this->logger->error( 'Failed to count keywords', array( 'error' => $e->getMessage() ) );
			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}

	/**
	 * Copy ranking data from SEO Intelligence table to saved keywords
	 *
	 * When keywords are saved to library from SEO Intelligence, this copies
	 * the ranking URL and position data for later use in auto-categorization.
	 *
	 * @param array  $keywords Array of keyword strings that were saved
	 * @param string $language_code Language code
	 * @param int    $location_code Location code
	 */
	private function copy_ranking_data_to_saved_keywords( $keywords, $language_code, $location_code ) {
		if ( empty( $keywords ) ) {
			return;
		}

		$this->logger->debug(
			'Copying ranking data to saved keywords',
			array(
				'keywords_count' => count( $keywords ),
				'language'       => $language_code,
				'location'       => $location_code,
			)
		);

		$copied_count = 0;

		foreach ( $keywords as $keyword ) {
			// Get latest SEO ranking data for this keyword
			$seo_data = $this->seo_repository->get_latest_keyword_ranking( $keyword, $language_code, $location_code );

			if ( $seo_data && ! empty( $seo_data->page_url ) ) {
				// Update saved keyword with ranking data
				$updated = $this->keyword_repository->update_keyword_by_text(
					$keyword,
					$language_code,
					array(
						'page_url'     => $seo_data->page_url,
						'position_abs' => $seo_data->position_abs,
					)
				);

				if ( $updated !== false ) {
					++$copied_count;
					$this->logger->debug(
						'Copied ranking data',
						array(
							'keyword'  => $keyword,
							'page_url' => $seo_data->page_url,
							'position' => $seo_data->position_abs,
						)
					);
				}
			}
		}

		$this->logger->info(
			'Ranking data copy complete',
			array(
				'total_keywords' => count( $keywords ),
				'copied'         => $copied_count,
				'skipped'        => count( $keywords ) - $copied_count,
			)
		);
	}

	/**
	 * AJAX Handler - Check ranking conflicts before scheduling
	 *
	 * Prevents keyword cannibalization by checking if similar keywords
	 * are already ranking in positions 1-50
	 */
	public function ajax_check_ranking_conflicts() {
		check_ajax_referer( 'wisewand_seo_real', 'nonce' );

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

		$keyword       = sanitize_text_field( $_POST['keyword'] ?? '' );
		$language_code = sanitize_text_field( $_POST['language_code'] ?? 'en' );

		if ( empty( $keyword ) ) {
			wp_send_json_error( array( 'message' => esc_html__( 'Keyword is required', 'wisewand-ai-content-writer-seo-content-generator' ) ) );
		}

		try {
			// Initialize deduplication engine
			if ( ! class_exists( 'Wisewand\\Utils\\Keyword_Deduplication_Engine' ) ) {
				require_once WISEWAND_PLUGIN_DIR . 'includes/Utils/Keyword_Deduplication_Engine.php';
			}

			$dedup_engine = new \Wisewand\Utils\Keyword_Deduplication_Engine( $this->logger );

			// Check for ranking conflicts
			$conflicts = $dedup_engine->check_ranking_conflicts( $keyword, $language_code );

			wp_send_json_success(
				array(
					'keyword'        => $keyword,
					'conflicts'      => $conflicts,
					'has_conflicts'  => $conflicts['has_conflicts'],
					'risk_level'     => $conflicts['risk_level'],
					'recommendation' => $conflicts['recommendation'],
				)
			);

		} catch ( \Exception $e ) {
			$this->logger->error(
				'Failed to check ranking conflicts',
				array(
					'keyword' => $keyword,
					'error'   => $e->getMessage(),
				)
			);

			wp_send_json_error( array( 'message' => esc_html( $e->getMessage() ) ) );
		}
	}
}