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

'use strict';

const upath = require( 'upath' );
const fs = require( 'fs' );

const EXEC_REGEXP = /\\?{@exec ([^}]+)\\?}/g;
const PATH_REGEXP = /[\w.\\/-]+\.c?js$/;

/**
 * Replaces the `{@exec path/to/function.js}` expression with the module results.
 *
 * The "path/to/function.js" path must be a relative path to the location where the project configuration (`umberto.json`) is saved.
 *
 * The callback must return a value that will replace the `{@exec ...}` expression.
 * It will be processed by Hexo when building the documentation.
 *
 * Asynchronous functions are not supported by Hexo, so the callback must be synchronous.
 *
 * @param {Object} page
 * @param {Object} hexo
 * @returns {Object}
 */
module.exports = function executeAndInsertFunctionResults( page, hexo ) {
	page.content = page.content.replace( EXEC_REGEXP, ( match, path ) => {
		if ( !page.projectName ) {
			console.error( `"${ match }" expression cannot be executed on a page that is assigned to no project.` );
			return match;
		}

		const projectConfig = hexo.projectGlobals[ page.projectName ].config;

		if ( match.startsWith( '\\{' ) ) {
			return match.replace( /^\\{/, '{' ).replace( /\\}$/, '}' );
		}

		const pathMatch = path.match( PATH_REGEXP );

		if ( !pathMatch || pathMatch[ 0 ] !== path ) {
			console.error( `"${ path }" is not a valid path!` );
			return match;
		}

		const callbackAbsolutePath = upath.join( upath.dirname( projectConfig.__configPath ), path );

		if ( !fs.existsSync( callbackAbsolutePath ) ) {
			console.error( `Path "${ path }" does not lead to any file.` );
			return match;
		}

		try {
			const callback = require( callbackAbsolutePath );
			const callbackResults = callback();

			if ( !callbackResults ) {
				console.error( `Module located in "${ path }" did not return any value.` );
				return match;
			}

			if ( callbackResults instanceof Promise ) {
				console.error( `Module located in "${ path }" returned a promise.` );
				return match;
			}

			return callbackResults;
		} catch ( e ) {
			console.error( `Module located in "${ path }" threw an error: ${ e.message }` );
			return match;
		}
	} );

	return page;
};
