/**
 * @license Copyright (c) 2017-2025, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md.
 */

'use strict';

const ParserCursor = require( './parser-cursor' );

/**
 * Transforms Github style admonitions in markdown to a rendered callouts.
 *
 * For example:
 *
 * > [!IMPORTANT]
 * > This is an important message.
 * > It should be highlighted.
 *
 * @param mapperFn - A function that takes an admonition object and returns a string.
 * @param str - The string to transform.
 * @returns The transformed string with admonitions replaced by the result of the mapper function.
 */
function transformMarkdownAdmonitions( mapperFn, str ) {
	const cursor = new ParserCursor( str );
	let acc = '';

	for ( ; cursor.index < cursor.text.length; ) {
		const ch = cursor.text[ cursor.index ];

		if ( ch === '>' ) {
			const maybeAdmonition = cursor.try( eatAdmonition );

			if ( maybeAdmonition ) {
				acc += mapperFn( maybeAdmonition );
				continue;
			}
		}

		acc += ch;
		cursor.index++;
	}

	return acc;
};

/**
 * Fast check if the string contains an admonition block.
 */
function fastCheckIfContainsAdmonition( str ) {
	return str.includes( '> [!' ) && str.includes( ']' );
}

/**
 * Eats an admonition block if it matches the expected pattern.
 *
 * @param cursor - The cursor to use for parsing.
 * @returns An object representing the admonition type and content, or undefined (if not matched)
 */
function eatAdmonition( cursor ) {
	if ( !cursor.eatTerminals( '> [!' ) ) {
		return;
	}

	const type = cursor.eatUntilTerminal( ']' ).trim();

	cursor.eatAllNextTerminals( ' \t\n ' );

	const content = cursor.accumulateEaters( eatAdmonitionLine );

	// Let's revert cursor back to the ending newline if it was consumed.
	if ( content.length > 0 && cursor.text[ cursor.index - 1 ] === '\n' ) {
		cursor.index--;
	}

	return {
		type,
		content
	};
}

/**
 * Eats a line of an admonition block.
 *
 * @param cursor - The cursor to use for parsing.
 * @returns The content of the admonition line, or undefined if not matched.
 */
function eatAdmonitionLine( cursor ) {
	cursor.eatIndent();

	if ( !cursor.eatTerminal( '>' ) ) {
		return;
	}

	return cursor.eatUntilEOL().trim();
}

exports.transformMarkdownAdmonitions = transformMarkdownAdmonitions;
exports.fastCheckIfContainsAdmonition = fastCheckIfContainsAdmonition;
