function toProperCase(str) {
    return str.replace(/\w\S*/g, function(txt) {
        return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
    });
}

function addSpinner($elem, andDisable = true) {
	if ($elem.find('.spinner').length === 0) {
		$elem.append('<span class="spinner is-active" style="margin-left: 3px;"></span>');
	}
	andDisable && $elem.prop('disabled',true);
}

function removeSpinner(selector, andEnable = true) {
  jQuery(selector).find('.spinner').remove();
  andEnable && jQuery(selector).each(function() {
	jQuery(this).prop('disabled',false);
  });
}

function toggleTableColumnVisible(tableId, index, show) {
	
	var $table = jQuery('#'+tableId);
	$table.find('tr').each(function() {
		jQuery(this).find('td, th').eq(index).toggleClass('hidden', !show);
	});
				
}

function toggleChatLogColumnSort(index){

	m_chatlog_sortField = index;
	m_chatlog_sortAsc = !m_chatlog_sortAsc;
	
	fetchChatLogHTML(event, m_chatlog_period);
}

function fetchChatLogHTML(event, forPeriod) {
	
	if(event) {
		event.preventDefault();
		const $btn = jQuery(event.currentTarget);
		addSpinner($btn);
	} 

	m_chatlog_period = forPeriod;
	
	var showColumns = [];
	jQuery('#chatLogColumnControls input:checkbox:checked').each(function() {
		showColumns.push(jQuery(this).data('column-index'));
	});

	jQuery('#twx-chat-log_grid').remove();
	jQuery('#twx-chat-chatlog-messages').text('Loading Chatlog. Please wait...').css('display','flex');
	
	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_getChatLogHTML',
			nonce: twxChat.nonce,
			period: m_chatlog_period,
			showColumns: showColumns,
			sortField: m_chatlog_sortField,
			sortAsc: m_chatlog_sortAsc
		},
		success: function(response) {
			// On success, update the HTML element with the response
			removeSpinner('.twx_chat_chatlog_filter_btn',true);

			if(!response.success){
				if(response.data) jQuery('#twx-chat-chatlog-messages').html(`<p style="color: red;">Error: ${response.data}</p>`);
				console.log('AJAX Error: ',response);
				return;
			}

			var gridHtml = response.data;
			
			var cols = getColumnNamesFromHtml(gridHtml);
			var colControlsHtml = getColumnControlsHtml('chatLogColumnControls', cols, showColumns);
			
			gridHtml = colControlsHtml + gridHtml;
			
			jQuery('#twx-chat-chatlog-messages').hide();
			jQuery('#twx-chat-chatlog-grid').html(gridHtml);
			
			jQuery('#twx-chat-download-chatlog-btn').show();
			
			// Attach click events to column controls
			jQuery('#chatLogColumnControls input[type="checkbox"]').on('change', function() {
				const index = jQuery(this).data('column-index');
				const isChecked = jQuery(this).is(':checked');
				toggleTableColumnVisible('chatLogEntriesTable', index, isChecked); 
			});
			
			jQuery('#chatLogEntriesTable th').on('click', function(event) {
				// need next 2 lines to prevent event above from triggering on this element
				event.preventDefault(); 
				event.stopPropagation();
				var $th = jQuery(this);
				if($th.hasClass('sortable')) toggleChatLogColumnSort($th.index());
			});

			jQuery('#twx-chat-chatlog-grid h3').on('click', function(event) {
				event.preventDefault();
				event.stopPropagation();
			});
			
		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
			removeSpinner('.twx_chat_chatlog_filter_btn',true);
		}
	});
}

function getColumnNamesFromHtml(html) {
	const $html = jQuery(html);
	const columnNames = $html.find('thead th').map(function() {
		return jQuery(this).text().trim();
	}).get();
	return columnNames;
}

function getColumnControlsHtml(divId, colNames, showColumns) {

	// Create checkboxes for each column header
	var html = '<div id="'+divId+'">';
	for(var i=0; i<colNames.length; i++){
		var checked = (showColumns.length == 0 || showColumns.includes(i) ? ' checked' : '');
		html += '<input type="checkbox" data-column-index="'+i+'"'+checked+'>'+colNames[i]+'&nbsp;&nbsp;&nbsp;';
	}
	html += '</div>';

	return html;
}

function downloadChatLogCSV() {

	var $table = jQuery('#chatLogEntriesTable');
	if($table == undefined || !$table) return;
	var numEntries = $table.data('num-entries');

	if(numEntries == 0){
		alert('No entries exist for this period. Cannot Download.');
		return;
	}

	if(!confirm(`Download Chat Log for this period (${numEntries} entries) ?`)) return;
	
    jQuery.ajax({
        url: twxChat.ajax_url, 
        method: 'POST',
        data: {
			action: 'twx_chat_getChatLogCSV',
			nonce: twxChat.nonce,
            period: m_chatlog_period
        },
        success: function(response) {
			if(!response.success) return;
            // Create a Blob from the CSV data
            var blob = new Blob([response.data], { type: 'text/csv;charset=utf-8;' });
            var url = URL.createObjectURL(blob);

            // Create a temporary link element
            var link = document.createElement('a');
            link.setAttribute('href', url);
            link.setAttribute('download', 'chatlog.csv');

            // Append the link to the body (necessary for Firefox)
            document.body.appendChild(link);

            // Simulate a click to trigger the download
            link.click();

            // Clean up by removing the link
            document.body.removeChild(link);
            URL.revokeObjectURL(url); // Free up the URL object
        },
        error: function(xhr, status, error) {
            console.error('Error downloading CSV:', error);
        }
    });
}	

function deleteChatLogEntriesConfirm() {

	var $button = jQuery('#twx-chat-delete-chatlog-entries-btn');

	var numToKeep = $button.data('entries-to-keep');
	var maxEntries = $button.data('max-entries');
	var currEntries = $button.data('current-entries');
	var maxEntriesAllowed = Math.min(parseInt(maxEntries), parseInt(currEntries));

	let userInput = prompt(`Enter number of recent Chat Log entries to keep (max ${maxEntriesAllowed}).\nNOTE: ALL other older entries will be deleted!`,numToKeep);
	if (userInput !== null) {

		var userNumToKeep = Number(userInput);
		if (!isNaN(userNumToKeep) && userNumToKeep >= 0 && userNumToKeep <= maxEntriesAllowed) {
			var numToDelete = parseInt(currEntries) - userNumToKeep;
			if(confirm(`Are you absolultely sure you want to keep ${userNumToKeep} recent Chat Log entries AND delete ${numToDelete} old Chat Log entries?\nNOTE: These cannot be retrieved later unless you have Downloaded them.`)){
				deleteChatLogEntries(userNumToKeep);
			}
		} else {
			alert(`invalid input. Must be between 0 and ${maxEntriesAllowed}.`);
		}
	}

}

