const utils = require( 'ext.discussionTools.init' ).utils;

module.exports = {};

/**
 * Override mw.config with the given data. Used for testing different languages etc.
 * (Automatically restored after every test by QUnit.newMwEnvironment.)
 *
 * @param {Object} config
 */
module.exports.overrideMwConfig = function ( config ) {
	Object.assign(
		mw.config.values,
		config
	);
};

/**
 * Return the node that is expected to contain thread items.
 *
 * @param {Document} doc
 * @return {Element}
 */
module.exports.getThreadContainer = function ( doc ) {
	// In tests created from Parsoid output, comments are contained directly in <body>.
	// In tests created from old parser output, comments are contained in <div class="mw-parser-output">.
	const body = doc.body;
	const wrapper = body.querySelector( 'div.mw-parser-output' );
	return wrapper || body;
};

/**
 * Get the offset path from ancestor to offset in descendant
 *
 * @param {Node} ancestor The ancestor node
 * @param {Node} node The descendant node
 * @param {number} nodeOffset The offset in the descendant node
 * @return {string} The offset path
 */
function getOffsetPath( ancestor, node, nodeOffset ) {
	const path = [ nodeOffset ];
	while ( node !== ancestor ) {
		if ( node.parentNode === null ) {
			// eslint-disable-next-line no-console
			console.log( node, 'is not a descendant of', ancestor );
			throw new Error( 'Not a descendant' );
		}
		path.unshift( utils.childIndexOf( node ) );
		node = node.parentNode;
	}
	return path.join( '/' );
}

function getPathsFromRange( root, range ) {
	return [
		getOffsetPath( root, range.startContainer, range.startOffset ),
		getOffsetPath( root, range.endContainer, range.endOffset )
	];
}

/**
 * Massage comment data to make it serializable as JSON.
 *
 * @param {CommentItem} parent Comment item; modified in-place
 * @param {Node} root Ancestor node of all comments
 */
module.exports.serializeComments = function ( parent, root ) {
	if ( !parent.range.startContainer ) {
		// Already done as part of a different thread
		return;
	}

	// Can't serialize circular structures to JSON
	delete parent.parent;

	// Can't serialize the DOM nodes involved in the range,
	// instead use their offsets within their parent nodes
	parent.range = getPathsFromRange( root, parent.range );
	if ( parent.signatureRanges ) {
		parent.signatureRanges = parent.signatureRanges.map( ( range ) => getPathsFromRange( root, range ) );
	}
	if ( parent.timestampRanges ) {
		parent.timestampRanges = parent.timestampRanges.map( ( range ) => getPathsFromRange( root, range ) );
	}
	if ( parent.timestamp ) {
		parent.timestamp = parent.getTimestampString();
	}
	if ( !parent.displayName ) {
		delete parent.displayName;
	}

	// Unimportant
	delete parent.rootNode;

	// Ignore generated properties
	delete parent.authors;
	delete parent.commentCount;
	delete parent.oldestReply;
	delete parent.latestReply;

	parent.replies.forEach( ( comment ) => {
		module.exports.serializeComments( comment, root );
	} );
};