/**
 * Wraps the currently selected text in the browser with an HTML span element
 * that has the specified CSS class and a data attribute containing a unique
 * identifier.
 *
 * To avoid creating many nested spans when users click on formatted text
 * (e.g. inside <strong>, <em>, etc.), this function first normalises the
 * selected fragment by unwrapping any existing plugin comment spans and
 * empty wrapper <span> elements, then adds a single clean wrapper.
 *
 * @param {string} frontCommentClass    - The CSS class to apply to the wrapping span element.
 * @param {string} tempHighlightId - A temporary identifier for the unsaved highlight.
 */
function cfWrapSelectedText( frontCommentClass, tempHighlightId ) {
	const selection = window.getSelection();
	if ( selection.rangeCount > 0 ) {
		const range = selection.getRangeAt( 0 );

		// Create the mark element (better for wrapping formatted content)
		const span = document.createElement( 'mark' );
		span.classList.add( frontCommentClass );
		span.setAttribute( 'data-icfp-temp-highlight', tempHighlightId );

		// Before wrapping, expand range to include complete inline elements if selection spans them
		const inlineTags = [ 'STRONG', 'EM', 'B', 'I', 'U', 'CODE', 'MARK', 'A' ];
		const startContainer = range.startContainer;
		const endContainer = range.endContainer;
		
		// Check if start is inside an inline element and expand to include it
		if ( startContainer.nodeType === Node.TEXT_NODE ) {
			const parent = startContainer.parentElement;
			if ( parent && inlineTags.includes( parent.tagName ) ) {
				// Check if selection starts in the middle of the inline element
				const textBefore = startContainer.textContent.substring( 0, range.startOffset );
				if ( textBefore.length > 0 ) {
					// Selection starts in the middle - expand to include whole element
					try {
						range.setStartBefore( parent );
					} catch ( e ) {
						// Ignore if can't expand
					}
				}
			}
		}
		
		// Check if end is inside an inline element and expand to include it
		if ( endContainer.nodeType === Node.TEXT_NODE ) {
			const parent = endContainer.parentElement;
			if ( parent && inlineTags.includes( parent.tagName ) ) {
				// Check if selection ends in the middle of the inline element
				const textAfter = endContainer.textContent.substring( range.endOffset );
				if ( textAfter.length > 0 ) {
					// Selection ends in the middle - expand to include whole element
					try {
						range.setEndAfter( parent );
					} catch ( e ) {
						// Ignore if can't expand
					}
				}
			}
		}

		// Try to use surroundContents first - it handles cross-element selections better
		// and preserves formatting tags like <strong>, <em>, etc.
		let useSurroundContents = false;
		try {
			// surroundContents will wrap the entire selection including all formatting
			range.surroundContents( span );
			useSurroundContents = true;
			
			// Normalize any nested comment marks that might be inside
			const nestedSpans = span.querySelectorAll( 'mark.' + frontCommentClass + ', mark[datatext], span.' + frontCommentClass + ', span[datatext]' );
			nestedSpans.forEach( ( nestedSpan ) => {
				if ( nestedSpan !== span && nestedSpan.parentNode ) {
					const parent = nestedSpan.parentNode;
					while ( nestedSpan.firstChild ) {
						parent.insertBefore( nestedSpan.firstChild, nestedSpan );
					}
					parent.removeChild( nestedSpan );
				}
			} );
		} catch ( e ) {
			// surroundContents failed (e.g., range partially selects a non-text node)
			// Fall back to extractContents approach
			useSurroundContents = false;
		}

		// Fallback to extractContents method if surroundContents didn't work
		if ( ! useSurroundContents ) {

			// Extract the current selection so we can normalise it before wrapping.
			const extracted = range.extractContents();

		// Normalise the fragment to prevent deep nesting of spans.
		( function normalizeFragment( fragment ) {
			const walker = document.createTreeWalker(
				fragment,
				NodeFilter.SHOW_ELEMENT,
				null,
				false
			);

			const toUnwrap = [];

			while ( walker.nextNode() ) {
				const el = walker.currentNode;

				// Consider both mark and span elements (for backward compatibility)
				if ( ! el || ( el.tagName !== 'MARK' && el.tagName !== 'SPAN' ) ) {
					continue;
				}

				const classAttr = el.getAttribute( 'class' );
				const isOurCommentSpan =
					el.classList.contains( frontCommentClass ) ||
					el.hasAttribute( 'datatext' );
				const isEmptyWrapper =
					! classAttr || classAttr.trim() === '';

				// Unwrap old comment spans and empty wrapper spans.
				if ( isOurCommentSpan || isEmptyWrapper ) {
					toUnwrap.push( el );
				}
			}

			toUnwrap.forEach( ( el ) => {
				const parent = el.parentNode;
				if ( ! parent ) {
					return;
				}

				while ( el.firstChild ) {
					parent.insertBefore( el.firstChild, el );
				}
				parent.removeChild( el );
			} );
			} )( extracted );

			// Wrap the cleaned fragment in a single mark element used for inline comments.
			// Reuse the mark we already created
			span.appendChild( extracted );
			range.insertNode( span );
		}

		// Clean up empty inline wrappers (e.g. leftover <sup></sup>) that can be
		// created by the browser when the selection starts/ends inside them.
		const inlineTagsToClean = [ 'SUP', 'SUB' ];

		[ span.previousSibling, span.nextSibling ].forEach( ( sibling ) => {
			if (
				! sibling ||
				sibling.nodeType !== Node.ELEMENT_NODE ||
				! inlineTagsToClean.includes( sibling.tagName )
			) {
				return;
			}

			const hasMeaningfulContent =
				sibling.textContent && sibling.textContent.trim().length > 0;

			if ( ! hasMeaningfulContent && sibling.parentNode ) {
				sibling.parentNode.removeChild( sibling );
			}
		} );
	}
}