function deleteChatLogEntries(numToKeep) {

    jQuery.ajax({
        url: twxChat.ajax_url, 
        method: 'POST',
        data: {
			action: 'twx_chat_deleteChatLogEntries',
			nonce: twxChat.nonce,
            numToKeep: numToKeep
        },
        success: function(response) {
			if(!response.success) {
				alert('An error occurred deleting old Chat Log entries. Contact support.');
			}else{
				alert('Successfully deleted '+response.data.numDeleted+' old Chat Log entries.');
			}
        },
        error: function(xhr, status, error) {
			alert('An error occurred deleting old Chat Log entries. Contact support.');
            console.error('Error deleting old Chat Log entries:', error);
        }
    });

}

function toggleSiteCrawlerScheduleTimeSetting() {
	var schedule = jQuery('#twx_chat_site_crawler_schedule').val();
	if (schedule == 'Daily' || schedule == 'Weekly') {
		jQuery('#twx_chat_site_crawler_schedule_time').closest('tr').show();
	} else {
		jQuery('#twx_chat_site_crawler_schedule_time').closest('tr').hide();
	}
}

function toggleSiteCrawlerScheduleDayOfWeekSetting() {
	var schedule = jQuery('#twx_chat_site_crawler_schedule').val();
	if (schedule == 'Weekly') {
		jQuery('#twx_chat_site_crawler_schedule_dayofweek').closest('tr').show();
	} else {
		jQuery('#twx_chat_site_crawler_schedule_dayofweek').closest('tr').hide();
	}
}

function toggleKnowledgeBaseColumnSort(index){

	m_knowledgebase_sortField = index;
	m_knowledgebase_sortAsc = !m_knowledgebase_sortAsc;
	fetchKnowledgeBaseHTML();
}

function refreshKnowledgeBaseButtons() {

	// new button
	if(jQuery('#knowledgeBaseEntriesTable') && m_knowledgebase_mode != 'new' && m_knowledgebase_mode != 'edit'){
		jQuery('#twx-chat-knowledgebase-newentry-btn').show();
	}else{
		jQuery('#twx-chat-knowledgebase-newentry-btn').hide();
	}

	// edit button
	if (jQuery('#knowledgeBaseEntriesTable tbody input[type="checkbox"]:checked').length > 0 && m_knowledgebase_mode != 'edit' && m_knowledgebase_mode != 'new') {
		jQuery('#twx-chat-knowledgebase-editentries-btn').show();
	} else {
		jQuery('#twx-chat-knowledgebase-editentries-btn').hide();
	}

	// delete button
	if (jQuery('#knowledgeBaseEntriesTable tbody input[type="checkbox"]:checked').length > 0 && m_knowledgebase_mode != 'edit' && m_knowledgebase_mode != 'new') {
		jQuery('#twx-chat-knowledgebase-deleteentries-btn').show();
	} else {
		jQuery('#twx-chat-knowledgebase-deleteentries-btn').hide();
	}

	// save and cancel buttons
	if(m_knowledgebase_mode == 'edit' || m_knowledgebase_mode == 'new'){
		jQuery('#twx-chat-knowledgebase-saveentries-btn, #twx-chat-knowledgebase-cancelaction-btn').show();
	}else{
		jQuery('#twx-chat-knowledgebase-saveentries-btn, #twx-chat-knowledgebase-cancelaction-btn').hide();
	}

	// clear filters
	if(Object.keys(m_knowledgebase_filters).length !== 0){
		jQuery('#twx-chat-knowledgebase-clearfilters-btn').show();
	}else{
		jQuery('#twx-chat-knowledgebase-clearfilters-btn').hide();
	}

}

function checkForPluginUpdates() {

	// Only used in Paid Plans
	let $checkBtn = jQuery('#twx_chat_check_for_updates_btn');
	$checkBtn.text('Checking...').show();
	addSpinner($checkBtn,true);
	let $updateZipBtn = jQuery('#twx_chat_update_plugin_zip_btn');
	$updateZipBtn.hide();

	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_checkForPluginUpdates',
			nonce: twxChat.nonce,
		},
		success: function(response) {
			if(!response.success || !response.data) return;
			console.log('checkForPluginUpdates:: data:',response.data);
			const dataJO = JSON.parse(response.data);
			removeSpinner($checkBtn,true);
			if(dataJO.new_version){
				//$updateNowBtn.text(`Version ${dataJO.new_version} Available. Update Now!`).show();
				$updateZipBtn.text(`Version ${dataJO.new_version} (${toProperCase(dataJO.new_plan)}) Available. Download ZIP`).show();
				$checkBtn.hide();
			}else{
				$checkBtn.text('No Updates Available. Check Again');
			}

		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
			removeSpinner($checkBtn,true);
			$checkBtn.show();
			//$updateNowBtn.text('Check for Updates');
		}
	});

}

function downloadAndInstallPluginUpdate(){

	let $checkBtn = jQuery('#twx_chat_check_for_updates_btn');
	let $updateNowBtn = jQuery('#twx_chat_update_plugin_now_btn');
	$updateNowBtn.prop('disabled', true).text('Installing...').show();

	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_downloadAndInstallPluginUpdate',
			nonce: twxChat.nonce,
		},
		success: function(response) {
			if(!response.success || !response.data) return;
			console.log('downloadAndInstallPluginUpdate:: data:',response.data);
			const dataJO = JSON.parse(response.data);
			$checkBtn.prop('disabled', false).show();
		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
			$checkBtn.prop('disabled', false).show();
			$updateNowBtn.text('Check for Updates').show();
		}
	});

}

