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

'use strict';

const cheerio = require( 'cheerio' );

module.exports = function toc( data, options = {} ) {
	const $ = cheerio.load( data, null, false );

	let usedHeadings = [ 'h2', 'h3', 'h4', 'h5', 'h6' ];

	if ( options.tocLimit >= 0 ) {
		usedHeadings = usedHeadings.slice( 0, options.tocLimit );
	}

	usedHeadings = usedHeadings.join( ',' );

	const headings = $( usedHeadings )
		.filter( function() {
			return !$( this ).parents( '.live-snippet, .collapsing-list__item' ).length;
		} );
	const className = options.class || 'secondary-navigation';

	if ( !headings.length ) {
		return '';
	}

	const $r = cheerio.load( `<nav class=${ className }><h3>Table of contents</h3></nav>`, null, false ); // result object
	const tocLastLevels = [ $r( 'nav' ), 0, 0, 0, 0, 0, 0 ];

	headings.each( function() {
		const hLevel = Number( this.name[ 1 ] );
		const id = $r( this ).attr( 'id' );
		const text = $r( this ).find( '.headerlink' ).remove().end().text().trim()
			// Replace the chevrons with their HTML escape characters to avoid rendering HTML in ToC.
			.replace( /</g, '&lt;' )
			.replace( />/g, '&gt;' );

		const newItem = `<li><a class="a11y-focusable" href="#${ id }" title="${ text }">${ text }</a></li>`;

		if ( tocLastLevels[ hLevel ] ) {
			// If there already is a parent node for current heading level, append the new toc item there.
			tocLastLevels[ hLevel ].append( newItem );
		} else {
			// If there's no parent node for current heading level, create it and append.
			if ( $r( 'li' ).length ) {
				$r( 'li' ).last().append( `<ol>${ newItem }</ol>` );
			} else {
				$r( 'nav' ).append( `<ol>${ newItem }</ol>` );
			}

			// Assign new created <ol> element to the helper array.
			tocLastLevels[ hLevel ] = $r( 'ol' ).last();
		}

		// After adding new toc item, lower heading levels in helper array must be cleared.
		// If e.g. level 2 <ol> was added, then level 3 nd lower <ol>s are no longer needed because they belong to upper toc nodes.
		clearLowerLevels( tocLastLevels, hLevel );
	} );

	return $r.html();
};

function clearLowerLevels( levels, current ) {
	for ( let i = current + 1; i < levels.length; i++ ) {
		levels[ i ] = 0;
	}
}
