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

/**
 * Transforms XML tree structure to Pug mixin format.
 * This module converts parsed XML nodes into Pug template syntax with proper indentation and attribute handling.
 */

'use strict';

const { TreeWalkActions, walkXMLTree } = require( './parser/walk-xml-tree' );

/**
 * Transforms an XML tree structure into Pug mixin syntax.
 *
 * @param tree - The XML tree object to transform
 * @param nesting - Base nesting level for indentation
 * @param options - Configuration options
 * @param options.nestingCharacter - Character used for indentation (default: '\t')
 * @returns The transformed Pug mixin string
 */
function transformXMLTreeToPug( tree, nesting, options = {} ) {
	const {
		nestingCharacter = '\t',
		parseText = str => str,
		fallbackGlobalRequire
	} = options;

	let hasMissingImports = false;

	// Walk through the XML tree and transform each node
	const requireTags = new Set( [] );
	const result = walkXMLTree( tree, {
		leave( node, depth, parents ) {
			// Calculate indentation based on nesting level and current depth
			const indent = nestingCharacter.repeat( nesting + depth );

			switch ( node.type ) {
				case 'text': {
					const parent = parents[ parents.length - 1 ];
					let mappedContent = node.content;

					if ( parent && parent.type === 'element' ) {
						mappedContent = parseText( parent, mappedContent );
					}

					if ( !mappedContent ) {
						return TreeWalkActions.remove();
					}

					return TreeWalkActions.replace( `${ indent }| ${ mappedContent }\n` );
				}

				case 'element': {
					if ( !node.patternElement?.requires?.length ) {
						hasMissingImports = true;
					} else if ( !hasMissingImports ) {
						for ( const requirePath of node.patternElement.requires ) {
							requireTags.add( requirePath );
						}
					}

					const mappedAttributes = (
						node.patternElement?.attributesMapper ?
							node.patternElement.attributesMapper( node ) :
							node.attributes
					);

					return TreeWalkActions.replace( [
						`${ indent }+${ node.patternElement.mixinName || node.name }(${ serializeMixinAttributes( mappedAttributes ) })`,
						...( node.children || [] )
					].join( '\n' ) );
				}
			}
		}
	} );

	// There is no need to duplicate requires if the fallback global require is already included.
	if ( hasMissingImports ) {
		requireTags.clear();
		requireTags.add( fallbackGlobalRequire );
	}

	return [
		...Array.from( requireTags ).map( requirePath => `include ${ requirePath }` ),
		result
	].join( '\n' );
}

/**
 * Serializes XML attributes into JSON format for Pug mixin parameters.
 * Normalizes attribute names and values according to Pug conventions.
 *
 * @param attributes - Object containing XML attributes
 * @returns JSON string representation of normalized attributes
 */
function serializeMixinAttributes( attributes ) {
	const entries = Object.fromEntries(
		Object
			.entries( attributes )
			.map( ( [ key, value ] ) => {
				const normalizedKey = normalizeMixinAttributeName( key );
				const normalizedValue = normalizeMixinAttributeValue( value );

				return [ normalizedKey, normalizedValue ];
			} )
	);

	return JSON.stringify( entries );
}

/**
 * Normalizes XML attribute values to appropriate JavaScript types.
 * Converts string representations of booleans to actual boolean values.
 *
 * @param value - The XML attribute value to normalize
 * @returns Normalized value (boolean for 'true'/'false', string otherwise)
 */
function normalizeMixinAttributeValue( value ) {
	if ( value === 'true' || value === '' ) {
		return true;
	}

	if ( value === 'false' ) {
		return false;
	}

	return value;
}

/**
 * Normalizes attribute names from kebab-case to camelCase.
 * e.g., data-foo-bar -> dataFooBar
 *
 * @param name - The kebab-case attribute name
 * @returns The camelCase attribute name
 */
function normalizeMixinAttributeName( name ) {
	return name.replace( /-([a-z])/g, g => g[ 1 ].toUpperCase() );
}

module.exports = transformXMLTreeToPug;