function fetchKnowledgeBaseHTML() {

	let similarQueryColIndexes = [];
	for (const [colIdx, filter] of Object.entries(m_knowledgebase_filters)) {
		console.log(`ColIdx: ${colIdx}, Value: ${filter}`);
		const $checkbox = jQuery(`input[name="similarquery-filter-field-${colIdx}"].similar-query-filter`);
		if($checkbox && $checkbox.is(':checked')){
			similarQueryColIndexes.push(parseInt(colIdx));
		}
	}

	const $searchBtn = jQuery('#twx-chat-knowledgebase-search-btn');
	addSpinner($searchBtn,true);

	jQuery('#twx-chat-knowledgebase-grid').html('');
	m_knowledgebase_mode = 'load';

	jQuery('#twx-chat-kb-messages').text('Loading Knowledge Base entries. Please wait...').css('display','flex');

	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_getKnowledgeBaseAdminHTML',
			nonce: twxChat.nonce,
			filters: m_knowledgebase_filters,
			sortField: m_knowledgebase_sortField,
			sortAsc: m_knowledgebase_sortAsc,
			similarQueryColIndexes: similarQueryColIndexes
		},
		success: function(response) {

			jQuery('#twx-chat-kb-messages').hide();
			removeSpinner('#twx-chat-knowledgebase-search-btn',true);

			// On success, update the HTML element with the response
			if(!response.success){
				if(response.data) jQuery('#twx-chat-kb-messages').html(`<p style="color: red;">Error: ${response.data}</p>`);
				console.log('AJAX Error: ',response);
				return;
			}
			
			var dataJO = JSON.parse(response.data);
			if(!dataJO.html || !dataJO.columns) return;

			var gridHtml = dataJO.html;
			m_knowledgebase_cols = dataJO.columns;

			jQuery('#twx-chat-knowledgebase-grid').html(gridHtml);
			
			jQuery('#knowledgeBaseEntriesTable th').on('click', function(event) {
				// Table header column clicked
				var $th = jQuery(this);
				if ($th.index() == 0) {
					// Select All checkbox
					const isChecked = jQuery('#twx-chat-knowledgebase-select-chk').is(':checked'); // Check if header checkbox is checked
					jQuery('#knowledgeBaseEntriesTable tbody tr td:first-child input[type="checkbox"]').prop('checked', isChecked);
					refreshKnowledgeBaseButtons();
				}else{
					// sort - need next 2 lines to prevent event from triggering on this element
					event.preventDefault(); 
					event.stopPropagation();
					if($th.hasClass('sortable')) toggleKnowledgeBaseColumnSort($th.index()-1);
				}
			});

			jQuery('#knowledgeBaseEntriesTable tbody td:first-child input[type="checkbox"]').on('click', function(event) {
				refreshKnowledgeBaseButtons();
			});

			jQuery('#twx-chat-knowledgebase-grid h3').on('click', function(event) {
				// need next 2 lines to prevent event above from triggering on this element
				event.preventDefault();
				event.stopPropagation();
			});

			jQuery('#knowledgeBaseEntriesTable .filter-field').on('blur', function () {
				const inputField = jQuery(this);
				clearTimeout(inputField.data('timer')); // Stop the delayed one

				const index = inputField.attr('column-index');
				const val = inputField.val();
				m_knowledgebase_filters[index] = val;
				fetchKnowledgeBaseHTML(); // Run immediately on blur
			});


			jQuery('#knowledgeBaseEntriesTable .filter-field').on('input', function () {
				const inputField = jQuery(this);
				clearTimeout(inputField.data('timer'));

				const timer = setTimeout(function () {
					const index = inputField.attr('column-index');
					const val = inputField.val();
					m_knowledgebase_filters[index] = val;
					fetchKnowledgeBaseHTML();
				}, 2000);

				inputField.data('timer', timer);
			});

			jQuery('#knowledgeBaseEntriesTable .similar-query-filter').on('change', function () {
				const colIdx = jQuery(this).attr('column-index');
				if(colIdx){
					const val = jQuery(`#knowledgeBaseEntriesTable input[name="filter-field-${colIdx}"]`).val();
					if(val && val.trim() !== '') fetchKnowledgeBaseHTML();
				}
			});

			refreshKnowledgeBaseButtons();

			// truncate sources if too long
			const truncateLimit = 120;
			jQuery('#knowledgeBaseEntriesTable td[data-colname="source"]').each(function () {
				const fullText = jQuery(this).text().trim();
				if (fullText.length > truncateLimit) {
				  const truncated = fullText.substring(0, truncateLimit) + '...';
				  jQuery(this)
					.attr('data-fulltext', fullText)
					.text(truncated);
				}
			});

		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
			jQuery('#twx-chat-kb-messages').html('<span style="color: red;">Error: ' + error + '</span>').css('display','flex');
			removeSpinner('#twx-chat-knowledgebase-search-btn',true);
		}
	});

}

// Called dynamically from fetchEditChatbotHTML (there won't be an IDE reference)
function onChange_chatbot_att_common_questions($input){
	// preview common questions buttons
	const $td = $input.parent();
	$td.children().slice(2).remove();

	const value = $input.val().trim();
	if (!value) return; // do nothing if input is empty
	$btnContainer = jQuery('<div id="chatbot-common-qs-preview" style="padding-top:5px;"></div>');
	$btnContainer.append(jQuery('<span>Buttons preview:</span>'));
	const items = value.split(',').map(item => item.trim()).filter(item => item.length > 0);

	items.forEach(text => {
		const $btn = jQuery('<button type="button" class="button-secondary" style="margin-left:10px;"></button>').text(text);
		$btnContainer.append($btn);
	});

	$td.append($btnContainer);
}

// Called dynamically from fetchEditChatbotHTML (there won't be an IDE reference)
function onChange_chatbot_att_avatar_character($input){
	validateChatbotAvatarAtts();
	$input.parent().children('.avatar-character-thumb-img').remove();
	$input.parent().children('.download-avatar-assets-btn').remove();
	$input.parent().children('.download-avatar-assets-results').remove();
	let avatarId = $input.val();
	if(avatarId){
		let src = twxChat.avatars[avatarId].thumb_src;
		$input.parent().append(jQuery(`<img class="avatar-character-thumb-img" src="${src}" width="80px" style="color:red;" alt="avatar thumb image for ${avatarId} not found">`));
		let $selectedOption = $input.find('option:selected');
		if($selectedOption && $selectedOption.data('exists') === false){
			if($selectedOption.data('available') === false){
				$input.parent().append(jQuery(`<div class="download-avatar-assets-results validation-msg" style="color:red;">This Avatar is available now on <a href="https://twxchat.twxlabs.au" target="_blank">TWXChat Plus</a></div>`));
			}else{
				let checkFile = $selectedOption.data('check-file');
				$input.parent().append(jQuery(`<button style="margin-left:10px;" class="download-avatar-assets-btn button-primary" onclick="downloadAvatarAssets('${avatarId}','${checkFile}',this)">Download and Install Avatar</button>`));
			}
		}
	}
}

// Called dynamically from fetchEditChatbotHTML (there won't be an IDE reference)
function onChange_chatbot_att_voice($input){
	$input.parent().children('.validation-msg').remove();
	//const regex = /^[a-zA-Z0-9_-]+:[a-zA-Z0-9_-]+$/;
	const regex = /^[a-zA-Z0-9_]+:[^:]+(?:\:\d{1,3}%?)?$/;
  	if($input.val() && !regex.test($input.val())){
		$input.parent().append(jQuery('<div class="validation-msg" style="color:red;">Invalid format</div>'));
	}
	// Some validations are connected to stream attribute
	jQuery(`#twx-chat-chatbot-edit .twx_chatbot_att[name="stream"]`).trigger('change');
}

// Called dynamically from fetchEditChatbotHTML (there won't be an IDE reference)
function onChange_chatbot_att_avatar_voice($input) {
	validateChatbotAvatarAtts();
	const value = $input.val();
	const regex = /^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+(:\d{1,3}%?)?$/;
	// Extract part before the first colon
	const provider = value ? value.split(':')[0] : null;
	if (value && !regex.test(value)) {
		$input.parent().append(jQuery('<div class="validation-msg" style="color:red;">Invalid format</div>'));
	}else{
		if(provider === 'aws' && !jQuery('#twx_chat_aws_cognito_identity_pool_id').val()){
			$input.parent().append(jQuery('<div class="validation-msg" style="color:red;">You are missing an AWS Cognito Pool Identity Key. See \'General\' tab. You will need that in order to use an AWS Polly Voice</div>'));
		}
	}
}

// Called dynamically from fetchEditChatbotHTML (there won't be an IDE reference)
function onChange_chatbot_att_avatar_image_url($input){
	$input.parent().children().slice(2).remove();
	if($input.val()){
		$input.parent().append(jQuery(`<img src="${$input.val()}" width="80px" style="color:red;" alt="image not found">`));
	}
}