function icfpResolveBlockIdFromDatatext( datatext ) {
	if ( ! datatext ) return null;

	const mark = document.querySelector(
		`mark[datatext="${ datatext }"]`
	);

	if ( ! mark ) return null;

	return findBlockIdForMark( mark );
}


function icfpHighlightTextInBlock({
	blockId,
	searchText,
	datatext,
	commentId
  }) {
	const block = document.querySelector(
	  `[data-icfp-block-id="${blockId}"]`
	);
 
	if (!block || !searchText) return;
 
	// Prevent duplicate highlight
	if (block.querySelector(`mark[datatext="${datatext}"]`)) return;
 
	const walker = document.createTreeWalker(
	  block,
	  NodeFilter.SHOW_TEXT,
	  null,
	  false
	);
 
	while (walker.nextNode()) {
	  const node = walker.currentNode;
	  const index = node.nodeValue.indexOf(searchText);
 
	  if (index === -1) continue;
 
	  const range = document.createRange();
	  range.setStart(node, index);
	  range.setEnd(node, index + searchText.length);
 
	  const mark = document.createElement('mark');
	  mark.className = 'mdspan-front-comment';
	  mark.setAttribute('datatext', datatext);
	  mark.setAttribute('data-comment-id', commentId);
	  mark.setAttribute('data-icfp-block-id', blockId);
 
	  range.surroundContents(mark);
 
	  mark.scrollIntoView({
		behavior: 'smooth',
		block: 'center'
	  });
 
	  break; // ✅ highlight ONLY first valid match
	}
  }

/**
 * Displays a message box with a textarea for the user to enter a note for the selected text.
 * The message box includes "Save Note" and "Close" buttons to handle the note saving and
 * closing the message box.
 *
 * @param {string} selection            - The selected text that the note is being added for.
 * @param {string} tempHighlightId      - Temporary identifier for the wrapping mark element.
 */
function cfBoardDisplay( selection, tempHighlightId ) {

	const commentWrapper = document.querySelector(
		'.cf-front-comment-wrapper'
	);
	commentWrapper.style.transform = 'translateX(0px)';
	commentWrapper.setAttribute( 'data-icfp-temp-highlight', tempHighlightId );

	// Calculate the position of the message box based on the selection
	// const selectionRange = window
	// 	.getSelection()
	// 	.getRangeAt( 0 )
	// 	.getBoundingClientRect();
	const commentWrapperInner = commentWrapper.querySelector(
		'.cf-front-comment-inner-wrapper'
	);

	// focus on comment box textarea
	const commentTextarea = document.getElementById( 'comment' );
	commentTextarea?.focus();
	const cursorPosition = 5;
	commentTextarea?.setSelectionRange( cursorPosition, cursorPosition );

	// Ensure the inner wrapper is visible
	if( commentWrapperInner ) {
		commentWrapperInner.style.display = 'block';
	}
	if( commentWrapper ) {
		commentWrapper.style.transform = 'translateX(0px)';
	}
	

	// Add event listeners for cancel button
	document
		.getElementById( 'cf-front-cancel' )
		?.addEventListener( 'click', function () {
			commentWrapper.style.transform = 'translateX(100%)';
			//cfUnwrapSelectedText( selection, commentBoardDatatext );
		} );
}

