<?php
/**
 * Single renderer for preview + frontend (avoids divergence).
 *
 * @package AuraBuilder
 */

declare(strict_types=1);

namespace AuraBuilder\Renderer;

use AuraBuilder\Document\Document;
use AuraBuilder\Document\Node;
use AuraBuilder\Widgets\WidgetRegistry;

final class DocumentRenderer {

	/**
	 * @param array{editor?:bool} $context
	 * @return array{html:string,css:string}
	 */
	public function render( Document $document, array $context = array() ): array {
		$styles = new StyleEngine();
		$bp     = is_array( $document->settings['breakpoints'] ?? null ) ? $document->settings['breakpoints'] : array();
		$styles->set_breakpoints(
			(int) ( $bp['tablet'] ?? 1024 ),
			(int) ( $bp['mobile'] ?? 767 )
		);
		$styles->collect( $document ); // uses document for parent grid context

		$editor = ! empty( $context['editor'] );
		$html   = $this->render_node( $document, $document->root_id, array_merge( $context, array( 'editor' => $editor ) ), $styles );

		$wrap_class = 'ab-document' . ( $editor ? ' ab-document--editor' : ' ab-document--front' );
		// Expose theme-friendly classes so global styles can target content.
		$wrap_class .= ' ab-theme-surface';
		$html       = '<div class="' . esc_attr( $wrap_class ) . '" data-doc="' . esc_attr( $document->id ) . '">' . $html . '</div>';

		$base_css = $styles->css();
		// Google Fonts: do NOT use esc_url() — it turns & into &#038; and breaks
		// the entire inline stylesheet (browsers stop parsing after bad @import).
		// Prefer a separate <link> enqueue; skip @import here to keep doc CSS valid.
		// (Fonts still load via ThemeStyles / editor when configured.)
		// Site background from document settings (Site Settings panel).
		$base_css .= $this->site_background_css( $document->settings );
		return array(
			'html' => $html,
			'css'  => $base_css,
		);
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	private function site_background_css( array $settings ): string {
		$mode = sanitize_key( (string) ( $settings['site_bg_mode'] ?? 'color' ) );
		$css  = '';
		$site_bg = isset( $settings['site_bg'] ) ? trim( (string) $settings['site_bg'] ) : '';
		if ( $site_bg !== '' && preg_match( '/^(#([0-9a-fA-F]{3,8})|rgba?\([^)]+\)|hsla?\([^)]+\)|var\([^)]+\))$/', $site_bg ) ) {
			$css .= '.ab-content-wrap,.ab-document--front,.ab-page-body{background-color:' . $site_bg . ' !important;}';
		}
		$size = trim( (string) ( $settings['site_bg_size'] ?? 'cover' ) );
		$pos  = sanitize_text_field( (string) ( $settings['site_bg_position'] ?? 'center center' ) );
		$rep  = sanitize_key( (string) ( $settings['site_bg_repeat'] ?? 'no-repeat' ) );
		if ( ! in_array( $rep, array( 'no-repeat', 'repeat', 'repeat-x', 'repeat-y' ), true ) ) {
			$rep = 'no-repeat';
		}
		// Sanitize size: keyword or percent pattern.
		if ( ! in_array( $size, array( 'cover', 'contain', 'auto', '100% 100%' ), true )
			&& ! preg_match( '/^[\d.]+%\s*([\d.]+%|auto)?$/', $size ) ) {
			$size = 'cover';
		}

		if ( $mode === 'image' ) {
			$img = esc_url( (string) ( $settings['site_bg_image'] ?? '' ) );
			if ( $img !== '' ) {
				$css .= '.ab-content-wrap,.ab-document--front,.ab-page-body{'
					. 'background-image:url("' . $img . '") !important;'
					. 'background-size:' . esc_attr( $size ) . ' !important;'
					. 'background-position:' . esc_attr( $pos ) . ' !important;'
					. 'background-repeat:' . esc_attr( $rep ) . ' !important;'
					. 'background-attachment:scroll;}';
			}
		}
		// Carousel markup is injected separately; base layer still gets color.
		return $css;
	}

	/**
	 * @param array<string, mixed> $context
	 */
	private function render_node( Document $doc, string $node_id, array $context, StyleEngine $styles ): string {
		$node = $doc->get_node( $node_id );
		if ( ! $node || $node->hidden ) {
			return '';
		}

		$registry = WidgetRegistry::instance();
		$widget   = $registry->get( $node->type );
		$class    = $styles->class_for( $node );
		$fx       = $styles->fx_classes( $node );
		$classes  = array_merge( array( 'ab-node', 'ab-type-' . $node->type, $class ), $fx, $node->classes );
		// Fixed height/width flags so front CSS can fill media into the box.
		$desk_st = is_array( $node->styles['desktop'] ?? null ) ? $node->styles['desktop'] : array();
		$h_flag  = trim( (string) ( $desk_st['height'] ?? $desk_st['min_height'] ?? '' ) );
		if ( $h_flag !== '' && $h_flag !== 'auto' ) {
			$classes[] = 'ab-has-h';
		}
		$w_flag = trim( (string) ( $desk_st['width'] ?? '' ) );
		if ( $w_flag !== '' && $w_flag !== 'auto' ) {
			$classes[] = 'ab-has-w';
		}
		// Section layout helper classes (CSS row/grid leaf rules).
		if ( $node->type === 'section' ) {
			$dir = (string) ( $node->settings['direction'] ?? 'grid' );
			if ( $dir === 'row' ) {
				$classes[] = 'ab-section--row';
			} elseif ( $dir === 'grid' ) {
				$classes[] = 'ab-section--grid';
			} else {
				$classes[] = 'ab-section--column';
			}
		}
		// Container row/column class (buttons side-by-side in a horizontal container).
		if ( $node->type === 'container' ) {
			$cdir = (string) ( $node->settings['direction'] ?? 'column' );
			$classes[] = $cdir === 'row' ? 'ab-container--row' : 'ab-container--column';
		}
		// Root width mode classes (beat theme .entry-content > * max-width).
		if ( $node->type === 'root' ) {
			$cw = (string) ( $node->settings['content_width'] ?? 'full' );
			if ( $cw === 'wide' ) {
				$classes[] = 'ab-root--wide';
			} elseif ( $cw === 'content' ) {
				$classes[] = 'ab-root--content';
			} else {
				$classes[] = 'ab-root--full';
			}
		}
		$editor   = ! empty( $context['editor'] );

		$attr = ' class="' . esc_attr( implode( ' ', array_unique( $classes ) ) ) . '" data-node-id="' . esc_attr( $node->id ) . '" data-type="' . esc_attr( $node->type ) . '"';
		if ( $editor ) {
			$attr .= ' data-ab-editable="1"';
		}
		if ( $fx ) {
			$attr .= ' data-ab-fx="1"';
		}
		$fx_style = $styles->fx_inline_style( $node );
		$extra_style = '';
		if ( $node->type === 'root' ) {
			$cw  = (string) ( $node->settings['content_width'] ?? 'full' );
			$max = (string) ( $node->settings['max_width'] ?? '1200px' );
			if ( $max !== '' && ! preg_match( '/(%|px|rem|em|vw|vh|ch)$/i', $max ) && is_numeric( $max ) ) {
				$max .= 'px';
			}
			if ( $cw === 'wide' && $max !== '' ) {
				$extra_style .= '--ab-root-max:' . $max . ';';
			}
		}
		$combined_style = trim( $fx_style . ( $fx_style !== '' && $extra_style !== '' ? ';' : '' ) . $extra_style );
		if ( $combined_style !== '' ) {
			$attr .= ' style="' . esc_attr( $combined_style ) . '"';
		}

		// Containers wrap children.
		if ( $widget && $widget->is_container() ) {
			$inner = '';
			foreach ( $node->children as $child_id ) {
				$inner .= $this->render_node( $doc, $child_id, $context, $styles );
			}
			if ( $editor && $inner === '' ) {
				$inner = '<div class="ab-drop-empty" data-parent="' . esc_attr( $node->id ) . '">+ Adicionar elemento</div>';
			}
			$tag = $node->type === 'section' ? 'section' : 'div';
			return '<' . $tag . $attr . '>' . $inner . '</' . $tag . '>';
		}

		// Leaf widgets.
		$content = $widget ? $widget->render( $node, $context ) : '';
		return '<div' . $attr . '>' . $content . '</div>';
	}
}
