<?php
/**
 * Frontend page template.
 *
 * Renders builder pages in a clean, full-width layout
 * without theme header/footer chrome.
 *
 * @package Antimanual_Builder
 * @since   1.0.0
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

$post_id       = get_the_ID();
$rendered_html = get_post_meta( $post_id, '_ambld_rendered_html', true );
$page_settings = json_decode( (string) get_post_meta( $post_id, '_ambld_page_settings', true ), true );
$header_template = AMBLD_Render_Template_Resolver::resolve( $post_id, 'header' );
$footer_template = AMBLD_Render_Template_Resolver::resolve( $post_id, 'footer' );

// Imported pages store their complete HTML (including inline <script> and <style>)
// in _ambld_import_document and rely on those tags being present to render correctly
// (scroll animations, interactive components, etc.). Determine this flag early so
// it can be used when processing head/footer markup below.
$is_imported_page = ! empty( get_post_meta( $post_id, '_ambld_import_document', true ) );

$imported_head_markup = '';
$imported_footer_markup = '';

// Allowlist for imported head markup. <script> and <style> are intentionally
// excluded — the plugin no longer permits arbitrary code insertion via the
// import surface, per the WordPress.org plugin guidelines.
$ambld_head_allowed_tags = array(
	'link'  => array(
		'rel'         => true,
		'href'        => true,
		'type'        => true,
		'media'       => true,
		'sizes'       => true,
		'as'          => true,
		'crossorigin' => true,
		'integrity'   => true,
		'hreflang'    => true,
	),
	'meta'  => array(
		'name'       => true,
		'content'    => true,
		'http-equiv' => true,
		'charset'    => true,
		'property'   => true,
	),
	'title' => array(),
	'base'  => array(
		'href'   => true,
		'target' => true,
	),
);

// Allowlist for imported footer markup: post-grade HTML, no scripts.
$ambld_footer_allowed_tags = wp_kses_allowed_html( 'post' );

$ambld_rendered_allowed_tags = require __DIR__ . '/rendered-allowed-tags.php';

if ( is_array( $page_settings ) ) {
	if ( ! empty( $page_settings['importedHeadMarkup'] ) ) {
		// Preserve scripts for imported pages — they were saved with the
		// unfiltered_html capability check (see save_page_meta / sanitize_builder_page_settings_array).
		// Strip scripts only for non-imported pages as a defense-in-depth pass.
		$imported_head_markup = $is_imported_page
			? (string) $page_settings['importedHeadMarkup']
			: preg_replace( '/<script\b[^>]*>[\s\S]*?<\/script>/i', '', (string) $page_settings['importedHeadMarkup'] );
	}
	if ( ! empty( $page_settings['importedFooterMarkup'] ) ) {
		$imported_footer_markup = $is_imported_page
			? (string) $page_settings['importedFooterMarkup']
			: preg_replace( '/<script\b[^>]*>[\s\S]*?<\/script>/i', '', (string) $page_settings['importedFooterMarkup'] );
	}
}
$imported_html_attributes = is_array( $page_settings ) && ! empty( $page_settings['importedHtmlAttributes'] ) && is_array( $page_settings['importedHtmlAttributes'] )
	? $page_settings['importedHtmlAttributes']
	: array();
$imported_body_attributes = is_array( $page_settings ) && ! empty( $page_settings['importedBodyAttributes'] ) && is_array( $page_settings['importedBodyAttributes'] )
	? $page_settings['importedBodyAttributes']
	: array();

$render_attribute_string = static function( $attributes, $excluded_names = array() ) {
	if ( ! is_array( $attributes ) ) {
		return '';
	}

	$attribute_pairs = array();

	foreach ( $attributes as $name => $value ) {
		$name = sanitize_key( (string) $name );

		// Never emit event-handler attributes (onclick, onload, onerror, …).
		// sanitize_key() preserves "onload" and esc_attr() escapes only the
		// VALUE, so without this guard an imported `<body onload="…">` would be
		// re-emitted as a live handler for every visitor (stored XSS).
		if ( '' === $name || 0 === strpos( $name, 'on' ) || in_array( $name, $excluded_names, true ) || ! is_string( $value ) || '' === trim( $value ) ) {
			continue;
		}

		$attribute_pairs[] = sprintf(
			'%s="%s"',
			esc_attr( $name ),
			esc_attr( $value )
		);
	}

	return implode( ' ', $attribute_pairs );
};

$imported_body_classes = array();
if ( ! empty( $imported_body_attributes['class'] ) && is_string( $imported_body_attributes['class'] ) ) {
	$imported_body_classes = preg_split( '/\s+/', trim( $imported_body_attributes['class'] ) );
	$imported_body_classes = array_values( array_filter( array_map( 'sanitize_html_class', $imported_body_classes ) ) );
}

// Tag the body for the Full-Width preset so generated CSS can target
// `body.is-full-width` for true full-bleed layouts.
$page_template_mode = is_array( $page_settings ) && isset( $page_settings['pageTemplate'] )
	? (string) $page_settings['pageTemplate']
	: '';
if ( 'full-width' === $page_template_mode ) {
	$imported_body_classes[] = 'is-full-width';
}

$html_attribute_string = $render_attribute_string( $imported_html_attributes, array( 'lang', 'dir' ) );
$body_attribute_string = $render_attribute_string( $imported_body_attributes, array( 'class' ) );

// Shared render pipeline: editor-artifact strip → inline-code strip →
// kses (BEFORE resolution, so form-plugin <script> output survives) →
// shortcode markers → dynamic regions.
$rendered_html = AMBLD_Render_Pipeline::prepare( $rendered_html, $post_id, 'canvas' );
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?><?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Attribute string is assembled from sanitized names and escaped values. ?><?php echo $html_attribute_string ? ' ' . $html_attribute_string : ''; ?>>
<head>
	<meta charset="<?php bloginfo( 'charset' ); ?>">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title><?php wp_title( '|', true, 'right' ); ?><?php bloginfo( 'name' ); ?></title>
	<?php
	if ( ! empty( $imported_head_markup ) ) {
		// Arbitrary <script>/<style> insertion is not permitted —
		// run all imported head markup through wp_kses() with the
		// script-free allowlist.
		echo wp_kses( $imported_head_markup, $ambld_head_allowed_tags );
	}
	?>
	<?php wp_head(); ?>
</head>
<body <?php body_class( array_merge( array( 'ambld-page', 'ambld-frontend' ), $imported_body_classes ) ); ?><?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Attribute string is assembled from sanitized names and escaped values. ?><?php echo $body_attribute_string ? ' ' . $body_attribute_string : ''; ?>>
	<?php wp_body_open(); ?>
	<?php if ( is_array( $header_template ) && ! empty( $header_template['main_html'] ) ) : ?>
		<header class="ambld-template ambld-template--header" data-amb-template-id="<?php echo esc_attr( absint( $header_template['id'] ) ); ?>">
			<?php echo wp_kses( $header_template['main_html'], $ambld_rendered_allowed_tags ); ?>
		</header>
	<?php endif; ?>

	<?php
	// Imported pages store a full-body fragment (with their own <header>, <main>,
	// <footer>) as renderedHtml. Wrapping that in another <main> creates nested
	// <main> elements and misplaces the page's structural chrome inside the
	// landmark. Use a neutral <div> for imported pages instead.
	$ambld_content_tag = $is_imported_page ? 'div' : 'main';
	?>
	<<?php echo esc_attr( $ambld_content_tag ); ?> class="ambld-main" id="ambld-content">
		<?php
		if ( ! empty( $rendered_html ) ) {
			// Already kses-filtered inside AMBLD_Render_Pipeline::prepare()
			// — the allowlist pass runs BEFORE shortcode resolution so
			// resolved plugin output (form scripts etc.) isn't stripped.
			// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			echo $rendered_html;
		} else {
			// Fall back to block rendering — same pipeline.
			$plugin = AMBLD_Antimanual_Builder::get_instance();
			if ( $plugin->get_renderer() ) {
				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				echo AMBLD_Render_Pipeline::prepare( $plugin->get_renderer()->render_page( $post_id ), $post_id, 'canvas' );
			}
		}
		?>
	</<?php echo esc_attr( $ambld_content_tag ); ?>>

	<?php
	if ( ! empty( $imported_footer_markup ) ) {
		echo wp_kses( $imported_footer_markup, $ambld_footer_allowed_tags );
	}
	?>
	<?php if ( is_array( $footer_template ) && ! empty( $footer_template['main_html'] ) ) : ?>
		<footer class="ambld-template ambld-template--footer" data-amb-template-id="<?php echo esc_attr( absint( $footer_template['id'] ) ); ?>">
			<?php echo wp_kses( $footer_template['main_html'], $ambld_rendered_allowed_tags ); ?>
		</footer>
	<?php endif; ?>

	<?php wp_footer(); ?>
</body>
</html>
