(function($) {
	'use strict';

	// Chunking constants — must match PHP SiteStaffr_Content_Chunker.
	var TARGET_CHUNK_CHARS = 200 * 4; // 800 chars
	var MAX_CHUNK_CHARS    = 300 * 4; // 1200 chars
	var OVERLAP_CHARS      = 100 * 4; // 400 chars

	var contentItems = [];
	var isSyncing = false;

	function init() {
		logDiagnostics();
		loadContentList();
		bindEvents();
	}

	function logDiagnostics() {
		var version = (sitestaffr_knowledge && sitestaffr_knowledge.version) || 'unknown';
		console.log('[SiteStaffr] Plugin version: ' + version);
		$.post(sitestaffr_knowledge.ajax_url, {
			action: 'sitestaffr_knowledge_schema_check',
			nonce:  sitestaffr_knowledge.nonce
		}, function(response) {
			if (response && response.success) {
				console.log('[SiteStaffr] Knowledge schema diagnostic:', response.data);
				if (!response.data.chunk_title_column) {
					console.warn('[SiteStaffr] chunk_title column MISSING — migration did not run. Deactivate + reactivate the plugin to trigger dbDelta.');
				}
			} else {
				console.warn('[SiteStaffr] Knowledge schema diagnostic failed:', response);
			}
		}).fail(function(xhr) {
			console.warn('[SiteStaffr] Knowledge schema diagnostic request failed:', xhr && xhr.status);
		});
	}

	function bindEvents() {
		$('#sync-knowledge-btn').on('click', startSync);
		$('#unsync-knowledge-btn').on('click', startUnsync);
		$('#diagnose-knowledge-btn').on('click', runDiagnostic);

		// --- Manual knowledge form ---
		$('#sitestaffr-add-standalone-knowledge').on('click', function() {
			var $form = $('#sitestaffr-manual-knowledge-form');
			$form.is(':visible') ? $form.slideUp(150) : $form.slideDown(150);
		});

		$('#sitestaffr-cancel-manual-knowledge').on('click', function() {
			$('#sitestaffr-manual-knowledge-form').slideUp(150);
			$('#sitestaffr-manual-title').val('');
			$('#sitestaffr-manual-text').val('');
		});

		$('#sitestaffr-save-manual-knowledge').on('click', function() {
			var title = $('#sitestaffr-manual-title').val().trim();
			var text  = $('#sitestaffr-manual-text').val().trim();
			if (!text) {
				alert('Knowledge text is required.');
				return;
			}
			var $btn = $(this).prop('disabled', true).text('Saving...');
			$.post(sitestaffr_knowledge.ajax_url, {
				action:  'sitestaffr_add_manual_knowledge',
				nonce:   sitestaffr_knowledge.manual_nonce,
				title:   title,
				text:    text,
				post_id: 0
			}, function(response) {
				if (response.success) {
					$('#sitestaffr-manual-knowledge-form').slideUp(150);
					$('#sitestaffr-manual-title').val('');
					$('#sitestaffr-manual-text').val('');
					loadContentList();
				} else {
					alert(response.data && response.data.message ? response.data.message : 'Failed to save.');
				}
			}).fail(function() {
				alert('Request failed. Please try again.');
			}).always(function() {
				$btn.prop('disabled', false).text('Save');
			});
		});

		$(document).on('click', '.sitestaffr-delete-manual-chunk', function() {
			var chunkId = $(this).data('chunk-id');
			if (!chunkId || !confirm('Delete this manual knowledge entry?')) return;
			var $chunkEl = $(this).closest('.sitestaffr-manual-chunk-item');
			$.post(sitestaffr_knowledge.ajax_url, {
				action:   'sitestaffr_delete_manual_knowledge',
				nonce:    sitestaffr_knowledge.manual_nonce,
				chunk_id: chunkId
			}, function(response) {
				if (response.success) {
					if ($chunkEl.length) {
						$chunkEl.fadeOut(200, function() { $(this).remove(); });
					} else {
						loadContentList();
					}
				} else {
					alert(response.data && response.data.message ? response.data.message : 'Delete failed.');
				}
			}).fail(function() {
				alert('Request failed. Please try again.');
			});
		});

		$(document).on('change', '.select-all-group', function() {
			var group = $(this).data('group');
			var checked = $(this).prop('checked');
			$('#' + group + '-list').find('input[type="checkbox"]').prop('checked', checked);
			updateSyncButton();
		});

		$(document).on('change', '.content-item input[type="checkbox"]', function() {
			updateSyncButton();
			var group = $(this).closest('.sitestaffr-knowledge-section').find('.select-all-group').data('group');
			var allInGroup = $('#' + group + '-list input[type="checkbox"]');
			var checkedInGroup = allInGroup.filter(':checked');
			$('.select-all-group[data-group="' + group + '"]').prop('checked', allInGroup.length === checkedInGroup.length);
		});

		$(document).on('click', '.view-chunks-link', function(e) {
			e.preventDefault();
			var postId = $(this).data('id');
			var $preview = $('.chunk-preview[data-id="' + postId + '"]');

			if ($preview.is(':visible')) {
				$preview.slideUp(150);
				$(this).text('View chunks');
				return;
			}

			$(this).text('Loading...');
			var $link = $(this);

			$.ajax({
				url: sitestaffr_knowledge.rest_url + 'knowledge/chunks?post_id=' + postId,
				method: 'GET',
				beforeSend: function(xhr) {
					xhr.setRequestHeader('X-WP-Nonce', sitestaffr_knowledge.rest_nonce);
				},
				success: function(response) {
					if (response.success && response.chunks.length > 0) {
						$preview.empty();
						response.chunks.forEach(function(chunk, i) {
							var badge = '';
							var deleteBtn = '';
							var hitBadge = '';
							if (chunk.source === 'manual') {
								badge = ' <span class="sitestaffr-chunk-badge sitestaffr-chunk-manual">Manual</span>';
								deleteBtn = ' <button type="button" class="button-link sitestaffr-delete-manual-chunk" data-chunk-id="' + chunk.id + '" style="color: #b32d2e;">Delete</button>';
							}
							if (chunk.hit_count > 0) {
								hitBadge = ' <span class="sitestaffr-chunk-badge sitestaffr-chunk-hits" title="Search hits">' + chunk.hit_count + ' hit' + (chunk.hit_count !== 1 ? 's' : '') + '</span>';
							}
							var $item = $('<div class="sitestaffr-manual-chunk-item" style="margin-bottom: 10px; padding-bottom: 8px; border-bottom: 1px solid #f0f0f0;">');
							var $header = $('<div style="font-weight: 600; margin-bottom: 4px;">');
							$header.append('Chunk ' + i + ' (' + $('<span>').text(chunk.post_title).html() + ')' + badge + hitBadge + deleteBtn);
							if (chunk.chunk_title) {
								var $aiTitle = $('<div style="font-weight: 500; color: #00838F; font-size: 12px; margin-bottom: 4px;">');
								$aiTitle.text('AI title: ' + chunk.chunk_title);
								$header.append($aiTitle);
							}
							var $body = $('<div style="white-space: pre-wrap; word-break: break-word;">').text(chunk.content);
							$item.append($header).append($body);
							$preview.append($item);
						});
						$preview.slideDown(150);
						$link.text('Hide chunks');
					} else {
						$preview.text('No chunks found.').slideDown(150);
						$link.text('Hide chunks');
					}
				},
				error: function() {
					$preview.text('Failed to load chunks.').slideDown(150);
					$link.text('Hide chunks');
				}
			});
		});
	}

	// --- Client-side content extraction ---

	/**
	 * Extract plain text from raw WordPress post_content.
	 * Runs entirely in the browser — no PHP processing needed.
	 */
	function extractText(rawContent) {
		// Strip Gutenberg block comments.
		var content = rawContent.replace(/<!--\s*\/?wp:\S[^]*?-->/g, '');

		// Strip shortcode tags (keep inner content).
		content = content.replace(/\[\/?[a-zA-Z_][a-zA-Z0-9_-]*[^\]]*\]/g, '');

		// Strip HTML using DOM — safe and handles all edge cases.
		var tmp = document.createElement('div');
		tmp.innerHTML = content;
		var text = tmp.textContent || tmp.innerText || '';

		// Normalize whitespace.
		text = text.replace(/\s+/g, ' ').trim();

		return text;
	}

	/**
	 * Chunk text into overlapping segments.
	 * Mirrors PHP SiteStaffr_Content_Chunker::chunk_text().
	 */
	function chunkText(text, postId) {
		if (text.length <= MAX_CHUNK_CHARS) {
			return [{ chunk_index: 0, text: text }];
		}

		var chunks = [];
		var position = 0;
		var chunkIndex = 0;

		while (position < text.length) {
			var end = Math.min(position + TARGET_CHUNK_CHARS, text.length);

			// Try to break at a sentence boundary.
			if (end < text.length) {
				var searchStart = Math.max(end - 200, position);
				var segment = text.substring(searchStart, end);
				var lastPeriod = segment.lastIndexOf('. ');
				if (lastPeriod !== -1) {
					end = searchStart + lastPeriod + 2;
				}
			}

			var chunkStr = text.substring(position, end).trim();
			if (chunkStr.length > 0) {
				chunks.push({ chunk_index: chunkIndex, text: chunkStr });
				chunkIndex++;
			}

			// Reached end of text — all content covered.
			if (end >= text.length) break;

			position = end - OVERLAP_CHARS;
		}

		return chunks;
	}

	// --- AJAX / REST helpers ---

	function loadContentList() {
		$.post(sitestaffr_knowledge.ajax_url, {
			action: 'sitestaffr_knowledge_get_content_list',
			nonce: sitestaffr_knowledge.nonce
		}, function(response) {
			if (response.success) {
				contentItems = response.data.items;
				renderContentList(response.data.items);
				updateStats(response.data.stats);
			} else {
				showError(response.data.message || 'Failed to load content.');
			}
		}).fail(function() {
			showError('Failed to load content list.');
		});
	}

	function renderContentList(items) {
		var $sections = $('#knowledge-content-sections');
		$sections.empty();

		// Group items by post_type.
		var groups = {};
		items.forEach(function(item) {
			var type = item.post_type || 'page';
			if (!groups[type]) groups[type] = [];
			groups[type].push(item);
		});

		// Render pages first, posts second, then CPTs alphabetically.
		var order = ['page', 'post'];
		Object.keys(groups).sort().forEach(function(type) {
			if (order.indexOf(type) === -1) order.push(type);
		});

		order.forEach(function(type) {
			if (!groups[type] || groups[type].length === 0) return;

			var count = groups[type].length;
			var label = type.charAt(0).toUpperCase() + type.slice(1).replace(/-/g, ' ') + 's';
			var containerId = type + '-list';

			var section = '<div class="sitestaffr-knowledge-section">' +
				'<h2 class="sitestaffr-knowledge-section-toggle" style="cursor: pointer; user-select: none;">' +
				'<span class="toggle-arrow" style="display: inline-block; transition: transform 0.2s; margin-right: 4px;">&#9654;</span>' +
				'<label style="cursor: pointer;" onclick="event.stopPropagation();">' +
				'<input type="checkbox" class="select-all-group" data-group="' + type + '">' +
				' ' + $('<span>').text(label).html() +
				' <span style="font-weight: normal; color: #666; font-size: 13px;">(' + count + ')</span>' +
				'</label></h2>' +
				'<div class="content-list" id="' + containerId + '" style="display: none;"></div>' +
				'</div>';
			$sections.append(section);

			renderGroup(containerId, groups[type]);
		});

		// Bind collapse/expand toggle.
		$sections.find('.sitestaffr-knowledge-section-toggle').on('click', function(e) {
			if ($(e.target).is('input[type="checkbox"]')) return;
			var $list = $(this).next('.content-list');
			var $arrow = $(this).find('.toggle-arrow');
			$list.slideToggle(200);
			$arrow.css('transform', $list.is(':visible') ? 'rotate(90deg)' : 'rotate(0deg)');
		});

		updateSyncButton();
		renderManualKnowledgeSection();
	}

	function renderManualKnowledgeSection() {
		var $sections = $('#knowledge-content-sections');

		// Remove existing manual section before re-rendering.
		$('#sitestaffr-manual-knowledge-section').remove();

		$.ajax({
			url: sitestaffr_knowledge.rest_url + 'knowledge/chunks?post_id=0',
			method: 'GET',
			beforeSend: function(xhr) {
				xhr.setRequestHeader('X-WP-Nonce', sitestaffr_knowledge.rest_nonce);
			},
			success: function(response) {
				var chunks = (response.success && response.chunks) ? response.chunks : [];
				var $section = $('<div class="sitestaffr-knowledge-section" id="sitestaffr-manual-knowledge-section">');
				var $header = $('<h2 class="sitestaffr-knowledge-section-toggle" style="cursor: pointer; user-select: none;">');
				$header.html(
					'<span class="toggle-arrow" style="display: inline-block; transition: transform 0.2s; margin-right: 4px;">&#9654;</span>' +
					'Manual Knowledge <span style="font-weight: normal; color: #666; font-size: 13px;">(' + chunks.length + ')</span>'
				);
				var $list = $('<div class="content-list" style="display: none; padding-top: 4px;">');

				if (chunks.length === 0) {
					$list.html('<p class="empty">No manual knowledge entries yet. Use the Add Knowledge button above.</p>');
				} else {
					chunks.forEach(function(chunk) {
						var $item = $('<div class="sitestaffr-manual-chunk-item content-item" style="flex-direction: column; align-items: flex-start;" data-chunk-id="' + chunk.id + '">');
						var $row = $('<div style="display: flex; align-items: center; gap: 8px; width: 100%;">');
						var hitBadge = chunk.hit_count > 0
							? ' <span class="sitestaffr-chunk-badge sitestaffr-chunk-hits" title="Search hits">' + chunk.hit_count + ' hit' + (chunk.hit_count !== 1 ? 's' : '') + '</span>'
							: '';
						var $title = $('<span class="title">').html(
							$('<span>').text(chunk.post_title).html() +
							' <span class="sitestaffr-chunk-badge sitestaffr-chunk-manual">Manual</span>' + hitBadge
						);
						var $del = $('<button type="button" class="button-link sitestaffr-delete-manual-chunk" style="color: #b32d2e; margin-left: auto;">Delete</button>').data('chunk-id', chunk.id);
						$row.append($title).append($del);
						var $body = $('<div style="font-size: 12px; color: #555; margin-top: 4px; white-space: pre-wrap; word-break: break-word;">').text(chunk.content);
						$item.append($row).append($body);
						$list.append($item);
					});
				}

				$header.on('click', function() {
					var $arrow = $(this).find('.toggle-arrow');
					$list.slideToggle(200);
					$arrow.css('transform', $list.is(':visible') ? 'rotate(90deg)' : 'rotate(0deg)');
				});

				$section.append($header).append($list);
				$sections.append($section);
			}
		});
	}

	function renderGroup(containerId, items) {
		var $container = $('#' + containerId);
		$container.empty();

		if (items.length === 0) {
			$container.html('<p class="empty">No published content found.</p>');
			return;
		}

		items.forEach(function(item) {
			var modified = item.modified ? new Date(item.modified).toLocaleDateString() : '';
			var viewLink = item.status === 'synced' ? ' <a href="#" class="view-chunks-link" data-id="' + item.id + '" style="font-size: 12px; margin-left: 8px;">View chunks</a>' : '';
			var html = '<div class="content-item" data-id="' + item.id + '" data-status="' + item.status + '">' +
				'<label>' +
				'<input type="checkbox" value="' + item.id + '">' +
				'<span class="status-dot ' + item.status + '" title="' + item.status + '"></span>' +
				'<span class="title">' + $('<span>').text(item.title || '(no title)').html() + viewLink + '</span>' +
				'</label>' +
				'<span class="modified">' + modified + '</span>' +
				'<div class="chunk-preview" data-id="' + item.id + '" style="display:none; margin: 8px 0 8px 28px; padding: 10px; background: #f9f9f9; border: 1px solid #e0e0e0; border-radius: 4px; font-size: 13px; max-height: 300px; overflow-y: auto; white-space: pre-wrap; word-break: break-word;"></div>' +
				'</div>';
			$container.append(html);
		});
	}

	function updateStats(stats) {
		$('#stat-chunks').text(stats.total_chunks || 0);
		$('#stat-posts').text(stats.total_posts || 0);
		$('#stat-last-sync').text(stats.last_synced ? new Date(stats.last_synced).toLocaleDateString() : 'Never');
	}

	function updateSyncButton() {
		var checked = getCheckedItems();
		var $btn = $('#sync-knowledge-btn');
		var $unsyncBtn = $('#unsync-knowledge-btn');
		$btn.prop('disabled', checked.length === 0 || isSyncing);
		if (checked.length > 0 && !isSyncing) {
			$btn.text('Sync Knowledge (' + checked.length + ')');
		} else if (!isSyncing) {
			$btn.text('Sync Knowledge');
		}

		// Unsync button: only enabled when checked items include synced ones.
		var syncedChecked = 0;
		$('.content-item input[type="checkbox"]:checked').each(function() {
			if ($(this).closest('.content-item').attr('data-status') === 'synced') {
				syncedChecked++;
			}
		});
		$unsyncBtn.prop('disabled', syncedChecked === 0 || isSyncing);
		if (syncedChecked > 0 && !isSyncing) {
			$unsyncBtn.text('Unsync Selected (' + syncedChecked + ')');
		} else {
			$unsyncBtn.text('Unsync Selected');
		}
	}

	function getCheckedItems() {
		var ids = [];
		$('.content-item input[type="checkbox"]:checked').each(function() {
			ids.push(parseInt($(this).val(), 10));
		});
		return ids;
	}

	// --- Diagnostic ---

	function runDiagnostic() {
		var ids = getCheckedItems();
		// Use first checked item, or first content item with content.
		var testId = ids.length > 0 ? ids[0] : (contentItems.length > 0 ? contentItems[0].id : 0);
		if (!testId) {
			showError('No pages available to diagnose.');
			return;
		}

		$('#sync-status').html('<strong>Running diagnostic on post ' + testId + '...</strong>');
		var results = [];

		var levels = [
			{ level: 0, label: 'REST API routing (static response)' },
			{ level: 1, label: 'DB metadata query' },
			{ level: 2, label: 'Post content extraction (DB)' },
			{ level: 3, label: 'Text extraction (DB + rendered page fallback)' },
			{ level: 4, label: 'Chunking (PHP string split)' },
			{ level: 5, label: 'Chunk + DB delete old chunks' },
			{ level: 6, label: 'Chunk + delete + set_transient' },
			{ level: 99, label: 'Full sync (all operations)' }
		];

		var current = 0;

		function testNext() {
			if (current >= levels.length) {
				var html = '<strong>Diagnostic results (post ' + testId + '):</strong><br>';
				results.forEach(function(r) {
					var color = r.ok ? '#065f46' : '#991b1b';
					html += '<span style="color:' + color + ';">' + (r.ok ? 'PASS' : 'FAIL') + '</span> ' + r.label;
					if (r.detail) html += ' — ' + r.detail;
					html += '<br>';
				});
				$('#sync-status').html(html);
				return;
			}

			var lvl = levels[current];
			var payload = { post_id: testId, debug_level: lvl.level };

			$.ajax({
				url: sitestaffr_knowledge.rest_url + 'knowledge/prepare',
				method: 'POST',
				contentType: 'application/json',
				data: JSON.stringify(payload),
				beforeSend: function(xhr) {
					xhr.setRequestHeader('X-WP-Nonce', sitestaffr_knowledge.rest_nonce);
				},
				success: function(response) {
					var ok = response.success !== false;
					var detail = '';
					if (response.content_bytes) detail = response.content_bytes + ' bytes in DB';
					if (response.content_length) detail = response.content_length + ' chars loaded';
					if (response.text_length !== undefined) detail = response.text_length + ' chars' + (response.source ? ' (' + response.source + ')' : '');
					if (response.total_chunks) detail = response.total_chunks + ' chunks';
					if (response.total) detail = response.total + ' chunks';
					if (response.message && !ok) detail = response.message;
					if (response.fetch_debug) {
						var fd = response.fetch_debug;
						var parts = [];
						if (fd.error) parts.push('fetch error: ' + fd.error);
						if (fd.http_status) parts.push('HTTP ' + fd.http_status);
						if (fd.html_length !== undefined) parts.push('html=' + fd.html_length);
						if (fd.extracted_length !== undefined) parts.push('dom=' + fd.extracted_length);
						if (fd.regex_fallback_length !== undefined) parts.push('regex=' + fd.regex_fallback_length);
						if (fd.url) parts.push(fd.url);
						if (parts.length) detail = (detail ? detail + ' | ' : '') + parts.join(', ');
					}
					if (response.debug) detail = (detail ? detail + ', ' : '') + response.debug;
					results.push({ ok: ok, label: lvl.label, detail: detail });
					if (!ok) {
						current = levels.length;
					} else {
						current++;
					}
					setTimeout(testNext, 300);
				},
				error: function(xhr) {
					results.push({ ok: false, label: lvl.label, detail: 'HTTP ' + xhr.status });
					// Stop on first failure — subsequent levels will also fail.
					current = levels.length;
					testNext();
				}
			});
		}

		testNext();
	}

	// --- Unsync ---

	function startUnsync() {
		var syncedIds = [];
		$('.content-item input[type="checkbox"]:checked').each(function() {
			var $item = $(this).closest('.content-item');
			if ($item.attr('data-status') === 'synced') {
				syncedIds.push(parseInt($(this).val(), 10));
			}
		});

		if (syncedIds.length === 0 || isSyncing) return;

		if (!confirm('Remove knowledge chunks for ' + syncedIds.length + ' item(s)? This cannot be undone.')) return;

		$('#sync-status').text('Removing...');

		$.ajax({
			url: sitestaffr_knowledge.rest_url + 'knowledge/unsync',
			method: 'POST',
			contentType: 'application/json',
			data: JSON.stringify({ post_ids: syncedIds }),
			beforeSend: function(xhr) {
				xhr.setRequestHeader('X-WP-Nonce', sitestaffr_knowledge.rest_nonce);
			},
			success: function(response) {
				if (response.success) {
					$('#sync-status').text('Removed ' + (response.removed || syncedIds.length) + ' item(s).');
					loadContentList();
				} else {
					$('#sync-status').html('<span style="color: #991b1b;">' + (response.message || 'Unsync failed.') + '</span>');
				}
			},
			error: function() {
				$('#sync-status').html('<span style="color: #991b1b;">Unsync request failed.</span>');
			}
		});
	}

	// --- Sync orchestration ---

	/**
	 * Two-phase sync:
	 * 1. PHP extracts text (strip_tags) + chunks it (lightweight, no WP hooks)
	 * 2. JS sends each chunk to PHP for embedding + DB save
	 */
	function startSync() {
		var ids = getCheckedItems();
		if (ids.length === 0 || isSyncing) return;

		isSyncing = true;
		var $btn = $('#sync-knowledge-btn');
		$btn.prop('disabled', true).text('Syncing...');
		$('#sync-progress').show();
		$('#sync-status').text('');
		$('#progress-fill').css('width', '0%');

		var totalPosts = ids.length;
		var postsDone = 0;
		var totalChunks = 0;
		var errors = [];

		function syncNextPost() {
			if (postsDone >= totalPosts) {
				finishSync(totalChunks, errors);
				return;
			}

			var postId = ids[postsDone];
			updateProgress('Preparing ' + (postsDone + 1) + ' of ' + totalPosts + '...');

			// Phase 1: PHP extracts text + chunks it (strip_tags, no WP hooks).
			$.ajax({
				url: sitestaffr_knowledge.rest_url + 'knowledge/prepare',
				method: 'POST',
				contentType: 'application/json',
				data: JSON.stringify({ post_id: postId }),
				beforeSend: function(xhr) {
					xhr.setRequestHeader('X-WP-Nonce', sitestaffr_knowledge.rest_nonce);
				},
				success: function(response) {
					if (!response.success) {
						errors.push(postId + ': ' + (response.message || 'No content'));
						postsDone++;
						syncNextPost();
						return;
					}

					var chunkTotal = response.total;
					var chunksDone = 0;
					var chunksSucceeded = 0;
					var BATCH_SIZE = 10;
					var BATCH_DELAY = 500;

					// Phase 2: Embed chunks in batches.
					function embedNextBatch() {
						if (chunksDone >= chunkTotal) {
							totalChunks += chunksSucceeded;
							var pct = Math.round(((postsDone + 1) / totalPosts) * 100);
							$('#progress-fill').css('width', pct + '%');
							$('.content-item[data-id="' + postId + '"]')
								.attr('data-status', 'synced')
								.find('.status-dot')
								.removeClass('stale unsynced')
								.addClass('synced');
							postsDone++;
							syncNextPost();
							return;
						}

						var batchEnd = Math.min(chunksDone + BATCH_SIZE, chunkTotal);
						var indices = [];
						for (var i = chunksDone; i < batchEnd; i++) {
							indices.push(i);
						}
						var isLast = (batchEnd >= chunkTotal);

						updateProgress(
							'Post ' + (postsDone + 1) + '/' + totalPosts +
							' \u2014 chunks ' + (chunksDone + 1) + '-' + batchEnd + '/' + chunkTotal
						);

						$.ajax({
							url: sitestaffr_knowledge.rest_url + 'knowledge/embed-batch',
							method: 'POST',
							contentType: 'application/json',
							timeout: 90000,
							data: JSON.stringify({
								post_id: postId,
								chunk_indices: indices,
								is_last: isLast
							}),
							beforeSend: function(xhr) {
								xhr.setRequestHeader('X-WP-Nonce', sitestaffr_knowledge.rest_nonce);
							},
							success: function(embedResponse) {
								if (embedResponse.success) {
									chunksSucceeded += embedResponse.saved || indices.length;
								} else {
									errors.push(postId + ' batch ' + chunksDone + '-' + batchEnd + ': ' + (embedResponse.message || 'Embed failed'));
								}
								chunksDone = batchEnd;
								setTimeout(embedNextBatch, BATCH_DELAY);
							},
							error: function(xhr) {
								var msg = 'Request failed';
								var delay = BATCH_DELAY;
								try { msg = JSON.parse(xhr.responseText).message || msg; } catch(e) {}
								// Back off on rate limit or WAF errors.
								if (xhr.status === 429 || xhr.status === 503) {
									delay = 5000;
									msg = 'Rate limited, retrying...';
								} else {
									errors.push(postId + ' batch ' + chunksDone + '-' + batchEnd + ': ' + msg);
									chunksDone = batchEnd;
								}
								setTimeout(embedNextBatch, delay);
							}
						});
					}

					embedNextBatch();
				},
				error: function(xhr) {
					var msg = 'Prepare request failed';
					try { msg = JSON.parse(xhr.responseText).message || msg; } catch(e) {}
					errors.push(postId + ': ' + msg);
					postsDone++;
					syncNextPost();
				}
			});
		}

		syncNextPost();
	}

	function updateProgress(text) {
		$('#progress-text').text(text);
	}

	function finishSync(totalChunks, errors) {
		isSyncing = false;
		$('#progress-fill').css('width', '100%');

		if (errors.length > 0) {
			$('#sync-status').html('<span style="color: #991b1b;">' + errors.length + ' error(s). ' + totalChunks + ' chunks indexed.</span>');
		} else {
			$('#sync-status').text('Done! ' + totalChunks + ' chunks indexed.');
		}

		$('#sync-knowledge-btn').text('Sync Knowledge');
		updateSyncButton();

		// Reload stats
		loadContentList();
	}

	function showError(msg) {
		$('#sync-status').html('<span style="color: #991b1b;">' + $('<span>').text(msg).html() + '</span>');
	}

	$(document).ready(init);

})(jQuery);