// Called dynamically from fetchEditChatbotHTML (there won't be an IDE reference)
function onChange_chatbot_att_avatar_background_url($input){
	$input.parent().children().slice(2).remove();
	if($input.val()){
		$input.parent().append(jQuery(`<img src="${$input.val()}" width="80px" style="color:red;" alt="image not found">`));
	}
}

// Called dynamically from fetchEditChatbotHTML (there won't be an IDE reference)
function onChange_chatbot_att_floating_image_url($input){
	$input.parent().children().slice(2).remove();
	if($input.val()){
		$input.parent().append(jQuery(`<img src="${$input.val()}" width="80px" style="color:red;" alt="image not found">`));
	}
}

function onChange_chatbot_att_kb_or_assistant($input){
	// Show/hide KB/Assistant related attribs
	const openaiAsstAttribs = ['assistant_id'];
	const kbAttribs = ['kb_source','show_sources'];
	let openaiAsstsDisplay = 'none';
	let kbDisplay = 'table-row';
	if($input.val() == 'openai_assistant'){
		openaiAsstsDisplay = 'table-row';
		kbDisplay = 'none';
	}
	for(let i=0;i<openaiAsstAttribs.length;i++){
		const $input = jQuery(`#twx-chat-chatbot-edit .twx_chatbot_att[name="${openaiAsstAttribs[i]}"]`);
		if($input) $input.closest('tr').css('display',openaiAsstsDisplay);
	}
	for(let i=0;i<kbAttribs.length;i++){
		const $input = jQuery(`#twx-chat-chatbot-edit .twx_chatbot_att[name="${kbAttribs[i]}"]`);
		if($input) $input.closest('tr').css('display',kbDisplay);
	}
}

function onChange_chatbot_att_type($input){
	// Show/hide Avatar/Non-Avatar related attribs
	const avatarAttribs = ['avatar_character','avatar_voice','avatar_image_url','avatar_background_url','flashcards'];
	const nonAvatarAttribs = ['voice'];
	let avatarDisplay = 'none';
	let nonAvatarDisplay = 'table-row';
	if($input.val() == 'avatar'){
		avatarDisplay = 'table-row';
		nonAvatarDisplay = 'none';
	}
	for(let i=0;i<avatarAttribs.length;i++){
		const $input1 = jQuery(`#twx-chat-chatbot-edit .twx_chatbot_att[name="${avatarAttribs[i]}"]`);
		if($input1) $input1.closest('tr').css('display',avatarDisplay);
	}
	for(let i=0;i<nonAvatarAttribs.length;i++){
		const $input2 = jQuery(`#twx-chat-chatbot-edit .twx_chatbot_att[name="${nonAvatarAttribs[i]}"]`);
		if($input2) $input2.closest('tr').css('display',nonAvatarDisplay);
	}
}

function onChange_chatbot_att_style($input){
	// Show/hide Style related attribs
	const floatingAttribs = ['floating_title','floating_image_url'];
	const nonFloatingAttribs = [];
	let floatingDisplay = 'none';
	let nonFloatingDisplay = 'table-row';
	if($input.val() == 'floating'){
		floatingDisplay = 'table-row';
		nonFloatingDisplay = 'none';
	}
	for(let i=0;i<floatingAttribs.length;i++){
		const $input1 = jQuery(`#twx-chat-chatbot-edit .twx_chatbot_att[name="${floatingAttribs[i]}"]`);
		if($input1) $input1.closest('tr').css('display',floatingDisplay);
	}
	for(let i=0;i<nonFloatingAttribs.length;i++){
		const $input2 = jQuery(`#twx-chat-chatbot-edit .twx_chatbot_att[name="${nonFloatingAttribs[i]}"]`);
		if($input2) $input2.closest('tr').css('display',nonFloatingDisplay);
	}
}

function validateChatbotAvatarAtts(){

	let $avatar_voice = jQuery('input[name="avatar_voice"].twx_chatbot_att');
	let $avatar_character = jQuery('select[name="avatar_character"].twx_chatbot_att');

	// remove any previous errors
	$avatar_voice.parent().children('.validation-msg').remove();
	$avatar_character.parent().children('.validation-msg').remove();

	if(($avatar_character.val() && !$avatar_voice.val()) || (!$avatar_character.val() && $avatar_voice.val())) {
		let html = '<div class="validation-msg" style="color:red;">avatar_character and avatar_voice must both contain values</div>';
		$avatar_voice.parent().append(jQuery(html));
		$avatar_character.parent().append(jQuery(html));
	}
}

function fetchChatbotsHTML() {

	// Get Chatbots Table contents

	jQuery('#twx-chat-chatbots-table').html('');
	jQuery('#twx-chat-chatbots-messages').text('Loading Chatbots. Please wait...').css('display','flex');
	
	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_getChatbotsAdminHTML',
			nonce: twxChat.nonce,
		},
		success: function(response) {

			if(!response.success){
				if(response.data) jQuery('#twx-chat-chatbots-messages').html(`<p style="color: red;">Error: ${response.data}</p>`);
				console.log('AJAX Error: ',response);
				return;
			}

			let dataJO = JSON.parse(response.data);
			if(!dataJO.html) return;

			jQuery('#twx-chat-chatbots-messages').hide();
			jQuery('#twx-chat-chatbots-table').html(dataJO.html);
			if(dataJO.limit_reached){
				jQuery('#twx-chat-chatbots-new-btn').hide();
				jQuery('#twx-chat-chatbots-new-limit-reached').show();
				jQuery('.twx-chat-chatbot-copy-btn').hide();
			}else{
				jQuery('#twx-chat-chatbots-new-btn').show();
				jQuery('#twx-chat-chatbots-new-limit-reached').hide();
				jQuery('.twx-chat-chatbot-copy-btn').show();
			}
		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
			jQuery('#twx-chat-chatbots-messages').html('<p style="color: red;">Error: ' + error + '</p>');
		}
	});

}