/**
 * Unwraps the currently selected text in the browser that was previously wrapped
 * with a span element by the `cfWrapSelectedText` function.
 *
 * @param {string} selection            - The selected text that was previously wrapped.
 * @param {string} commentBoardDatatext - The unique identifier used in the data attribute of the wrapping span element.
 */
function cfUnwrapSelectedText( selection, commentBoardDatatext ) {
	const element = document.querySelector(
		`[data-icfp-temp-highlight="${ commentBoardDatatext }"]`
	);
	if ( element ) {
		const parent = element.parentNode;
		while ( element.firstChild ) {
			parent.insertBefore( element.firstChild, element );
		}
		parent.removeChild( element );
	}
}

const editholdy_is_activate = false;
/**
 * Converts the provided HTML content into a serialized Gutenberg block.
 *
 * @param {string}  content               - The HTML content to be converted to a Gutenberg block.
 * @param {boolean} [remove_spaces=false] - Whether to remove newlines and carriage returns from the serialized block.
 * @return {string} The serialized Gutenberg block.
 */
function convert_to_gutenberg( content, remove_spaces = false ) {
	const gutblock = wp.blocks.rawHandler( { HTML: content } );
	let serelized = wp.blocks.serialize( gutblock );
	serelized = remove_spaces ? serelized.replace( /(\n|\r)/g, '' ) : serelized;
	return serelized;
}

/**
 * Scrolls the page to the location of the selected text content.
 *
 * @param {HTMLElement} element - The HTML element that was clicked to trigger this function.
 */
function cfFrontCommentOnClick( element ) {
	// Clear previous highlights.
	// For spans that were added only for highlighting (no "datatext" attribute),
	// completely unwrap them so we don't accumulate nested spans over time.
	// For persisted comment spans (with "datatext"), just remove the highlight class.
	Array.from(
		document.querySelectorAll( '.mdspan-front-comment' )
	).forEach( ( el ) => {
		if (
			el.hasAttribute( 'data-comment-id' ) ||
			el.hasAttribute( 'data-icfp-highlight-id' ) ||
			el.hasAttribute( 'datatext' )
		) {
			el.classList.remove( 'mdspan-front-comment' );
			return;
		}

		const parent = el.parentNode;
		if ( ! parent ) {
			return;
		}

		while ( el.firstChild ) {
			parent.insertBefore( el.firstChild, el );
		}
		parent.removeChild( el );
	} );
	Array.from( document.querySelectorAll( '.cf-media-highlighted' ) ).forEach(
		( el ) => el.classList.remove( 'cf-media-highlighted' )
	);

	const wordCount = {}; // Object to track word occurrences

	const blockContent = element.getAttribute( 'data-content' );
	//const inputHTML = document.querySelectorAll('.single-post main .elementor')[0].innerHTML;
	const commentCountTop = document.querySelector(
		'.cf-front-comment-count_wrap'
	);
	let contentQuerySelectorClass = commentCountTop?.parentElement?.className;
	contentQuerySelectorClass = '.' + contentQuerySelectorClass?.trim().replace(/\s+/g, '.');
	const inputHTML = document.querySelectorAll(contentQuerySelectorClass)[0]?.innerHTML;
	// Check if blockContent is a URL then highlight the media element
	if ( isValidURL( blockContent ) ) {
		cfHighlightMediaElements( blockContent );
		return;
	}

	// Decide what text to search for. If the original selection spanned multiple
	// inline elements and contains spaces, we fall back to the longest word so
	// that we can still highlight a meaningful part of the selection.
	// const raw = blockContent || '';
	// const words = raw.split(/\s+/).filter(Boolean);
	// let searchKey = raw;
	// if ( words.length > 1 ) {
	// 	searchKey = words
	// 		.slice()
	// 		.sort( ( a, b ) => b.length - a.length )[ 0 ];
	// }

	function escapeRegExp( string ) {
		return string.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
	}

	// Regex pattern to make sure words are matched exactly
	const regexPattern = new RegExp(
		`\\b(${ escapeRegExp( searchKey ) })\\b`,
		'gi'
	);
	
	// Replace the matched words with a span
	// const highlightedContent = inputHTML.replace( regexPattern, function( matchedWord ) {
	// 	const lowerCaseWord = matchedWord.toLowerCase();
		
	// 	// Track how many times the word has been found
	// 	if ( ! wordCount[ lowerCaseWord ] ) {
	// 		wordCount[ lowerCaseWord ] = 0;
	// 	}
	// 	wordCount[ lowerCaseWord ]++;
		
	// 	const highlightClass = 'mdspan-front-comment';

	// 	// Wrap the matched word in a mark element with class
	// 	return `<mark class="${ highlightClass }">${ matchedWord }</mark>`;
	// } );

	// document.querySelector( contentQuerySelectorClass ).innerHTML =
	// 	highlightedContent;

	if ( highlightedContent.includes( searchKey ) ) {
		const selectedText = jQuery(
			".single-post main .elementor *:contains('" + searchKey + "')"
		);

		// If the matching element is found, highlight it
		if (selectedText) {
			// // Select the element with the mdspan-front-comment class
			// const highlightedElement = document.querySelector('.mdspan-front-comment');

			// // If the element is found, scroll to it
			// if (highlightedElement) {
			// 	highlightedElement.scrollIntoView({
			// 		behavior: 'smooth', // Smooth scroll animation
			// 		block: 'center', // Align the element to the center of the viewport
			// 		inline: 'nearest' // Align the element to the nearest edge (left or right)
			// 	});
			// }
		}
	}
	// ✅ NEW: block-safe highlight (Elementor + Gutenberg)
	const datatext = element.getAttribute( 'data-datatext' );
	const commentId = element.getAttribute( 'data-comment-id' );
	const commentText = element.getAttribute( 'data-content' );

	const blockId = icfpResolveBlockIdFromDatatext( datatext );

	// ❌ Abort if block cannot be resolved
	if ( ! blockId ) {
		console.warn(
			'Elementor highlight aborted – blockId not resolved',
			{ datatext }
		);
		return;
	}

	// ✅ Use SAFE block-based highlight
	if ( typeof icfpHighlightTextInBlock === 'function' ) {
		icfpHighlightTextInBlock({
			blockId,
			searchText: commentText,
			datatext,
			commentId
		});
	}

}

