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

/**
 * This script converts XML-like custom component syntax in Markdown files to Pug templates.
 * It allows writers to use an intuitive HTML-like syntax in documentation while leveraging
 * the power of Pug mixins for rendering complex UI components.
 *
 * The process works in two steps:
 * 1. Before markdown rendering: Components are identified, processed, and replaced with UUID markers
 * 2. After markdown rendering: UUID markers are replaced with the fully rendered components
 *
 * This two-phase approach is necessary because Markdown processors often misinterpret HTML
 * indentation and nested structures, leading to unwanted paragraph breaks, incorrect
 * formatting, or stripped content. By temporarily replacing components with simple markers,
 * we avoid Markdown interference with our component HTML structure.
 *
 * For example:
 *   <ck:badge variant="premium" /> becomes +badge({ variant: 'premium' })
 *   <ck:button label="Click me" icon="github" /> becomes +button({ label: 'Click me', icon: 'github' })
 *
 * It allows binding slots to components, so that the content of a component can be passed as a slot.
 *
 * For example:
 *   <ck:tabs>
 *     <ck:tab id="desktop" label="Desktop" active="true">
 *       TAB
 *     </ck:tab>
 *     <ck:tab id="mobile" label="Mobile">
 *       Use the **main menu button in the upper-left corner** to navigate through the documentation.
 *     </ck:tab>
 *   </ck:tabs>
 * becomes:
 *   +tabs()
 *     +tab({ id: 'desktop', label: 'Desktop', active: true })
 *       | TAB
 *     +tab({ id: 'mobile', label: 'Mobile' })
 *       | Use the <strong>main menu button in the upper-left corner</strong> to navigate through the documentation.
 */

'use strict';

const path = require( 'path' );
const removeIndentation = require( '../../../utils/remove-indentation' );
const createPrerenderPugTemplate = require( '../../../utils/pug-renderer/create-prerender-pug-template' );
const {
	renderXMLPugComponentsInMarkdown,
	extractReplacementBase64Tag
} = require( '../../../utils/pug-to-xml-binding/render-xml-pug-components-in-markdown' );