function fetchEditChatbotHTML(chatbotId = null) {

	// if null chatbotId, then assume new

	jQuery('#twx-chat-chatbot-edit').html('');
	jQuery('#twx-chat-chatbot-edit').data('chatbot-id', (chatbotId ?? ''));
	jQuery('#twx-chat-chatbots-messages').text('Loading Chatbot. Please wait...').css('display','flex');
	
	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_getEditChatbotAdminHTML',
			nonce: twxChat.nonce,
			id: chatbotId ?? '',
		},
		success: function(response) {

			removeSpinner('.twx-chat-chatbot-edit-btn',true);

			if(!response.success){
				if(response.data) jQuery('#twx-chat-chatbots-messages').html(`<p style="color: red;">Error: ${response.data}</p>`);
				console.log('AJAX Error: ',response);
				return;
			}
			
			// On success, update the HTML element with the response
			let dataJO = JSON.parse(response.data);
			if(!dataJO.html) return;

			jQuery('#twx-chat-chatbots-messages').hide();
			jQuery('#twx-chat-chatbot-edit').html(dataJO.html);

			jQuery('#twx-chat-chatbot-save-btn').on('click', () => {
				if(jQuery('#twx-chat-chatbot-edit .validation-msg').length > 0){
					alert('Chatbot is not valid. Check validation messages.');
				}else{
					addSpinner(jQuery('#twx-chat-chatbot-save-btn'));
					jQuery('#twx-chat-chatbot-cancel-btn').prop('disabled',true);
					upsertChatbotFromEditFields();
				}
			});

			jQuery('#twx-chat-chatbot-cancel-btn').on('click', () => {
				jQuery('#twx-chat-chatbot-edit').html('');
				jQuery('html, body').animate({ scrollTop: jQuery('#chatbots').offset().top	}, 200);
			});

			jQuery('.twx-chat-more-info-btn').on('click', function() {
				const attrName = jQuery(this).data('chatbot-attr');
				if(attrName){
					const url = `${twxChat.twxchat_support_base_url}/api/feature_info.php?&feature=chatbot_attr_${attrName}`;
					window.open(url, '_blank');
				}
			});

			jQuery('#twx-chat-chatbot-edit-name').on('change', function() {
				const $input = jQuery(this);
				$input.parent().children('.validation-msg').remove();
				if(!$input.val()){
					$input.parent().append(jQuery('<div class="validation-msg" style="color:red;">Must contain value</div>'));
				}
			});

			jQuery('.twx_chatbot_att').on('change', function() {
				const $input = jQuery(this);
				// call on change function if one exists
				const fnName = `onChange_chatbot_att_${$input.attr('name')}`;
				if (typeof window[fnName] === "function") {
					window[fnName]($input);
				}
			});

			// Populate voice datalist if exists
			/*
			const $voiceDatalist = jQuery('#attr-datalist-voice');
			if($voiceDatalist){
				getAvailableVoices().forEach((voice, index) => {
					$voiceDatalist.append(`<option value="${voice.name}">${voice.name}</option>`);
				});
			}
			*/

			// Trigger the change events right after rendering
			jQuery('.twx_chatbot_att').trigger('change');
			// Scroll
			jQuery('html, body').animate({ scrollTop: jQuery('#twx-chat-chatbot-edit').offset().top	}, 200);
		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
			jQuery('#twx-chat-chatbots-messages').html('<p style="color: red;">Error: ' + error + '</p>');
			removeSpinner('.twx-chat-chatbot-edit-btn',true);
		}
	});

}

function getAvailableVoices(){

	let availableVoices = [];
	const voices = speechSynthesis.getVoices();
    if (voices.length === 0) {
        // Wait for voices to be loaded
        speechSynthesis.onvoiceschanged = getAvailableVoices;
        return;
    }	
	voices.forEach((v, i) => {
		//console.log(`${i + 1}. ${v.name} | lang: ${v.lang} | gender: ${inferGender(v.name)} | neural: ${isNeural(v.name)} | localService: ${v.localService}`);
		availableVoices.push(v);
	});

	return availableVoices;
}

function upsertChatbotFromEditFields() {

	// Insert new or update existing chatbot from edit fields

	let id = jQuery('#twx-chat-chatbot-edit').data('chatbot-id') ?? '';
	let name = jQuery('#twx-chat-chatbot-edit input[name="name"]').val() ?? '';
	let settings = {};

	jQuery('.twx_chatbot_att').each(function(index){
		let $att = jQuery(this);
		const name = $att.attr('name');
		const value = $att.val();
		if (value) {
			assignNestedSetting(settings, name, value);
		}
	});

	console.log('upsertChatbotFromEditFields',id,name,settings);

	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_updateChatbotAdmin',
			nonce: twxChat.nonce,
			id: id,
			name: name,
			settings: settings
		},
		success: function(response) {

			// On success, update the HTML element with the response
			removeSpinner('#twx-chat-chatbot-save-btn');
			if(!response.success || !response.data) return;
			jQuery('#twx-chat-chatbot-edit').html('');
			fetchChatbotsHTML();
			jQuery('html, body').animate({ scrollTop: jQuery('#chatbots').offset().top	}, 200);
		},
		error: function(xhr, status, error) {
			// Handle errors
			removeSpinner('#twx-chat-chatbot-save-btn');
			console.log('AJAX Error: ' + status + ' - ' + error);
		}
	});

}

function assignNestedSetting(obj, name, value) {
	const keys = name
		.replace(/\]/g, '')
		.split('[');

	let current = obj;
	for (let i = 0; i < keys.length; i++) {
		const key = keys[i];
		if (i === keys.length - 1) {
			current[key] = value;
		} else {
			current[key] = current[key] || {};
			current = current[key];
		}
	}
}

function deleteChatbot(chatbotId){

	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_deleteChatbotAdmin',
			nonce: twxChat.nonce,
			id: chatbotId
		},
		success: function(response) {

			if(!response.success || !response.data) return;
			// Refresh Chatbot List on successful delete
			fetchChatbotsHTML();
		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
		}
	});
}

function copyChatbot(chatbotId){

	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_copyChatbotAdmin',
			nonce: twxChat.nonce,
			id: chatbotId
		},
		success: function(response) {

			if(!response.success || !response.data) return;
			// Refresh Chatbot List on successful copy
			fetchChatbotsHTML();
		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
		}
	});
}

function downloadAvatarAssets(avatarId, checkFile, button) {

	if(!confirm('Avatar files can be up to 30MB. Are you sure you want to download this Avatar?')) return;

	let $parent = jQuery(button).parent();
	jQuery(button).remove();
	let $dlResults = jQuery(`<div class="download-avatar-assets-results">Downloading. Please wait...</div>`);
	$parent.append($dlResults);

	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_downloadAvatarAssets',
			nonce: twxChat.nonce,
			id: avatarId,
			check_file: checkFile
		},
		success: function(response) {
			if(!response.success || !response.data) return;
			$dlResults.text(response.data.error_msg ? response.data.error_msg : response.data.success_msg);
			$dlResults.css('color', response.data.error_msg ? 'red' : 'green');
		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
		}
	});
}

function copySupportInfo() {

	let $checkBtn = jQuery('#twx_chat_copy_support_info_btn');
	addSpinner($checkBtn,true);

	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_getSupportInfo',
			nonce: twxChat.nonce,
		},
		success: function(response) {
			removeSpinner($checkBtn,true);
			if(!response.success || !response.data || !response.data.supportInfoText) return;
			navigator.clipboard.writeText(response.data.supportInfoText).then(() => {
				alert('Support info copied to clipboard');
			}).catch(err => {
				alert('Failed to copy: ' + err);
			});

		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
			removeSpinner($checkBtn,true);
		}
	});
}

function startSiteCrawlerNow(){

	m_siteCrawlerRunning = true;
	jQuery('#twx_chat_site_crawler_log').html('');
	jQuery('#twx_chat_site_crawler_log').parent().prepend(
	'<div style="color:green; display: flex; align-items: center; gap: 8px;">' +
		'<span class="spinner is-active"></span>' +
		'<span>Running...</span>' +
	'</div>'
	);
	startSiteCrawlerMonitorTimer();

	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_startSiteCrawlerNow',
			nonce: twxChat.nonce,
		},
		success: function(response) {
			if(!response.success || !response.data) return;
			console.log('startSiteCrawlerNow:: status:',response.data.status);

		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
		}
	});

}