/**
 * Highlights the specified text within the provided HTML content by wrapping it
 * in a span element with the class 'mdspan-front-comment'.
 *
 * @param {string} html - The HTML content to search and highlight the text within.
 * @param {string} text - The text to search for and highlight within the HTML content.
 */
function cfHighlightText( html, text ) {
	// Create a temporary element to strip HTML tags
	const tempElement = document.createElement( 'div' );
	tempElement.innerHTML = html;
	const plainText = tempElement.textContent || tempElement.innerText || '';

	const inputText = jQuery( ".single-post *:contains('" + text + "')" );

	let innerHTML = plainText;
	const index = innerHTML.indexOf( text );
	if ( index >= 0 ) {
		innerHTML =
			innerHTML.substring( 0, index ) +
			"<mark class='mdspan-front-comment'>" +
			innerHTML.substring( index, index + text.length ) +
			'</mark>' +
			innerHTML.substring( index + text.length );
		inputText[ 0 ].innerHTML = innerHTML;
	}
}

/**
 * Hides the front-end comment board by resetting the comment input, hiding
 * the comment wrapper, and hiding any success message.
 */
function cfHideFrontCommentBoard() {
	document.querySelector( '.cf-front-comment-wrapper #comment' ).value = '';
	document.querySelector( '.cf-front-comment-wrapper' ).style.transform =
		'translateX(100%)';
	document.querySelector( '.cf-front-comment-inner-wrapper' ).style.display =
		'none';
	document.querySelector( '.ajax-success' ).style.display = 'none';
}

/**
 * Validates the provided URL using a regular expression pattern.
 * The pattern checks for the following parts of a URL:
 * 
 * @param {string} url - The URL to validate.
 * @returns {boolean} Whether the URL is valid.
 */
function isValidURL( url ) {
	const pattern = new RegExp('^(https?:\\/\\/)?' + // Protocol
		'((([a-zA-Z0-9$_.+!*\'(),;-])+\\.)+[a-zA-Z]{2,})' + // Domain name
		'(\\/[a-zA-Z0-9$_.+!*\'(),;:@&=-]*)*' + // Path
		'(\\?[a-zA-Z0-9$_.+!*\'(),;:@&=-]*)?' + // Query string
		'(#[a-zA-Z0-9-]*)?$', 'i'); // Fragment
	return pattern.test( url );
}

/**
 * Function to highlight matched elements with the specified media URL.
 * The function uses querySelector to directly select the element with the matching src attribute.
 * If the matching element is found, it is highlighted by adding a red border.
 * If no matching element is found, a message is logged to the console.
 * The function also scrolls to the highlighted element using scrollIntoView with smooth behavior.
 *
 * @param {string} mediaUrl - The URL of the media element to highlight.
 * @returns {void}
 */