const PATTERN_ELEMENTS = [
	// Info boxes
	{
		pattern: /^(?:ck:)?info-box$/,
		mixinName: 'callout',
		allowMarkdownContent: true,
		requires: [
			'_components/svg/index',
			'_components/icon/index',
			'_components/icon-message/index',
			'_components/callout/index'
		],
		attributesMapper: ( { attributes } ) => {
			const variants = [
				'info', 'warning', 'error', 'success',
				'note', 'tip', 'important', 'caution'
			];

			// First check if variant is directly specified
			if ( attributes.variant && variants.includes( attributes.variant ) ) {
				return {
					variant: attributes.variant
				};
			}

			// Otherwise check if any variant name is used as an attribute
			const foundVariant = variants.find( v => v in attributes );

			return {
				variant: foundVariant || 'info'
			};
		}
	},

	// Tabs
	{
		pattern: 'ck:tabs',
		mixinName: 'tabs'
	},
	{
		pattern: 'ck:tab',
		mixinName: 'tab',
		allowMarkdownContent: true
	},

	// Code tabs
	{
		pattern: 'ck:code-switcher-tabs',
		mixinName: 'code-switcher'
	},

	// Banner
	{
		pattern: 'ck:banner',
		mixinName: 'banner',
		allowMarkdownContent: true
	},

	// Icons
	{
		pattern: 'ck:svg',
		mixinName: 'inline-svg',
		allowMarkdownContent: false,
		requires: [
			'_components/svg/index'
		]
	},
	{
		pattern: 'ck:spritesheet-svg',
		mixinName: 'spritesheet-local-svg',
		allowMarkdownContent: false,
		requires: [
			'_components/svg/index'
		]
	},
	{
		pattern: 'ck:preload-spritesheet-svg',
		mixinName: 'preload-spritesheet-svg',
		allowMarkdownContent: false,
		requires: [
			'_components/svg/index'
		]
	},
	{
		pattern: 'ck:preload-svg-spritesheet-icon',
		mixinName: 'preload-svg-spritesheet-icon',
		allowMarkdownContent: false,
		requires: [
			'_components/svg/index',
			'_components/icon/index'
		]
	},

	// Code block
	{
		pattern: 'ck:code-block',
		attributesMapper: node => {
			if ( node.attributes.code ) {
				return node.attributes;
			}

			const content = removeIndentation(
				node.children
					.join( '\n' ).trim()
					.replace( /^\|[\s]+/, '' )
			);

			node.children = [];

			return {
				...node.attributes,
				code: content || null
			};
		},

		// Custom renderer is used to optimize rendering of the code block component.
		// There is hundreds of code blocks in the documentation, and we want to avoid
		// rendering them for each page.
		renderer: ( () => {
			const template = createPrerenderPugTemplate( {
				hexo,
				requires: [
					'_components/svg/index',
					'_components/icon/index',
					'_components/tooltip-popover/index',
					'_components/skeleton/index',
					'_components/code-block/index'
				],
				component: 'code-block',
				skipCacheIf: ( { code } ) => code?.split( '\n' ).length > 1,
				componentAttrs: {
					static: {
						copyable: true
					},
					dynamic: [ 'copyable', 'language' ],
					mask: [ 'code' ]
				}
			} );

			return ( { attributes } ) => template( attributes );
		} )()
	},
	{
		pattern: /^(?:ck:)?code-switcher$/,
		mixinName: 'cke5-code-switcher',
		allowMarkdownContent: false,
		requires: [
			'_components/svg/index',
			'_components/icon/index',
			'_components/tooltip-popover/index',
			'_components/skeleton/index',
			'_components/code-block/index',
			'_components/tabs/index',
			'_components/code-switcher/index',
			'_components/cke5-code-switcher/index'
		],

		// Transform content into attribute, as pug does't play well with preformatted code.
		attributesMapper: node => {
			const content = removeIndentation(
				node.children
					.join( '\n' ).trim()
					.replace( /^\|[\s]+/, '' )
			);

			node.children = [];

			return {
				...node.attributes,
				code: content || null
			};
		}
	},

	// Card
	{
		pattern: /^(?:ck:)?card$/,
		mixinName: 'card',
		allowMarkdownContent: true,
		requires: [
			'_components/svg/index',
			'_components/icon/index',
			'_components/card/index'
		]
	},
	{
		pattern: 'ck:card-description',
		mixinName: 'card-description',
		allowMarkdownContent: true
	},
	{
		pattern: 'ck:card-footer',
		mixinName: 'card-footer',
		allowMarkdownContent: true
	},
	{
		pattern: 'ck:card-learn-more',
		mixinName: 'card-learn-more',
		allowMarkdownContent: true
	},

	{
		pattern: 'ck:columns',
		mixinName: 'columns',
		allowMarkdownContent: true,
		requires: [
			'_components/columns/index'
		]
	},

	// Heading badge
	{
		pattern: 'ck:heading-badge',
		mixinName: 'heading-badge',
		allowMarkdownContent: false,
		requires: [
			'_components/heading-badge/index',
			'_components/badge/index',
			'_components/svg/index',
			'_components/icon/index',
			'_components/tooltip-popover/index'
		]
	},
	{
		pattern: 'ck:badge',
		mixinName: 'badge',
		allowMarkdownContent: false,
		requires: [
			'_components/svg/index',
			'_components/icon/index',
			'_components/tooltip-popover/index',
			'_components/badge/index'
		]
	},

	// Dynamic match pattern
	{
		pattern: /ck:[\w-]+/,
		map: node => {
			const [ , mixinName ] = node.name.split( ':' );

			return {
				...node,
				patternElement: {
					mixinName,
					allowMarkdownContent: node.attributes.markdown !== undefined
				}
			};
		}
	}
];

hexo.extend.filter.register( 'before_post_render', page => {
	if ( page.projectTheme !== 'gloria' || !page.content ) {
		return page;
	}

	const basePath = path.join( hexo.theme_dir, 'layout', 'gloria', '_components' );
	const result = renderXMLPugComponentsInMarkdown( page.content, {
		hexo,
		basePath,
		patternElements: PATTERN_ELEMENTS,
		source: page.source,
		mixinContextVars: {
			page
		}
	} );

	page.content = result;

	return page;
}, 8 );

hexo.extend.filter.register( 'after_post_render', page => {
	if ( page.projectTheme !== 'gloria' || !page.content ) {
		return page;
	}

	try {
		page.content = extractReplacementBase64Tag( { page } );
	} catch ( error ) {
		console.error( error );
		throw error;
	}

	return page;
} );
