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 getColumnNamesFromHtml(html) {
	const $html = jQuery(html);
	const columnNames = $html.find('thead th').map(function() {
		return jQuery(this).text().trim();
	}).get();
	return columnNames;
}

function getJSONKeyByValue(obj, value) {
    return Object.keys(obj).find(key => obj[key] === value);
}

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 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 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...','twx-chat')).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','twx-chat')+` ${avatarId} `+__('not found','twx-chat')+`">`));
		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;">`+__('You have reached the Avatar Character limit. To access more Characters, like this one, please subscribe to one of our','twx-chat')+` <a href="https://twxchat.twxlabs.au/download/plus" target="_blank">`+__('paid plans','twx-chat')+`</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','twx-chat')+`</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_]+:[^:]+(?:\:\d{1,3}%?)?$/;
  	if($input.val() && !regex.test($input.val())){
		$input.parent().append(jQuery('<div class="validation-msg" style="color:red;">'+__('Invalid format','twx-chat')+'</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_kb_or_assistant($input){
	// 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_stream($input){
	// At the moment, only non-streaming works for OpenAI Assistants
	$input.parent().children('.validation-msg').remove();
	$kbAsst = jQuery('#twx-chat-chatbot-edit .twx_chatbot_att[name="kb_or_assistant"]');
	if($kbAsst.length && $kbAsst.val() === 'openai_assistant' && $input.val() === 'true'){
		$input.parent().append(jQuery('<div class="validation-msg" style="color:red;">'+__('At this time, streaming is not available for OpenAI Assistant chatbots','twx-chat')+'</div>'));
	}
}

// Called dynamically from fetchEditChatbotHTML (there won't be an IDE reference)
function onChange_chatbot_att_avatar_voice_name($input) {
	validateChatbotAvatarAtts();
	const value = $input.val();
	//const regex = /^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+(:\d{1,3}%?)?$/;
	const parts = value.split(':');
	const provider = parts[0] ?? '';
	const voiceName = parts[1] ?? '';

	if(provider === 'aws' && !jQuery('#twx_chat_aws_cognito_identity_pool_id').val()){
		jQuery('#twxchat_avatar_voice_test_btn').prop('disabled',true);
		$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','twx-chat')+'</div>'));
	}else{

		isPollyNeuralVoiceSupported(voiceName, function(supported, voices) {
			if (supported) {
				jQuery('#twxchat_avatar_voice_test_btn').prop('disabled',false);
				//console.log("✅ Voice is supported with neural engine in this region.");
			} else {
				jQuery('#twxchat_avatar_voice_test_btn').prop('disabled',true);
				$input.parent().append(jQuery('<div class="validation-msg" style="color:red;">'+__('This voice is not supported in the AWS region of your Cognito Pool Identity Key in the','twx-chat')+'\'General\' tab. '+__('Ensure you have followed the','twx-chat')+' <span class="twx-chat-feature-button" data-feature="aws_cognito_identity_pool_id">'+__('correct instructions','twx-chat')+'</span> '+__('especially those relating to Regions.','twx-chat')+'</div>'));
				console.warn("❌ Voice not supported with neural engine here.");
				console.log("Available voices:", voices.map(v => v.Id));
			}
		});

	}
}

// 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">`));
	}
}

// Called dynamically from fetchEditChatbotHTML (there won't be an IDE reference)
function onChange_chatbot_att_assistant_id($input) {
    $input.parent().children('.validation-msg').remove();
    // Ensure value starts with "asst_"
    const regex = /^asst_[a-zA-Z0-9_-]*$/;
    if ($input.val() && !regex.test($input.val())) {
        $input.parent().append(
            jQuery('<div class="validation-msg" style="color:red;">'+__('Invalid format. OpenAI Assistant IDs must begin with an \'asst_\'','twx-chat')+'</div>')
        );
    }
}

function validateChatbotAvatarAtts(){

	let $avatar_voice = jQuery('#twx-chat-chatbot-edit .twx_chatbot_att[name="avatar_voice_name"]');
	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','twx-chat')+'</div>';
		$avatar_voice.parent().append(jQuery(html));
		$avatar_character.parent().append(jQuery(html));
	}
}