function cancelRunningSiteCrawler(){

	jQuery.ajax({
		url: twxChat.ajax_url,
		type: 'POST',
		data: {
			action: 'twx_chat_cancelRunningSiteCrawler',
			nonce: twxChat.nonce,
		},
		success: function(response) {
			if(!response.success || !response.data) return;
			m_siteCrawlerRunning = false; // site crawler monitor will handle the rest
		},
		error: function(xhr, status, error) {
			// Handle errors
			console.log('AJAX Error: ' + status + ' - ' + error);
		}
	});

}

function startSiteCrawlerMonitorTimer() {

	const $statusDiv = jQuery('#twx_chat_site_crawler_log').parent().children().first();
    const timerId = setInterval(() => {
        if (!m_siteCrawlerRunning) {
            clearInterval(timerId);
			$statusDiv.remove(); // remove Running div
			jQuery('#twx_chat_site_crawler_crawl_now_btn').prop('disabled', false);
			jQuery('#twx_chat_site_crawler_cancel_crawl_btn').prop('disabled', true);
            return;
        }

		let progress = getSiteCrawlerProgress();
		if(progress){
			jQuery('#twx_chat_site_crawler_log').html(progress.log);
			m_siteCrawlerRunning = progress.running;
		}

    }, 4000); 
}

function getSiteCrawlerProgress() {

	let progress = null;

    // synchronously get site crawler progress from server
    jQuery.ajax({
        url: twxChat.ajax_url,
        type: 'POST',
        async: false,  // This makes the request synchronous
		data: {
			action: 'twx_chat_getSiteCrawlerProgress',
			nonce: twxChat.nonce,
		},
		dataType: 'json',
        success: function(response) {
			if (response.success && response.data.progress) {
				progress = response.data.progress;
			}
        },
        error: function(xhr, status, error) {
            console.log('getSiteCrawlerProgress Error:', error);
			throw error;
        }
    });
	return progress;
}

function refreshUserFieldsTable() {
	const $rows = jQuery('#twx-chat-chatbot-user-fields-table tbody tr');
	$rows.each(function(index) {
		const $row = jQuery(this);
		$row.find('td:first').text(index + 1); // Update row number

		// Update input names
		$row.find('input[name^="user_fields[fields]"]').each(function() {
			const $input = jQuery(this);
			const nameAttr = $input.attr('name');
			const newName = nameAttr.replace(/user_fields\[fields\]\[\d+\]/, `user_fields[fields][${index}]`);
			$input.attr('name', newName);
		});

		// Update delete button data-idx
		$row.find('.twx-chat-chatbot-user-field-delete-btn').attr('data-idx', index);
	});
}

