import { assert } from 'vitest';

/** @type {HTMLDivElement} */
let _container;

/**
 * @param {string} html
 * @param {{
 *  removeDataSvelte?: boolean,
 *  preserveComments?: boolean,
 * }} options
 */
export function normalize_html(html, options = {}) {
	const container = (_container ??= document.createElement('div'));

	if (!options.preserveComments) {
		html = html.replace(/(<!--.*?-->)/g, '');
	}

	if (options.removeDataSvelte) {
		html = html.replace(/(data-svelte-h="[^"]+")/g, '');
	}

	html = html.replace(/>[ \t\n\r\f]+</g, '><').trim();

	container.innerHTML = html;

	clean_children(container);

	return container.innerHTML.replace(/<\/?noscript\/?>/g, '');
}

/** @param {any} node */
function clean_children(node) {
	// sort attributes
	const attributes = Array.from(node.attributes).sort((a, b) => (a.name < b.name ? -1 : 1));

	attributes.forEach((attr) => {
		node.removeAttribute(attr.name);
	});

	attributes.forEach((attr) => {
		node.setAttribute(attr.name, attr.value);
	});

	let previous = null;
	// recurse
	[...node.childNodes].forEach((child) => {
		if (child.nodeType === 3) {
			// text
			if (
				node.namespaceURI === 'http://www.w3.org/2000/svg' &&
				node.tagName !== 'text' &&
				node.tagName !== 'tspan'
			) {
				node.removeChild(child);
			}

			child.data = child.data.replace(/[ \t\n\r\f]+/g, '\n');

			if (previous && previous.nodeType === 3) {
				previous.data += child.data;
				previous.data = previous.data.replace(/[ \t\n\r\f]+/g, '\n');

				node.removeChild(child);
				child = previous;
			}
		} else if (child.nodeType === 8) {
			// comment
			// do nothing
		} else {
			clean_children(child);
		}

		previous = child;
	});

	// collapse whitespace
	if (node.firstChild && node.firstChild.nodeType === 3) {
		node.firstChild.data = node.firstChild.data.replace(/^[ \t\n\r\f]+/, '');
		if (!node.firstChild.data.length) node.removeChild(node.firstChild);
	}

	if (node.lastChild && node.lastChild.nodeType === 3) {
		node.lastChild.data = node.lastChild.data.replace(/[ \t\n\r\f]+$/, '');
		if (!node.lastChild.data.length) node.removeChild(node.lastChild);
	}
}

/**
 *
 * @param {string} actual
 * @param {string} expected
 * @param {{
 *      message?: string,
 *      normalize_html?: {
 *         removeDataSvelte?: boolean,
 *        preserveComments?: boolean,
 *      },
 *      without_normalize?: boolean,
 * }} options
 */
export function assert_html_equal(actual, expected, options = {}) {
	if (options.without_normalize) {
		actual = actual.replace(/\r\n/g, '\n');
		expected = expected.replace(/\r\n/g, '\n');

		if (options.normalize_html.removeDataSvelte) {
			actual = actual.replace(/(\sdata-svelte-h="[^"]+")/g, '');
			expected = expected.replace(/(\sdata-svelte-h="[^"]+")/g, '');
		}
	} else {
		actual = normalize_html(actual, options.normalize_html);
		expected = normalize_html(expected, options.normalize_html);
	}

	try {
		assert.equal(actual, expected, options.message);
	} catch (err) {
		// Remove this function from the stack trace so that the error is shown in the test file

		if (Error.captureStackTrace) {
			Error.captureStackTrace(err, assert_html_equal);
		}
		throw err;
	}
}