function cfHighlightMediaElements(mediaUrl) {
	// Use querySelector to directly select the element with the matching src
	const matchingElement = document.querySelector(`.single-post main .elementor img[src="${mediaUrl}"], .single-post main .elementor audio[src="${mediaUrl}"], .single-post main .elementor video[src="${mediaUrl}"]`);

	Array.from(
		document.querySelectorAll( '.cf-media-highlighted' )
	).forEach( ( el ) =>
		el.classList.remove( 'cf-media-highlighted' )
	);

	// If the matching element is found, highlight it
	if (matchingElement) {
		matchingElement.classList.add('cf-media-highlighted');

		// Select the element with the cf-media-highlighted class
		const highlightedElement = document.querySelector('.cf-media-highlighted');

		// If the element is found, scroll to it
		if (highlightedElement) {
			highlightedElement.scrollIntoView({
				behavior: 'smooth', // Smooth scroll animation
				block: 'center', // Align the element to the center of the viewport
				inline: 'nearest' // Align the element to the nearest edge (left or right)
			});
		}
	}
}

document.addEventListener( 'DOMContentLoaded', () => {

	const commentForm = document.querySelector( '.cf-front-comment-wrapper .comment-form' );
	const submitButton = commentForm?.querySelector('.cf-front-comment-wrapper .comment-form input[type="submit"]');

	// Function to handle clicks and get the 'src' attribute for img and video elements
	function cfHandleImgVideoElementClick(event) {
		const clickedElement = event.target;

		if (clickedElement.tagName === 'IMG' || clickedElement.tagName === 'VIDEO') {
			const src = clickedElement.getAttribute('src');
			const xAxis = event.pageX;
			const yAxis = event.pageY - 35;
			const toolbar = document.getElementById('toolbar');
			toolbar.style.opacity = 1;
			toolbar.style.transform = `translate(${xAxis}px, ${yAxis}px)`;

			// Check if the comment form has a hidden input field for media_src
			const hiddenInput = commentForm.querySelector('#media_src');
			if (hiddenInput) {
				hiddenInput.value = src;
			} else {
				// Create a hidden input field to store the src value
				const hiddenInput = document.createElement('input');
				hiddenInput.setAttribute('type', 'hidden');
				hiddenInput.setAttribute('name', 'media_src');
				hiddenInput.setAttribute('value', src);
				hiddenInput.setAttribute('id', 'media_src');
				commentForm.querySelector('.form-submit').appendChild(hiddenInput);
			}
		}
	}

	// Attach event listeners to all img and video tags on the page
	document.querySelectorAll('.single-post main .elementor img, .single-post main .elementor video').forEach(element => {
		element.addEventListener('click', cfHandleImgVideoElementClick);
	});

	// Function to handle the play event for audio elements
	function cfHandleAudioPlay(event) {
		const audioElement = event.target;
		const src = audioElement.getAttribute('src');
		const xAxis = event.target.offsetLeft;
		const yAxis = event.target.offsetTop;
		const toolbar = document.getElementById('toolbar');
		toolbar.style.opacity = 1;
		toolbar.style.transform = `translate(${xAxis}px, ${yAxis}px)`;

		// Check if the comment form has a hidden input field for media_src
		const hiddenInput = commentForm.querySelector('#media_src');
		if (hiddenInput) {
			hiddenInput.value = src;
		} else {
			// Create a hidden input field to store the src value
			const hiddenInput = document.createElement('input');
			hiddenInput.setAttribute('type', 'hidden');
			hiddenInput.setAttribute('name', 'media_src');
			hiddenInput.setAttribute('value', src);
			hiddenInput.setAttribute('id', 'media_src');
			commentForm.querySelector('.form-submit').appendChild(hiddenInput);
		}
	}

	// Attach play event listener to all audio tags
	document.querySelectorAll('.single-post main .elementor audio').forEach(element => {
		element.addEventListener('play', cfHandleAudioPlay);
	});

	document.addEventListener('click', function (e) {
		if (e.target.classList.contains('comment-reply-link')) {
			document.querySelector('.cf-front-comment-wrapper .comment-reply-title').style.display = 'block';

			const commentID = e.target.dataset.commentid;
			const commentParent = document.querySelector('.cf-front-comment-wrapper #comment_parent');

			commentParent.value = commentID;

			if (submitButton) {
				submitButton.value = 'Post Reply';
			}

			document.querySelector('.cf-front-comment-wrapper .comment-reply-title').style.display = 'block';
			if( null !== document.querySelector('.cf-front-comment-wrapper .logged-in-as') ) {
				document.querySelector('.cf-front-comment-wrapper .logged-in-as').style.display = 'none';
			}

			Array.from( document.querySelectorAll( '.ajax-success' ) ).forEach(
				( el ) => el.remove( 'ajax-success' )
			);
		}
	} );
} );