jQuery(document).ready(function($) {
	
	// Hide all tab contents except the first one
	$('.twx-chat-tab-content').not(':first').hide();

	// Load Chatbots now
	fetchChatbotsHTML();

    // Handle tab clicks
    $('.nav-tab').on('click', function (e) {
        e.preventDefault();

        // Remove active class from all tabs and add to the clicked tab
        $('.nav-tab').removeClass('nav-tab-active');
        $(this).addClass('nav-tab-active');

		// Hide all tab contents
		$('.twx-chat-tab-content').hide();

        // Hide all tab content and show the content for the clicked tab
        //$('.tab-content').hide();
        $('#' + $(this).data('tab')).show();

        // Save the active tab to localStorage
        localStorage.setItem('twx_chat_active_tab', $(this).data('tab'));

        // Update the hidden input value to store the active tab for form submission
        //$('#active-tab').val($(this).data('tab'));
    });

    // Restore the active tab on page load
    const activeTab = localStorage.getItem('twx_chat_active_tab') || 'general';
    $('.nav-tab[data-tab="' + activeTab + '"]').click();

	// Handle Feature button clicks
	$(document).on('click', '.twx-chat-feature-button', function() {
		const $elem = $(this);
		const feature = $elem.data('feature') ?? '';
		const url = `${twxChat.twxchat_support_base_url}/api/feature_info.php?&feature=${feature}`;
		window.open(url, '_blank');
	});

	// Handle Plus/Pro clicks
	$(document).on('click', '.twx-plus-button, .twx-pro-button', function() {
		const url = `${twxChat.twxchat_support_base_url}/features`;
		window.open(url, '_blank');
	});
	
	$('#twx-chat-download-chatlog-btn').on('click', function() {
		downloadChatLogCSV();
	});

	$('#twx-chat-delete-chatlog-entries-btn').on('click', function() {
		deleteChatLogEntriesConfirm();
	});

	// Chat Log periods setup
	var periods = ['Today','2 Days','1 Week','1 Month','3 Months','All Time'];
	var html = 'Period: ';
	for (var i = 0; i < periods.length; i++) {
		var period = periods[i];
		html += '&nbsp;&nbsp;<button class="twx_chat_chatlog_filter_btn" onclick="fetchChatLogHTML(event,\''+period+'\')">'+period+'</button>';
	}
	$('#twx-chat-chatlog-periods').html(html);
	
	// Site Crawler
	$('#site-crawler').on('change', '[name^="twx_chat_options["]', function() {
		// If any options changed, need to Save before Allow Run Now
		$('#twx_chat_site_crawler_crawl_now_btn').hide();
	});

	toggleSiteCrawlerScheduleTimeSetting();
	toggleSiteCrawlerScheduleDayOfWeekSetting();

	$('#twx_chat_site_crawler_schedule').on('change', function () {
        toggleSiteCrawlerScheduleTimeSetting();
		toggleSiteCrawlerScheduleDayOfWeekSetting();
    });

	$('#twx-chat-knowledgebase-search-btn').on('click', function() {
		fetchKnowledgeBaseHTML();
		refreshKnowledgeBaseButtons();
	});

	let crawlerRunning = $('#twx_chat_site_crawler_crawl_now_btn').data('running') ? true : false;
	$('#twx_chat_site_crawler_crawl_now_btn').prop('disabled',crawlerRunning);
	$('#twx_chat_site_crawler_cancel_crawl_btn').prop('disabled',!crawlerRunning);
	if(crawlerRunning){
		m_siteCrawlerRunning = true;
		$('#twx_chat_site_crawler_log').parent().prepend('<div style="color:green;">Running...</div>');
		startSiteCrawlerMonitorTimer();
	}

	$('#twx_chat_site_crawler_crawl_now_btn').on('click', function() {
		if(confirm(`Are you sure you want to start the Site Crawler Now?`)) {
			$(this).prop('disabled', true);
			$('#twx_chat_site_crawler_cancel_crawl_btn').prop('disabled',false);
			startSiteCrawlerNow();
		}
	});

	$('#twx_chat_site_crawler_cancel_crawl_btn').on('click', function() {
		if(confirm(`Are you sure you want to cancel the running Site Crawler?`)) {
			$(this).prop('disabled', true);
			$('#twx_chat_site_crawler_crawl_now_btn').prop('disabled',false);
			cancelRunningSiteCrawler();
		}
	});

	// Chatbots
	$('#twx-chat-chatbots-new-btn').on('click', function() {
		fetchEditChatbotHTML(null);
	});

	// Plugin Info
	$('#twx_chat_copy_support_info_btn').on('click', function() {
		copySupportInfo();
	});

	// Populate browser voices
	getAvailableVoices();

	$(document).on('click', '.twx-chat-chatbot-edit-btn', function() {
		// event will fire on future added objects
		const $btn = $(this);
		let id = $btn.data('chatbot-id') ?? null;
		//$btn.prop('disabled', true)
		addSpinner($btn, true);
		fetchEditChatbotHTML(id);
	});

	$(document).on('click', '.twx-chat-chatbot-delete-btn', function() {
		// event will fire on future added objects
		let id = $(this).data('chatbot-id') ?? null;
		let inUse =  $(this).data('in-use') ?? false;
		if(inUse){
			if(confirm('This Chatbot is in use. Are you sure you want to delete it?')) deleteChatbot(id);
		}else{
			deleteChatbot(id);
		}
	});

	$(document).on('click', '.twx-chat-chatbot-copy-btn', function() {
		// event will fire on future added objects
		let id = $(this).data('chatbot-id') ?? null;
		if(id){
			copyChatbot(id);
		}
	});

	$(document).on('click', '.twx-chat-copy-shortcode-btn', function() {
		// event will fire on future added objects
		const shortcode = $(this).siblings('code').text();

		navigator.clipboard.writeText(shortcode).then(() => {
			$(this).text('✅'); // Show success icon
			const button = $(this);
			setTimeout(() => button.text('📋'), 1500); // Reset after 1.5s
		}).catch(err => {
			alert('Failed to copy: ' + err);
		});
	});

	$(document).on('click', '#twx-chat-chatbot-new-user-field-btn', function() {

		// Add User Field Attribute controls
		const idx = jQuery('#twx-chat-chatbot-user-fields-table tbody tr').length;

		const newRowHtml = `<tr>
			<td>${idx+1}</td>
			<td><input size="30px" type="text" name="user_fields[fields][${idx}][name]" class="twx_chatbot_att chatbot_user_fields_name" value="" required></td>
			<td><input size="100px" type="text" name="user_fields[fields][${idx}][description]" class="twx_chatbot_att" value=""></td>
			<td><button class="button-secondary twx-chat-chatbot-user-field-delete-btn" data-idx="${idx}">Delete</button></td>
			</tr>`;
		const $newRow = jQuery(newRowHtml);

		if(idx === 0){
			const headerHtml = `<thead><tr><th>#</th><th>Name</th><th>Description</th><th>Actions</th></tr></thead><tbody></tbody>`;
			const $header = jQuery(headerHtml);
			jQuery('#twx-chat-chatbot-user-fields-table').append($header);
		}
		jQuery('#twx-chat-chatbot-user-fields-table tbody').append($newRow);
		if(idx === 0){

			const $webhookInput = jQuery(`
			<div class="field-group">
  				<label style="display:block; font-weight:600; margin-bottom:4px;">Update Webhook URL</label>
  				<input type="text" name="user_fields[update_webhook_url]" class="twx_chatbot_att regular-text" size="100px" value="" />
			</div>`);
			$webhookInput.insertAfter('#twx-chat-chatbot-user-fields-table');

			const $flashcardTitleInput = jQuery(`
			<div class="field-group">
  				<label style="display:block; font-weight:600; margin-bottom:4px;">Flashcard Title (Avatar chatbots only. If empty, no Flashcard will show to the User)</label>
				<input type="text" name="user_fields[flashcard_title]" placeholder="eg: Your info" class="twx_chatbot_att regular-text" size="100px" value="Your fields" />
			</div>`);
			$flashcardTitleInput.insertAfter('#twx-chat-chatbot-user-fields-table');
		}
		jQuery('#twx-chat-chatbot-user-fields-table').show();
	});

	$(document).on('change', 'input.twx_chatbot_att.chatbot_user_fields_name', function(e) {
		var val = jQuery(this).val();
		if (val) {
			var snake = val
				.toString()
				.trim()
				.toLowerCase()
				.replace(/[^a-z0-9]+/g, '_')  // Replace non-alphanumeric with _
				.replace(/^_+|_+$/g, '')      // Trim leading/trailing underscores
				.replace(/_+/g, '_');         // Replace multiple _ with single _
			jQuery(this).val(snake);
		}
	});

	$(document).on('click', '.twx-chat-chatbot-user-field-delete-btn', function(e) {
		e.preventDefault();
		const idx = jQuery(this).data('idx');
		jQuery('#twx-chat-chatbot-user-fields-table tbody tr').eq(idx).remove();
		refreshUserFieldsTable();
	});
	
	// Knowledge Base
	$('#twx-chat-knowledgebase-clearfilters-btn').on('click', function() {
		$('#knowledgeBaseEntriesTable .filter-field').each(function() {
			$(this).val('');
		});
		m_knowledgebase_filters = {};
		fetchKnowledgeBaseHTML();
	});

	// new kb entry
	$('#twx-chat-knowledgebase-newentry-btn').on('click', function() {
		var $table = $('#knowledgeBaseEntriesTable');
		var newRow = '<tr data-row-id=""><td></td>';
		for (let i = 1; i < m_knowledgebase_cols.length; i++) {
			var col = m_knowledgebase_cols[i];
			var colName = col['colName'];
			var inner = '';
			var required = col['required'] ?? false;
			if(col['canEdit']){
				inner = `<textarea class="editable-td" placeholder="Enter value for ${col['display']} (${required ? 'required' : 'optional'})"${required ? ' required' : ' '}>${col['defaultVal'] ?? ''}</textarea>`;
			}
			newRow += '<td data-colname="'+colName+'">'+inner+'</td>';
		}
		newRow += '</tr>';
		// Insert the new row after the second row (index 1, since rows are zero-based)
		const $insertedRow = $(newRow);
		$table.find('tr').eq(1).after($insertedRow);

		m_knowledgebase_mode = 'new';
		refreshKnowledgeBaseButtons();

		$insertedRow.find('textarea.editable-td').first().focus();
	});

	// cancel action
	$('#twx-chat-knowledgebase-cancelaction-btn').on('click', function() {
		// restore rows		
		if(m_knowledgebase_mode == 'edit'){
			$('#knowledgeBaseEntriesTable tbody input[type="checkbox"]:checked').each(function() {
				var $row = $(this).closest('tr');
				$row.find('td').each(function(index) {
					var $col = $(this);
					if(!$col.data('colname')) return;
					var colName = $col.data('colname');
					if(!m_knowledgebase_cols[index]['canEdit']) return;
					var currentValue = $col.text(); // Get current td value
					if($col.data('fulltext')) currentValue = $col.data('fulltext');
					$col.html(currentValue);
				});
			});
		}
		if(m_knowledgebase_mode == 'new'){
			$('#knowledgeBaseEntriesTable tr').eq(2).remove();
		}

		m_knowledgebase_mode = '';
		refreshKnowledgeBaseButtons();
	});

	// delete kbs with confirm
	$('#twx-chat-knowledgebase-deleteentries-btn').on('click', function() {
		var numToDelete = $('#knowledgeBaseEntriesTable tbody input[type="checkbox"]:checked').length;
		if(numToDelete == 0) return;
		const userConfirmed = confirm(`Are you sure you want to delete the selected ${numToDelete} entries?`);
		if (userConfirmed) {
			var kbIds = [];
			$('#knowledgeBaseEntriesTable tbody input[type="checkbox"]:checked').each(function() {
				var $row = $(this).closest('tr');
				if($row.data('row-id')) kbIds.push($row.data('row-id'));
			});
			m_knowledgebase_mode == 'save';
			refreshKnowledgeBaseButtons();
	
			kbIds.length > 0 && $.ajax({
				url: twxChat.ajax_url,
				type: 'POST',
				data: {
					action: 'twx_chat_deleteKnowledgeBaseAdmin',
					nonce: twxChat.nonce,
					kbIds: kbIds,
				},
				success: function(response) {
		
					// On success, update the HTML element with the response
					if(!response.success || !response.data) return;
					fetchKnowledgeBaseHTML();
				},
				error: function(xhr, status, error) {
					// Handle errors
					console.log('AJAX Error: ' + status + ' - ' + error);
				}
			});
		}
	});

	// begin edit KB entries
	$('#twx-chat-knowledgebase-editentries-btn').on('click', function() {
		// Edit mode on
		$('#knowledgeBaseEntriesTable tbody input[type="checkbox"]:checked').each(function() {
			var $row = $(this).closest('tr');
			$row.find('td').each(function(index) {
				var $col = $(this);
				if(!$col.data('colname')) return;
				var colName = $col.data('colname');
				if(!m_knowledgebase_cols[index]['canEdit']) return;
				var currentValue = $col.text(); // Get current td value
				const kbCol = m_knowledgebase_cols[index];

				// Create textarea for editing
				$col.html(`<textarea class="editable-td" placeholder="Enter value for ${kbCol.display} (${kbCol.required ? 'required' : 'optional'})"${kbCol.required ? ' required' : ' '}>${currentValue}</textarea>`);
				var $textarea = $col.find('textarea');
				$textarea.css('height', 'auto');
				$textarea.css('height', $textarea[0].scrollHeight + 'px');
				// Keep resizing as user types
				$textarea.on('input', function () {
					this.style.height = 'auto';
					this.style.height = this.scrollHeight + 'px';
				});
								
				m_knowledgebase_mode = 'edit';
			});
		});
		refreshKnowledgeBaseButtons();
	});

	// save kb updates
	$('#twx-chat-knowledgebase-saveentries-btn').on('click', function() {
		let entries = [];
		let hasError = false;
		if(m_knowledgebase_mode == 'edit'){
			$('#knowledgeBaseEntriesTable tbody input[type="checkbox"]:checked').each(function() {
				var $row = $(this).closest('tr');
				var rowData = {};
				rowData['kbId'] = $row.data('row-id');
				$row.find('td').each(function(index) {
					var $col = $(this);
					if(!$col.data('colname')) return;
					var colName = $col.data('colname');
					if(!m_knowledgebase_cols[index]['canEdit']) return;
					var $textarea = $col.find('textarea');
					var value = $textarea.val();
					if ($textarea.prop('required') && !value.trim()) {
						$textarea.addClass('twx-invalid-input');
						hasError = true;
					} else {
						$textarea.removeClass('twx-invalid-input');
						//$col.html(rowData[colName]);
					}
					rowData[colName] = value;
				});
				if(Object.keys(rowData).length > 1) entries.push(rowData);
			});
		} else if(m_knowledgebase_mode == 'new') {
			var rowData = {};
			$('#knowledgeBaseEntriesTable tr').eq(2).find('td').each(function(index) {
				var $col = $(this);
				if(!$col.data('colname')) return;
				var colName = $col.data('colname');
				if(!m_knowledgebase_cols[index]['canEdit']) return;
				var $textarea = $col.find('textarea');
				var value = $textarea.val();
				if ($textarea.prop('required') && !value.trim()) {
					$textarea.addClass('twx-invalid-input');
					hasError = true;
				} else {
					$textarea.removeClass('twx-invalid-input');
				}
				rowData[colName] = value;
			});
			if(Object.keys(rowData).length > 1) entries.push(rowData);
		}

		if (hasError) {
			alert("Please fill in all required fields.");
			return;
		}

		m_knowledgebase_mode == 'save';
		refreshKnowledgeBaseButtons();

		entries.length > 0 && $.ajax({
			url: twxChat.ajax_url,
			type: 'POST',
			data: {
				action: 'twx_chat_updateKnowledgeBaseAdmin',
				nonce: twxChat.nonce,
				entries: entries,
			},
			success: function(response) {
	
				// On success, update the HTML element with the response
				if(!response.success || !response.data) return;
				fetchKnowledgeBaseHTML();
			},
			error: function(xhr, status, error) {
				// Handle errors
				console.log('AJAX Error: ' + status + ' - ' + error);
			}
		});
	});

	// knowlege base file upload
    $('#twx-chat-kb-upload-file-form').on('submit', function (e) {
        e.preventDefault();

        let formData = new FormData(this);
		formData.append('action', 'twx_chat_uploadKnowledgeBaseFile'); // Include action as part of FormData

		$('#twx-chat-kb-messages').text('Uploading. Please wait...').css('display','flex');

        $.ajax({
            url: twxChat.ajax_url, 
            type: 'POST',
			data: formData,
            processData: false,
            contentType: false,
            success: function (response) {
				console.log('ajax response',response);
				if(response.success){
					$('#twx-chat-kb-messages').html('<p style="color: green;">' + response.data + '</p>');
				}else{
					$('#twx-chat-kb-messages').html('<p style="color: red;">Error: ' + response.data + '</p>');
				}
            },
            error: function (jqXHR, textStatus, errorThrown) {
				console.log('ajax error',jqXHR, textStatus,errorThrown);
                $('#twx-chat-kb-messages').html('<p style="color: red;">Error: ' + errorThrown.toString() + '</p>');
            }
        });
    });	

	// Check for Plugin Updates button - paid Plans Only see this
	$('#twx_chat_check_for_updates_btn').on('click', function() {
		checkForPluginUpdates();
	});

	// TODO future implementation for paid Plans auto-update
	/*
	$('#twx_chat_update_plugin_now_btn').on('click', function() {
		downloadAndInstallPluginUpdate();
	});
	*/

	// Download ZIP Plugin Update buttton - paid Plans Only see this
	$('#twx_chat_update_plugin_zip_btn').on('click', function() {
		if(window.TWXChatAdminPlus){
			TWXChatAdminPlus.downloadPluginUpdateZip();
		}
	});

});

var m_chatlog_period = '';
var m_chatlog_sortField = null;
var m_chatlog_sortAsc = true;

var m_knowledgebase_sortField = null;
var m_knowledgebase_sortAsc = true;
var m_knowledgebase_filters = {};
var m_knowledgebaseFilterTimeout = null;
var m_knowledgebase_cols = [];
var m_knowledgebase_mode = '';
var m_siteCrawlerRunning = false;