function showHideChatbotAttributes(){

	// show or hide chatbot attribute fields as needed
	if(twxChat.all_chatbot_atts === undefined) return;

	jQuery('#twx-chat-chatbot-edit .twx_chatbot_att').each(function(index){
		let $att = jQuery(this);
		const name = $att.attr('name');
		//const value = $att.val();
		let show = true;

		if (twxChat.all_chatbot_atts[name]) {
			const attDef = twxChat.all_chatbot_atts[name];
			if(attDef['parent_attr'] && attDef['parent_attr_value']){
				const $parentAtt = jQuery(`#twx-chat-chatbot-edit .twx_chatbot_att[name="${attDef['parent_attr']}"]`);
				if($parentAtt && $parentAtt.val() !== attDef['parent_attr_value']){
					show = false;
				}
			}
		}

		if(show){
			$att.closest('tr').show();
		}else{
			$att.closest('tr').hide();
		}
	});

}

function fetchChatbotsHTML() {

	// Get Chatbots Table contents

	jQuery('#twx-chat-chatbots-table').html('');
	jQuery('#twx-chat-chatbots-messages').text(__('Loading Chatbots. Please wait...','twx-chat')).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','twx-chat')+`: ${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...','twx-chat')).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','twx-chat')+`: ${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:visible').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() {
				showHideChatbotAttributes();
				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 to ensure validations are run and show/hide executed
			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','twx-chat')+': ' + 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 initAWSPolly(){

	if (typeof AWS === "undefined" || !AWS.Polly) return;

    if (!twxChat.aws_cognito_identity_pool_id) {
        console.error('Missing AWS Identity Pool ID. Avatar speech disabled.');
        return;
    }

    // Parse region from Identity Pool ID
    const [region, ...restOfId] = twxChat.aws_cognito_identity_pool_id.split(':');
    if (!region) {
        console.error('Invalid AWS Identity Pool ID:', twxChat.aws_cognito_identity_pool_id);
        return;
    }

    AWS.config.region = region;
    AWS.config.credentials = new AWS.CognitoIdentityCredentials({
      IdentityPoolId: twxChat.aws_cognito_identity_pool_id
    });

	m_polly = new AWS.Polly();
}

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) {

			// need full page reload in order to populate chatbot dropdowns in other tabs (like Customise)
			window.location.reload();
			/*
			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?','twx-chat'))) return;

	let $parent = jQuery(button).parent();
	jQuery(button).remove();
	let $dlResults = jQuery(`<div class="download-avatar-assets-results">`+__('Downloading. Please wait...','twx-chat')+`</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','twx-chat'));
			}).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...','twx-chat')+'</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);
	});
}

function onAWSPollyPlayClick(){
	const $voiceSelect = jQuery('#twx-chat-chatbot-edit .twx_chatbot_att[name="avatar_voice_name"]');
	if(!$voiceSelect || !$voiceSelect.val()) return;
	const $voiceSpeed = jQuery('#twx-chat-chatbot-edit .twx_chatbot_att[name="avatar_voice_speed"]');
	if(!$voiceSpeed || !$voiceSpeed.val()) return;
	const parts = $voiceSelect.val().split(':');
	const provider = parts[0] || '';
    const name     = parts[1] || '';
    const rate = $voiceSpeed.val()+'%';
	const text = `Hi! This is the voice for ${name}.`;
	const callbackFn = function() {
		jQuery('#twxchat_avatar_voice_test_btn').prop('disabled',false);
	};
	jQuery('#twxchat_avatar_voice_test_btn').prop('disabled',true);
	speakPollyText(text, name, rate, callbackFn);
}

function isPollyNeuralVoiceSupported(voiceName, callback) {
    if (!m_polly) {
        console.error("Polly client (m_polly) not initialized.");
        callback(false, []);
        return;
    }

    const params = {
        Engine: "neural" // we only want neural voices
    };

    m_polly.describeVoices(params, function(err, data) {
        if (err) {
            console.error("Polly describeVoices error:", err);
            callback(false, []);
            return;
        }

        const voices = data.Voices || [];
        const found = voices.some(v => 
            v.Id === voiceName && v.SupportedEngines.includes("neural")
        );

        callback(found, voices);
    });
}

function speakPollyText(text, voice, rate, onEndCallback) {

    const ssml = `<speak><prosody rate="${rate}">${text}</prosody></speak>`;

    const params = {
        OutputFormat: "mp3",
        Text: ssml,
        TextType: "ssml",
        VoiceId: voice,
        Engine: "neural"
    };

    m_polly && m_polly.synthesizeSpeech(params, function(err, data) {
        if (err) {
            console.error("Polly error:", err);
            return;
        }
        if (data && data.AudioStream) {
            const uInt8Array = new Uint8Array(data.AudioStream);
            const blob = new Blob([uInt8Array.buffer], { type: "audio/mpeg" });
            const url = URL.createObjectURL(blob);

            const audio = new Audio(url);

            // Release the blob when finished playing
            audio.addEventListener("ended", function() {
                URL.revokeObjectURL(url);
				if(onEndCallback) onEndCallback();
            });

            // Optional: stop and release previous audio if you want single playback
            if (window._pollyAudio && !window._pollyAudio.paused) {
                window._pollyAudio.pause();
                URL.revokeObjectURL(window._pollyAudio.src);
            }

            window._pollyAudio = audio; // store globally if needed
            audio.play();
        }
    });
}

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
    let activeTab = localStorage.getItem('twx_chat_active_tab') || 'general';
    if(window.location.hash) {
		// user has requested specific tab using #
		const hash = window.location.hash.replaceAll('#','');
		if($('.nav-tab[data-tab="' + hash + '"]').length) activeTab = hash;
	}
	//console.log("activeTab:",activeTab);
    $('.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 siteurl = encodeURIComponent(window.location.origin);
		const locale = encodeURIComponent(twxChat.locale);
		const url = `${twxChat.twxchat_support_base_url}/api/feature_info.php?feature=${feature}&siteurl=${siteurl}&locale=${locale}`;
		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() {
		TWXChatAdminPlus && TWXChatAdminPlus.downloadChatLogCSV();
	});

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

	$('#twx-chat-clear-chatlog-filters-btn').on('click', function() {
		TWXChatAdminPlus && TWXChatAdminPlus.clearChatLogFilters();
	});

	// Chat Log periods setup
	const periods =  twxChat.chatLogFilterPeriods;
	var html = 'Period: ';
    Object.keys(periods).forEach(function(label) {
        const value = periods[label];
		html += '&nbsp;&nbsp;<button class="twx_chat_chatlog_filter_btn" onclick="TWXChatAdminPlus.fetchChatLogHTML(event,\''+value+'\')">'+label+'</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/Cancel Run
		$('#twx_chat_site_crawler_crawl_now_btn').hide();
		$('#twx_chat_site_crawler_cancel_crawl_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...','twx-chat')+'</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?','twx-chat'))) {
			$(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?','twx-chat'))) {
			$(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();

	// Init AWS Polly for voice tests
	initAWSPolly();

	$(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?','twx-chat'))) 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','twx-chat')+`</th><th>`+__('Description','twx-chat')+`</th><th>`+__('Actions','twx-chat')+`</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','twx-chat')+`</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)','twx-chat')+`</label>
				<input type="text" name="user_fields[flashcard_title]" placeholder="eg: `+__('Your info','twx-chat')+`" class="twx_chatbot_att regular-text" size="100px" value="`+__('Your fields','twx-chat')+`" />
			</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','twx-chat')+` ${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','twx-chat')+` ${numToDelete} `+__('entries?','twx-chat'));
		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','twx-chat')+` ${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');
					}
					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.','twx-chat'));
			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
		formData.append('_wpnonce', twxChat.nonce);

		$('#twx-chat-kb-messages').text(__('Uploading. Please wait...','twx-chat')).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) {
				let errorMsg = '';
				if(jqXHR.responseJSON?.data){
					errorMsg = jqXHR.responseJSON.data;
				}else{
					if(jqXHR.responseText){
						errorMsg = jqXHR.responseText;
					}
				}
				console.log('ajax error',jqXHR, textStatus,errorThrown);
				if(errorMsg) $('#twx-chat-kb-messages').html(`<p style="color: red;">Error: ${errorMsg}</p>`);
            }
        });
    });	

	// Check for Plugin Updates button - paid Plans Only see this
	$('#twx_chat_check_for_updates_btn').on('click', function(e) {
		e.preventDefault();
		if(window.TWXChatAdminPlus){
			TWXChatAdminPlus.checkForPluginUpdates();
		}else{
			alert(__('This feature is not available. Please ensure you have a valid TWX-Chat Customer Key entered','twx-chat'));
		}
	});

	// Download and Install Plugin Update buttton - paid Plans Only see this
	$('#twx_chat_update_plugin_now_btn').on('click', function(e) {
		e.preventDefault();
		if(window.TWXChatAdminPlus){
			if(confirm(__('Are you sure you want to upgrade the TWX-Chat plugin now?','twx-chat'))) TWXChatAdminPlus.downloadAndInstallPluginUpdate();
		}else{
			alert(__('This feature is not available. Please ensure you have a valid TWX-Chat Customer Key entered','twx-chat'));
		}
	});

	// Download ZIP Plugin Update buttton - paid Plans Only see this
	$('#twx_chat_update_plugin_zip_btn').on('click', function(e) {
		e.preventDefault();
		if(window.TWXChatAdminPlus){
			TWXChatAdminPlus.downloadPluginUpdateZip();
		}else{
			alert(__('This feature is not available. Please ensure you have a valid TWX-Chat Customer Key entered','twx-chat'));
		}
	});

	$('#twx_chat_openai_embedding_model').on('change', function() {
		const currentModel = $(this).data('current-model');
		const newValue = $(this).val();
		if(newValue !== currentModel){
			if(!confirm(__('Changing the Embedding Model will re-index your Knowledge Base after clicking "Save Changes". This may take some time. Are you sure you wish to continue?','twx-chat'))){
				$(this).val(currentModel);
			}
		}
	});

	// Handle Translate button clicks
	$(document).on('click', '.twx_chat_chatlog_translate_lang_btn', function(e) {

		e.preventDefault();
		const $btn = $(this);

		let $currentTd = $btn.closest('td');
		let $spanLang = $currentTd.find('span');		
		const langCode = $spanLang.text().trim();
		let $tdUserPrompt = $currentTd.next('td');
		let $tdBotResponse = $tdUserPrompt.next('td');

		// check if already translated and if so, toggle visibility and return
		if ($tdUserPrompt.find('.twx-original-text').length > 0 &&
			$tdBotResponse.find('.twx-original-text').length > 0) {

			// check which needs to be displayed
			const showOriginal = $tdUserPrompt.find('.twx-original-text').is(':hidden');
			$tdUserPrompt.find('.twx-original-text').toggle(showOriginal);
			$tdBotResponse.find('.twx-original-text').toggle(showOriginal);
			$tdUserPrompt.find('.twx-translated-text').toggle(!showOriginal);
			$tdBotResponse.find('.twx-translated-text').toggle(!showOriginal);

			return; // Stop — no AJAX needed
		}

		// Grab their text
		const userPrompt = $tdUserPrompt.text().trim();
		const botResponse = $tdBotResponse.text().trim();

		addSpinner($btn, true);

		jQuery.ajax({
			url: twxChat.ajax_url,
			type: 'POST',
			data: {
				action: 'twx_chat_getTranslatedLangText',
				nonce: twxChat.nonce,
				fromLangCode: langCode,
				texts: [userPrompt, botResponse]
			},
			dataType: 'json',
			success: function(response) {
				removeSpinner($btn,true);
				if (response.success) {
					//$spanLang.text(response.data.toLangCode || langCode);
					if(response.data.translatedTexts) {
						$tdUserPrompt.text('');
						$tdBotResponse.text('');
						
						// Append divs with orig and translated text
						$tdUserPrompt.append('<div class="twx-original-text" style="display:none;">' + userPrompt.replace(/</g, "&lt;") + '</div>');
						$tdBotResponse.append('<div class="twx-original-text" style="display:none;">' + botResponse.replace(/</g, "&lt;") + '</div>');

						$tdUserPrompt.append('<div class="twx-translated-text">' + response.data.translatedTexts[0].replace(/</g, "&lt;") + '</div>');
						$tdBotResponse.append('<div class="twx-translated-text">' + response.data.translatedTexts[1].replace(/</g, "&lt;") + '</div>');
					}
				}
			},
			error: function(xhr, status, error) {
				console.log('twx_chat_chatlog_translate_lang_btn Error:', error);
				removeSpinner($btn,true);
				throw error;
			}
		});

	});

});

const { __ } = wp.i18n; // required for language translations

var m_chatlog_period = '';
var m_chatlog_sortField = null;
var m_chatlog_sortAsc = true;
var m_chatlog_filters = {};
var m_knowledgebase_sortField = null;
var m_knowledgebase_sortAsc = true;
var m_knowledgebase_filters = {};
var m_knowledgebase_cols = [];
var m_knowledgebase_mode = '';
var m_siteCrawlerRunning = false;
var m_polly = null;
