<?php
/**
 * Builds native Elementor page structures from a public reference URL.
 *
 * @package Niwa_AI
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Niwa_AI_Elementor_URL_Blueprint_Builder {
	const MAX_SECTIONS = 18;
	const MAX_CARDS    = 8;
	const MAX_BUTTONS  = 3;

	private $openai_client;
	private $auth_manager;
	private $model_router;

	public function __construct( $openai_client = null, $auth_manager = null, $model_router = null ) {
		$this->openai_client = $openai_client;
		$this->auth_manager  = $auth_manager;
		$this->model_router  = $model_router;
	}

	public function build_elements_from_url( $url, $instructions = '' ) {
		$result = $this->fetch_blueprint( $url, $instructions );
		if ( empty( $result['success'] ) ) {
			return $result;
		}

		$blueprint = isset( $result['blueprint'] ) && is_array( $result['blueprint'] ) ? $result['blueprint'] : array();
		$elements  = $this->build_elements_from_blueprint( $blueprint );
		if ( empty( $elements ) ) {
			return array(
				'success' => false,
				'error'   => 'Reference URL did not produce any Elementor sections.',
			);
		}

		return array(
			'success'      => true,
			'blueprint'    => $blueprint,
			'elements'     => $elements,
			'post_content' => $this->render_post_content( $blueprint ),
			'llm_used'     => ! empty( $result['llm_used'] ),
			'llm_error'    => isset( $result['llm_error'] ) ? (string) $result['llm_error'] : '',
		);
	}

	public function fetch_blueprint( $url, $instructions = '' ) {
		$url = $this->validate_public_url( $url );
		if ( is_wp_error( $url ) ) {
			return array( 'success' => false, 'error' => $url->get_error_message() );
		}

		if ( class_exists( 'Niwa_AI_Elementor_Reference_Blueprints' ) ) {
			$known_blueprint = Niwa_AI_Elementor_Reference_Blueprints::for_url( $url, $instructions );
			if ( is_array( $known_blueprint ) && ! empty( $known_blueprint['sections'] ) ) {
				$known_blueprint = $this->normalize_blueprint( $known_blueprint, $url, $instructions );
				return array(
					'success'   => true,
					'blueprint' => $known_blueprint,
					'llm_used'  => false,
					'llm_error' => '',
				);
			}
		}

		$response = Niwa_AI_Safe_HTTP::get(
			$url,
			array(
				'timeout'     => 25,
				'redirection' => 4,
				'user-agent'  => 'NiwaAI Elementor Reference Builder/1.0; ' . home_url( '/' ),
				'headers'     => array(
					'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
				),
			),
			2 * 1024 * 1024
		);

		if ( is_wp_error( $response ) ) {
			return array( 'success' => false, 'error' => $response->get_error_message() );
		}

		$code = intval( wp_remote_retrieve_response_code( $response ) );
		if ( $code < 200 || $code >= 400 ) {
			return array( 'success' => false, 'error' => 'Reference URL returned HTTP ' . $code . '.' );
		}

		$html = (string) wp_remote_retrieve_body( $response );
		if ( strlen( $html ) < 200 ) {
			return array( 'success' => false, 'error' => 'Reference URL returned an empty or too-small HTML document.' );
		}

		$blueprint = $this->extract_blueprint_from_html( $html, $url, $instructions );
		$blueprint = $this->maybe_refine_blueprint_with_llm( $blueprint, $instructions );
		$blueprint = $this->normalize_blueprint( $blueprint, $url, $instructions );

		return array(
			'success'   => true,
			'blueprint' => $blueprint,
			'llm_used'  => ! empty( $blueprint['llm_used'] ),
			'llm_error' => isset( $blueprint['llm_error'] ) ? (string) $blueprint['llm_error'] : '',
		);
	}

	public function build_elements_from_blueprint( $blueprint ) {
		$blueprint = $this->normalize_blueprint( is_array( $blueprint ) ? $blueprint : array(), '', '' );
		if ( 'angie_ai_product' === $blueprint['style_profile'] && class_exists( 'Niwa_AI_Elementor_Angie_Reference_Page_Renderer' ) ) {
			$renderer = new Niwa_AI_Elementor_Angie_Reference_Page_Renderer();
			return $renderer->build( $blueprint );
		}

		$sections  = isset( $blueprint['sections'] ) && is_array( $blueprint['sections'] ) ? $blueprint['sections'] : array();
		$elements  = array();

		foreach ( array_values( $sections ) as $index => $section ) {
			$element = $this->build_section( $section, $index );
			if ( ! empty( $element ) ) {
				$elements[] = $element;
			}
		}

		return $elements;
	}

	private function validate_public_url( $url ) {
		return Niwa_AI_Safe_HTTP::validate_url( esc_url_raw( trim( (string) $url ) ) );
	}

	private function extract_blueprint_from_html( $html, $url, $instructions ) {
		$blueprint = array(
			'source_url'   => $url,
			'title'        => '',
			'description'  => '',
			'instructions' => $this->trim_text( $instructions, 500 ),
			'sections'     => array(),
			'llm_used'     => false,
			'llm_error'    => '',
		);

		if ( ! class_exists( 'DOMDocument' ) || ! class_exists( 'DOMXPath' ) ) {
			return $this->extract_blueprint_with_regex( $html, $blueprint );
		}

		$previous = libxml_use_internal_errors( true );
		$doc      = new DOMDocument();
		$loaded   = $doc->loadHTML( '<?xml encoding="utf-8" ?>' . $html );
		libxml_clear_errors();
		libxml_use_internal_errors( $previous );

		if ( ! $loaded ) {
			return $this->extract_blueprint_with_regex( $html, $blueprint );
		}

		$xpath = new DOMXPath( $doc );
		$this->remove_nodes( $xpath, '//script|//style|//noscript|//svg|//iframe|//canvas|//form|//header|//nav|//footer' );

		$title_node = $xpath->query( '//title' )->item( 0 );
		if ( $title_node ) {
			$blueprint['title'] = $this->trim_text( $title_node->textContent, 120 );
		}

		$description_node = $xpath->query( '//meta[translate(@name,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")="description"]/@content' )->item( 0 );
		if ( $description_node ) {
			$blueprint['description'] = $this->trim_text( $description_node->nodeValue, 220 );
		}

		$root = $xpath->query( '//main' )->item( 0 );
		if ( ! $root ) {
			$root = $xpath->query( '//article' )->item( 0 );
		}
		if ( ! $root ) {
			$root = $xpath->query( '//body' )->item( 0 );
		}
		if ( ! $root ) {
			return $this->extract_blueprint_with_regex( $html, $blueprint );
		}

		$query   = './/*[self::h1 or self::h2 or self::h3 or self::h4 or self::p or self::li or self::a or self::img or (self::span and contains(concat(" ", normalize-space(@class), " "), " dsm-tab-title ")) or (self::div and contains(concat(" ", normalize-space(@class), " "), " elementor-heading-title "))]';
		$nodes   = $xpath->query( $query, $root );
		$current = null;

		foreach ( $nodes as $node ) {
			$tag  = strtolower( $node->nodeName );
			$text = 'img' === $tag ? '' : $this->trim_text( $node->textContent, 700 );
			$class = method_exists( $node, 'getAttribute' ) ? (string) $node->getAttribute( 'class' ) : '';
			$is_inline_title = ( 'span' === $tag && false !== strpos( ' ' . $class . ' ', ' dsm-tab-title ' ) ) || ( 'div' === $tag && false !== strpos( ' ' . $class . ' ', ' elementor-heading-title ' ) );
			if ( in_array( $tag, array( 'h1', 'h2' ), true ) ) {
				if ( '' === $text || $this->is_boilerplate_text( $text ) ) {
					continue;
				}
				$this->push_section( $blueprint['sections'], $current );
				$current = $this->new_section( $text, 'h1' === $tag ? 'hero' : 'content' );
				continue;
			}

			if ( ! is_array( $current ) ) {
				$current = $this->new_section( '' !== $blueprint['title'] ? $blueprint['title'] : 'Reference section', 'hero' );
			}

			if ( in_array( $tag, array( 'h3', 'h4' ), true ) || $is_inline_title ) {
				if ( '' !== $text && ! $this->section_has_card( $current, $text ) && count( $current['cards'] ) < self::MAX_CARDS ) {
					$current['cards'][] = array(
						'title' => $this->trim_text( $text, 120 ),
						'body'  => '',
						'image' => array(),
					);
				}
				continue;
			}

			if ( 'img' === $tag ) {
				$src = $this->image_src_from_node( $node );
				if ( '' !== $src ) {
					$image = array(
						'url' => $this->resolve_url( $src, $url ),
						'alt' => $this->trim_text( $node->getAttribute( 'alt' ), 120 ),
					);
					if ( empty( $current['image']['url'] ) ) {
						$current['image'] = $image;
					} elseif ( ! empty( $current['cards'] ) ) {
						$last = count( $current['cards'] ) - 1;
						if ( $last >= 0 && empty( $current['cards'][ $last ]['image']['url'] ) ) {
							$current['cards'][ $last ]['image'] = $image;
						}
					}
				}
				continue;
			}

			if ( 'a' === $tag ) {
				$href = $node->getAttribute( 'href' );
				if ( '' !== $text && strlen( $text ) <= 70 && '' !== $href && ! $this->section_has_button( $current, $text ) && count( $current['buttons'] ) < self::MAX_BUTTONS ) {
					$current['buttons'][] = array(
						'text' => $this->trim_text( $text, 70 ),
						'url'  => $this->resolve_url( $href, $url ),
					);
				}
				continue;
			}

			if ( '' === $text || $this->is_boilerplate_text( $text ) ) {
				continue;
			}

			$card_index = count( $current['cards'] ) - 1;
			if ( $card_index >= 0 && '' === $current['cards'][ $card_index ]['body'] && strlen( $text ) <= 260 ) {
				$current['cards'][ $card_index ]['body'] = $this->trim_text( $text, 240 );
				continue;
			}

			if ( ! $this->section_body_contains( $current, $text ) ) {
				$current['body'] = trim( $current['body'] . "\n\n" . $this->trim_text( $text, 520 ) );
			}
		}

		$this->push_section( $blueprint['sections'], $current );

		if ( empty( $blueprint['sections'] ) ) {
			$blueprint = $this->extract_blueprint_with_regex( $html, $blueprint );
		}

		return $blueprint;
	}

	private function extract_blueprint_with_regex( $html, $blueprint ) {
		$text = html_entity_decode( wp_strip_all_tags( preg_replace( '#<(script|style|noscript|svg|iframe)[^>]*>.*?</\1>#is', ' ', $html ) ), ENT_QUOTES, 'UTF-8' );
		$text = $this->collapse_text( $text );
		if ( '' === $blueprint['title'] && preg_match( '#<title[^>]*>(.*?)</title>#is', $html, $m ) ) {
			$blueprint['title'] = $this->trim_text( wp_strip_all_tags( $m[1] ), 120 );
		}
		$blueprint['sections'][] = array(
			'type'    => 'hero',
			'heading' => '' !== $blueprint['title'] ? $blueprint['title'] : 'Reference page',
			'body'    => $this->trim_text( $text, 700 ),
			'cards'   => array(),
			'buttons' => array(),
			'image'   => array(),
		);
		return $blueprint;
	}

	private function remove_nodes( $xpath, $query ) {
		$nodes = $xpath->query( $query );
		if ( ! $nodes ) {
			return;
		}
		for ( $i = $nodes->length - 1; $i >= 0; $i-- ) {
			$node = $nodes->item( $i );
			if ( $node && $node->parentNode ) {
				$node->parentNode->removeChild( $node );
			}
		}
	}

	private function maybe_refine_blueprint_with_llm( $blueprint, $instructions ) {
		if ( ! $this->openai_client || ! method_exists( $this->openai_client, 'generate' ) || ! $this->auth_manager ) {
			$blueprint['llm_used']  = false;
			$blueprint['llm_error'] = 'LLM dependencies are not loaded.';
			return $blueprint;
		}

		$auth_mode = method_exists( $this->auth_manager, 'get_mode' ) ? (string) $this->auth_manager->get_mode() : 'api_key';
		$creds     = method_exists( $this->auth_manager, 'get_credentials' ) ? (array) $this->auth_manager->get_credentials() : array();
		$model     = $this->model_router && method_exists( $this->model_router, 'route_for_founder_agent' )
			? (string) $this->model_router->route_for_founder_agent()
			: ( $this->model_router && method_exists( $this->model_router, 'route_for_agent' ) ? (string) $this->model_router->route_for_agent() : 'gpt-5.4-mini' );

		$payload = array(
			'blueprint'            => $blueprint,
			'founder_instructions' => (string) $instructions,
			'contract'             => 'Return only JSON with the same blueprint shape. Improve hierarchy and section types for a native Elementor rebuild. Keep sections/cards/buttons/images editable. Do not return HTML/CSS/JS/PHP. Do not invent claims. Do not add html widgets.',
		);

		$result = $this->openai_client->generate(
			$model,
			wp_json_encode( $payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ),
			'You are Niwa Elementor Page Architect. Convert a fetched webpage outline into a clean section-by-section native Elementor blueprint. Return strict JSON only.',
			$auth_mode,
			$creds,
			75
		);

		if ( empty( $result['success'] ) || empty( $result['answer'] ) ) {
			$blueprint['llm_used']  = false;
			$blueprint['llm_error'] = isset( $result['error'] ) ? (string) $result['error'] : 'LLM returned no blueprint.';
			return $blueprint;
		}

		$parsed = $this->parse_json_answer( (string) $result['answer'] );
		if ( ! is_array( $parsed ) || empty( $parsed['sections'] ) || ! is_array( $parsed['sections'] ) ) {
			$blueprint['llm_used']  = false;
			$blueprint['llm_error'] = 'LLM returned a non-blueprint JSON payload.';
			return $blueprint;
		}

		$parsed['source_url']   = isset( $blueprint['source_url'] ) ? (string) $blueprint['source_url'] : '';
		$parsed['title']        = isset( $parsed['title'] ) && '' !== trim( (string) $parsed['title'] ) ? (string) $parsed['title'] : (string) $blueprint['title'];
		$parsed['description']  = isset( $parsed['description'] ) ? (string) $parsed['description'] : (string) $blueprint['description'];
		$parsed['instructions'] = (string) $instructions;
		$parsed['llm_used']     = true;
		$parsed['llm_error']    = '';

		return $parsed;
	}

	private function parse_json_answer( $answer ) {
		$answer = trim( (string) $answer );
		if ( '' === $answer ) {
			return null;
		}

		if ( preg_match( '/```(?:json)?\s*(.*?)```/is', $answer, $match ) ) {
			$answer = trim( $match[1] );
		}

		$decoded = json_decode( $answer, true );
		if ( is_array( $decoded ) ) {
			return $decoded;
		}

		$start = strpos( $answer, '{' );
		$end   = strrpos( $answer, '}' );
		if ( false !== $start && false !== $end && $end > $start ) {
			$decoded = json_decode( substr( $answer, $start, $end - $start + 1 ), true );
			if ( is_array( $decoded ) ) {
				return $decoded;
			}
		}

		return null;
	}

	private function normalize_blueprint( $blueprint, $url, $instructions ) {
		$blueprint = is_array( $blueprint ) ? $blueprint : array();
		$source    = isset( $blueprint['source_url'] ) && '' !== trim( (string) $blueprint['source_url'] ) ? (string) $blueprint['source_url'] : (string) $url;
		$title     = isset( $blueprint['title'] ) ? $this->trim_text( $blueprint['title'], 120 ) : '';
		$desc      = isset( $blueprint['description'] ) ? $this->trim_text( $blueprint['description'], 220 ) : '';
		$sections  = isset( $blueprint['sections'] ) && is_array( $blueprint['sections'] ) ? $blueprint['sections'] : array();
		$normalized_sections = array();

		foreach ( array_values( $sections ) as $index => $section ) {
			$section = $this->normalize_section( $section, $index );
			if ( '' === $section['heading'] && '' === $section['body'] && empty( $section['cards'] ) ) {
				continue;
			}
			$normalized_sections[] = $section;
			if ( count( $normalized_sections ) >= self::MAX_SECTIONS ) {
				break;
			}
		}

		if ( empty( $normalized_sections ) ) {
			$normalized_sections[] = array(
				'type'    => 'hero',
				'eyebrow' => '',
				'heading' => '' !== $title ? $title : 'Reference page',
				'body'    => $desc,
				'cards'   => array(),
				'buttons' => array(),
				'image'   => array(),
			);
		}

		return array(
			'source_url'   => esc_url_raw( $source ),
			'title'        => $title,
			'description'  => $desc,
			'instructions' => $this->trim_text( '' !== (string) $instructions ? $instructions : ( isset( $blueprint['instructions'] ) ? $blueprint['instructions'] : '' ), 500 ),
			'style_profile' => isset( $blueprint['style_profile'] ) ? sanitize_key( (string) $blueprint['style_profile'] ) : '',
			'specialized_blueprint' => ! empty( $blueprint['specialized_blueprint'] ),
			'sections'     => $normalized_sections,
			'llm_used'     => ! empty( $blueprint['llm_used'] ),
			'llm_error'    => isset( $blueprint['llm_error'] ) ? (string) $blueprint['llm_error'] : '',
		);
	}

	private function normalize_section( $section, $index ) {
		$section = is_array( $section ) ? $section : array();
		$type    = isset( $section['type'] ) ? sanitize_key( (string) $section['type'] ) : '';
		$heading = isset( $section['heading'] ) ? $this->trim_text( $section['heading'], 130 ) : '';
		$body    = isset( $section['body'] ) ? $this->trim_text( $section['body'], 850 ) : '';
		$eyebrow = isset( $section['eyebrow'] ) ? $this->trim_text( $section['eyebrow'], 80 ) : '';

		$cards = array();
		if ( ! empty( $section['cards'] ) && is_array( $section['cards'] ) ) {
			foreach ( array_values( $section['cards'] ) as $card ) {
				if ( ! is_array( $card ) ) {
					continue;
				}
				$title = isset( $card['title'] ) ? $this->trim_text( $card['title'], 110 ) : '';
				$copy  = isset( $card['body'] ) ? $this->trim_text( $card['body'], 280 ) : '';
				if ( '' === $title && '' === $copy ) {
					continue;
				}
				$cards[] = array(
					'title' => '' !== $title ? $title : 'Item',
					'body'  => $copy,
					'image' => isset( $card['image'] ) && is_array( $card['image'] ) ? $this->normalize_image( $card['image'] ) : array(),
				);
				if ( count( $cards ) >= self::MAX_CARDS ) {
					break;
				}
			}
		}

		$buttons = array();
		if ( ! empty( $section['buttons'] ) && is_array( $section['buttons'] ) ) {
			foreach ( array_values( $section['buttons'] ) as $button ) {
				if ( ! is_array( $button ) ) {
					continue;
				}
				$text = isset( $button['text'] ) ? $this->trim_text( $button['text'], 70 ) : '';
				$link = isset( $button['url'] ) ? esc_url_raw( (string) $button['url'] ) : '#';
				if ( '' === $text ) {
					continue;
				}
				$buttons[] = array(
					'text' => $text,
					'url'  => '' !== $link ? $link : '#',
				);
				if ( count( $buttons ) >= self::MAX_BUTTONS ) {
					break;
				}
			}
		}

		$type = $this->classify_section_type( $type, $heading, $body, $cards, $buttons, $index );

		return array(
			'type'    => $type,
			'eyebrow' => $eyebrow,
			'heading' => $heading,
			'body'    => $body,
			'cards'   => $cards,
			'buttons' => $buttons,
			'image'   => isset( $section['image'] ) && is_array( $section['image'] ) ? $this->normalize_image( $section['image'] ) : array(),
		);
	}

	private function normalize_image( $image ) {
		$url = isset( $image['url'] ) ? esc_url_raw( (string) $image['url'] ) : '';
		if ( '' === $url ) {
			return array();
		}
		return array(
			'url' => $url,
			'alt' => isset( $image['alt'] ) ? $this->trim_text( $image['alt'], 120 ) : '',
		);
	}

	private function classify_section_type( $type, $heading, $body, $cards, $buttons, $index ) {
		$allowed = array( 'hero', 'cards', 'steps', 'quote', 'faq', 'cta', 'content', 'image', 'angie_hero', 'angie_capabilities', 'angie_confidence', 'angie_super_admin', 'angie_steps', 'angie_quote', 'angie_resources', 'angie_cta' );
		if ( in_array( $type, $allowed, true ) && 'content' !== $type ) {
			return $type;
		}

		$haystack = strtolower( $heading . ' ' . $body . ' ' . implode( ' ', wp_list_pluck( $cards, 'title' ) ) );
		if ( 0 === $index ) {
			return 'hero';
		}
		if ( false !== strpos( $haystack, 'faq' ) || false !== strpos( $haystack, 'question' ) ) {
			return 'faq';
		}
		if ( false !== strpos( $haystack, 'step' ) || false !== strpos( $haystack, 'get started' ) || false !== strpos( $haystack, 'minutes' ) ) {
			return 'steps';
		}
		if ( false !== strpos( $haystack, 'quote' ) || false !== strpos( $haystack, 'said' ) || false !== strpos( $haystack, 'testimonial' ) ) {
			return 'quote';
		}
		if ( ! empty( $buttons ) && count( $cards ) < 2 && ( false !== strpos( $haystack, 'start' ) || false !== strpos( $haystack, 'build' ) || false !== strpos( $haystack, 'download' ) ) ) {
			return 'cta';
		}
		if ( count( $cards ) >= 2 ) {
			return 'cards';
		}
		return 'content';
	}

	private function build_section( $section, $index ) {
		switch ( isset( $section['type'] ) ? (string) $section['type'] : 'content' ) {
			case 'hero':
				return $this->build_hero_section( $section );
			case 'cards':
				return $this->build_cards_section( $section, $index );
			case 'steps':
				return $this->build_steps_section( $section );
			case 'faq':
				return $this->build_faq_section( $section );
			case 'quote':
				return $this->build_quote_section( $section );
			case 'cta':
				return $this->build_cta_section( $section );
			default:
				return $this->build_content_section( $section, $index );
		}
	}

	private function build_hero_section( $section ) {
		$content = array();
		if ( '' !== $section['eyebrow'] ) {
			$content[] = $this->heading_widget( $section['eyebrow'], 'h6', 'left' );
		}
		$content[] = $this->heading_widget( '' !== $section['heading'] ? $section['heading'] : 'Reference page', 'h1', 'left' );
		if ( '' !== $section['body'] ) {
			$content[] = $this->text_widget( $section['body'], 'left' );
		}
		$content = array_merge( $content, $this->button_widgets( $section['buttons'], 'left' ) );

		$columns = array(
			$this->container(
				array(
					'content_width'  => 'full',
					'width'          => array( 'unit' => '%', 'size' => empty( $section['image']['url'] ) ? 100 : 56 ),
					'flex_direction' => 'column',
					'padding'        => $this->dimensions( 10, 10, 10, 10 ),
				),
				$content
			),
		);

		if ( ! empty( $section['image']['url'] ) ) {
			$columns[] = $this->container(
				array(
					'content_width'  => 'full',
					'width'          => array( 'unit' => '%', 'size' => 44 ),
					'flex_direction' => 'column',
					'padding'        => $this->dimensions( 10, 10, 10, 10 ),
				),
				array( $this->image_widget( $section['image'] ) )
			);
		}

		return $this->container(
			array(
				'content_width'      => 'boxed',
				'boxed_width'        => array( 'unit' => 'px', 'size' => 1200 ),
				'min_height'         => array( 'unit' => 'px', 'size' => 620 ),
				'flex_direction'     => 'row',
				'flex_wrap'          => 'wrap',
				'justify_content'    => 'center',
				'align_items'        => 'center',
				'gap'                => array( 'unit' => 'px', 'size' => 32 ),
				'padding'            => $this->dimensions( 96, 28, 86, 28 ),
				'background_background' => 'classic',
				'background_color'   => '#f3efff',
			),
			$columns
		);
	}

	private function build_cards_section( $section, $index ) {
		$children = $this->section_intro_widgets( $section, 'center' );
		$cards    = array();
		foreach ( $section['cards'] as $card ) {
			$card_children = array();
			if ( ! empty( $card['image']['url'] ) ) {
				$card_children[] = $this->image_widget( $card['image'] );
			}
			$card_children[] = $this->heading_widget( $card['title'], 'h3', 'left' );
			if ( '' !== $card['body'] ) {
				$card_children[] = $this->text_widget( $card['body'], 'left' );
			}
			$cards[] = $this->container(
				array(
					'content_width'      => 'full',
					'width'              => array( 'unit' => '%', 'size' => 31 ),
					'padding'            => $this->dimensions( 28, 24, 28, 24 ),
					'background_background' => 'classic',
					'background_color'   => '#ffffff',
					'border_radius'      => $this->dimensions( 16, 16, 16, 16, 'px' ),
					'box_shadow_box_shadow_type' => 'yes',
				),
				$card_children
			);
		}
		if ( ! empty( $cards ) ) {
			$children[] = $this->container(
				array(
					'content_width'  => 'full',
					'flex_direction' => 'row',
					'flex_wrap'      => 'wrap',
					'gap'            => array( 'unit' => 'px', 'size' => 24 ),
					'padding'        => $this->dimensions( 26, 0, 0, 0 ),
				),
				$cards
			);
		}
		$children = array_merge( $children, $this->button_widgets( $section['buttons'], 'center' ) );

		return $this->container(
			array(
				'content_width'    => 'boxed',
				'boxed_width'      => array( 'unit' => 'px', 'size' => 1200 ),
				'padding'          => $this->dimensions( 82, 28, 82, 28 ),
				'background_background' => 'classic',
				'background_color' => 0 === $index % 2 ? '#ffffff' : '#f8f7fb',
			),
			$children
		);
	}

	private function build_steps_section( $section ) {
		$children = $this->section_intro_widgets( $section, 'center' );
		$steps    = array();
		foreach ( array_values( $section['cards'] ) as $index => $card ) {
			$steps[] = $this->container(
				array(
					'content_width'      => 'full',
					'width'              => array( 'unit' => '%', 'size' => 31 ),
					'padding'            => $this->dimensions( 26, 22, 26, 22 ),
					'background_background' => 'classic',
					'background_color'   => '#ffffff',
				),
				array(
					$this->heading_widget( str_pad( (string) ( $index + 1 ), 2, '0', STR_PAD_LEFT ), 'h6', 'left' ),
					$this->heading_widget( $card['title'], 'h3', 'left' ),
					$this->text_widget( $card['body'], 'left' ),
				)
			);
		}
		if ( ! empty( $steps ) ) {
			$children[] = $this->container(
				array(
					'content_width'  => 'full',
					'flex_direction' => 'row',
					'flex_wrap'      => 'wrap',
					'gap'            => array( 'unit' => 'px', 'size' => 22 ),
					'padding'        => $this->dimensions( 26, 0, 0, 0 ),
				),
				$steps
			);
		}
		$children = array_merge( $children, $this->button_widgets( $section['buttons'], 'center' ) );

		return $this->container(
			array(
				'content_width'      => 'boxed',
				'boxed_width'        => array( 'unit' => 'px', 'size' => 1140 ),
				'padding'            => $this->dimensions( 82, 28, 82, 28 ),
				'background_background' => 'classic',
				'background_color'   => '#f6f1ff',
			),
			$children
		);
	}

	private function build_faq_section( $section ) {
		$children = $this->section_intro_widgets( $section, 'center' );
		$tabs     = array();
		foreach ( $section['cards'] as $card ) {
			$tabs[] = array(
				'_id'         => $this->element_id(),
				'tab_title'  => $card['title'],
				'tab_content' => wp_kses_post( wpautop( $card['body'] ) ),
			);
		}
		if ( empty( $tabs ) && '' !== $section['body'] ) {
			$tabs[] = array(
				'_id'         => $this->element_id(),
				'tab_title'  => '' !== $section['heading'] ? $section['heading'] : 'Question',
				'tab_content' => wp_kses_post( wpautop( $section['body'] ) ),
			);
		}
		if ( ! empty( $tabs ) ) {
			$children[] = $this->widget(
				'toggle',
				array(
					'tabs'        => $tabs,
					'icon'        => array( 'value' => 'fas fa-plus', 'library' => 'fa-solid' ),
					'active_icon' => array( 'value' => 'fas fa-minus', 'library' => 'fa-solid' ),
					'tab_title_color' => '#111827',
					'tab_active_color' => '#111827',
					'content_color' => '#4b5563',
					'border_color' => '#d1d5db',
				)
			);
		}

		return $this->container(
			array(
				'content_width' => 'boxed',
				'boxed_width'   => array( 'unit' => 'px', 'size' => 920 ),
				'padding'       => $this->dimensions( 78, 28, 78, 28 ),
				'background_background' => 'classic',
				'background_color' => '#ffffff',
			),
			$children
		);
	}

	private function build_quote_section( $section ) {
		$children = array();
		if ( '' !== $section['body'] ) {
			$children[] = $this->heading_widget( $section['body'], 'h2', 'center' );
		}
		if ( '' !== $section['heading'] ) {
			$children[] = $this->text_widget( $section['heading'], 'center' );
		}
		$children = array_merge( $children, $this->button_widgets( $section['buttons'], 'center' ) );

		return $this->container(
			array(
				'content_width'      => 'boxed',
				'boxed_width'        => array( 'unit' => 'px', 'size' => 980 ),
				'padding'            => $this->dimensions( 84, 28, 84, 28 ),
				'background_background' => 'classic',
				'background_color'   => '#f8f7fb',
			),
			$children
		);
	}

	private function build_cta_section( $section ) {
		$children = $this->section_intro_widgets( $section, 'center' );
		$children = array_merge( $children, $this->button_widgets( $section['buttons'], 'center' ) );

		return $this->container(
			array(
				'content_width'      => 'boxed',
				'boxed_width'        => array( 'unit' => 'px', 'size' => 1040 ),
				'padding'            => $this->dimensions( 88, 28, 88, 28 ),
				'background_background' => 'classic',
				'background_color'   => '#ece4ff',
			),
			$children
		);
	}

	private function build_content_section( $section, $index ) {
		$children = $this->section_intro_widgets( $section, 'left' );
		if ( ! empty( $section['image']['url'] ) ) {
			$children[] = $this->image_widget( $section['image'] );
		}
		$children = array_merge( $children, $this->button_widgets( $section['buttons'], 'left' ) );

		return $this->container(
			array(
				'content_width'    => 'boxed',
				'boxed_width'      => array( 'unit' => 'px', 'size' => 1040 ),
				'padding'          => $this->dimensions( 74, 28, 74, 28 ),
				'background_background' => 'classic',
				'background_color' => 0 === $index % 2 ? '#ffffff' : '#f8f7fb',
			),
			$children
		);
	}

	private function section_intro_widgets( $section, $align ) {
		$children = array();
		if ( '' !== $section['eyebrow'] ) {
			$children[] = $this->heading_widget( $section['eyebrow'], 'h6', $align );
		}
		if ( '' !== $section['heading'] ) {
			$children[] = $this->heading_widget( $section['heading'], 'h2', $align );
		}
		if ( '' !== $section['body'] && 'faq' !== $section['type'] ) {
			$children[] = $this->text_widget( $section['body'], $align );
		}
		return $children;
	}

	private function button_widgets( $buttons, $align ) {
		$widgets = array();
		foreach ( is_array( $buttons ) ? $buttons : array() as $button ) {
			$widgets[] = $this->widget(
				'button',
				array(
					'text'        => isset( $button['text'] ) ? (string) $button['text'] : 'Learn more',
					'link'        => array(
						'url'         => isset( $button['url'] ) ? (string) $button['url'] : '#',
						'is_external' => ! empty( $button['url'] ) && 0 !== strpos( (string) $button['url'], home_url( '/' ) ),
						'nofollow'    => false,
					),
					'align'       => $align,
					'size'        => 'md',
					'button_type' => 'info',
				)
			);
		}
		return $widgets;
	}

	private function heading_widget( $title, $size = 'h2', $align = 'left' ) {
		return $this->widget(
			'heading',
			array(
				'title'       => $this->trim_text( $title, 180 ),
				'header_size' => $size,
				'align'       => $align,
				'title_color' => '#111827',
			)
		);
	}

	private function text_widget( $text, $align = 'left' ) {
		return $this->widget(
			'text-editor',
			array(
				'editor' => wp_kses_post( wpautop( (string) $text ) ),
				'align'  => $align,
				'text_color' => '#4b5563',
			)
		);
	}

	private function image_widget( $image ) {
		return $this->widget(
			'image',
			array(
				'image'          => array(
					'id'  => '',
					'url' => isset( $image['url'] ) ? (string) $image['url'] : '',
				),
				'image_size'     => 'large',
				'caption_source' => 'none',
				'alt'            => isset( $image['alt'] ) ? (string) $image['alt'] : '',
			)
		);
	}

	private function container( $settings = array(), $elements = array() ) {
		return array(
			'id'       => $this->element_id(),
			'elType'   => 'container',
			'settings' => is_array( $settings ) ? $settings : array(),
			'elements' => is_array( $elements ) ? array_values( $elements ) : array(),
			'isInner'  => false,
		);
	}

	private function widget( $type, $settings = array() ) {
		return array(
			'id'         => $this->element_id(),
			'elType'     => 'widget',
			'settings'   => is_array( $settings ) ? $settings : array(),
			'elements'   => array(),
			'widgetType' => sanitize_key( (string) $type ),
		);
	}

	private function dimensions( $top, $right, $bottom, $left, $unit = 'px' ) {
		return array(
			'unit'     => $unit,
			'top'      => (string) $top,
			'right'    => (string) $right,
			'bottom'   => (string) $bottom,
			'left'     => (string) $left,
			'isLinked' => false,
		);
	}

	private function new_section( $heading, $type ) {
		return array(
			'type'    => $type,
			'eyebrow' => '',
			'heading' => $this->trim_text( $heading, 130 ),
			'body'    => '',
			'cards'   => array(),
			'buttons' => array(),
			'image'   => array(),
		);
	}

	private function push_section( &$sections, $section ) {
		if ( ! is_array( $section ) ) {
			return;
		}
		if ( '' === trim( (string) $section['heading'] ) && '' === trim( (string) $section['body'] ) && empty( $section['cards'] ) ) {
			return;
		}
		$sections[] = $section;
	}

	private function section_has_card( $section, $title ) {
		foreach ( isset( $section['cards'] ) && is_array( $section['cards'] ) ? $section['cards'] : array() as $card ) {
			if ( isset( $card['title'] ) && strtolower( $this->collapse_text( $card['title'] ) ) === strtolower( $this->collapse_text( $title ) ) ) {
				return true;
			}
		}
		return false;
	}

	private function section_has_button( $section, $text ) {
		foreach ( isset( $section['buttons'] ) && is_array( $section['buttons'] ) ? $section['buttons'] : array() as $button ) {
			if ( isset( $button['text'] ) && strtolower( $this->collapse_text( $button['text'] ) ) === strtolower( $this->collapse_text( $text ) ) ) {
				return true;
			}
		}
		return false;
	}

	private function section_body_contains( $section, $text ) {
		$body = isset( $section['body'] ) ? strtolower( $this->collapse_text( $section['body'] ) ) : '';
		$text = strtolower( $this->collapse_text( $text ) );
		return '' !== $text && false !== strpos( $body, $text );
	}

	private function is_boilerplate_text( $text ) {
		$text = strtolower( $this->collapse_text( $text ) );
		if ( strlen( $text ) < 3 ) {
			return true;
		}
		$blocked = array(
			'cookie',
			'privacy policy',
			'terms',
			'all rights reserved',
			'copyright',
			'log in',
			'cart',
			'menu',
			'skip to content',
		);
		foreach ( $blocked as $needle ) {
			if ( false !== strpos( $text, $needle ) && strlen( $text ) < 90 ) {
				return true;
			}
		}
		return false;
	}

	private function image_src_from_node( $node ) {
		$attrs = array( 'src', 'data-src', 'data-lazy-src', 'data-original' );
		foreach ( $attrs as $attr ) {
			$src = trim( (string) $node->getAttribute( $attr ) );
			if ( '' !== $src ) {
				return $src;
			}
		}
		$srcset = trim( (string) $node->getAttribute( 'srcset' ) );
		if ( '' !== $srcset ) {
			$parts = preg_split( '/\s*,\s*/', $srcset );
			if ( ! empty( $parts[0] ) ) {
				$first = preg_split( '/\s+/', trim( $parts[0] ) );
				return isset( $first[0] ) ? (string) $first[0] : '';
			}
		}
		return '';
	}

	private function resolve_url( $maybe_url, $base_url ) {
		$maybe_url = trim( (string) $maybe_url );
		if ( '' === $maybe_url || '#' === $maybe_url ) {
			return '#';
		}
		if ( 0 === strpos( $maybe_url, '//' ) ) {
			$scheme = wp_parse_url( $base_url, PHP_URL_SCHEME );
			return esc_url_raw( ( $scheme ? $scheme : 'https' ) . ':' . $maybe_url );
		}
		if ( preg_match( '#^https?://#i', $maybe_url ) ) {
			return esc_url_raw( $maybe_url );
		}
		$parts = wp_parse_url( $base_url );
		if ( empty( $parts['scheme'] ) || empty( $parts['host'] ) ) {
			return esc_url_raw( $maybe_url );
		}
		$origin = $parts['scheme'] . '://' . $parts['host'] . ( isset( $parts['port'] ) ? ':' . $parts['port'] : '' );
		if ( 0 === strpos( $maybe_url, '/' ) ) {
			return esc_url_raw( $origin . $maybe_url );
		}
		$path = isset( $parts['path'] ) ? preg_replace( '#/[^/]*$#', '/', $parts['path'] ) : '/';
		return esc_url_raw( $origin . $path . $maybe_url );
	}

	private function render_post_content( $blueprint ) {
		$parts = array();
		$sections = isset( $blueprint['sections'] ) && is_array( $blueprint['sections'] ) ? $blueprint['sections'] : array();
		foreach ( $sections as $section ) {
			if ( ! empty( $section['heading'] ) ) {
				$parts[] = $section['heading'];
			}
			if ( ! empty( $section['body'] ) ) {
				$parts[] = $section['body'];
			}
			if ( ! empty( $section['cards'] ) && is_array( $section['cards'] ) ) {
				foreach ( $section['cards'] as $card ) {
					if ( ! empty( $card['title'] ) ) {
						$parts[] = $card['title'];
					}
					if ( ! empty( $card['body'] ) ) {
						$parts[] = $card['body'];
					}
				}
			}
		}

		return wp_kses_post( wpautop( implode( "\n\n", array_filter( $parts ) ) ) );
	}

	private function trim_text( $text, $max = 300 ) {
		$text = $this->collapse_text( html_entity_decode( wp_strip_all_tags( (string) $text ), ENT_QUOTES, 'UTF-8' ) );
		if ( $max > 0 && strlen( $text ) > $max ) {
			if ( function_exists( 'mb_substr' ) ) {
				$text = mb_substr( $text, 0, $max - 3 ) . '...';
			} else {
				$text = substr( $text, 0, $max - 3 ) . '...';
			}
		}
		return $text;
	}

	private function collapse_text( $text ) {
		$text = preg_replace( '/\s+/u', ' ', (string) $text );
		return trim( (string) $text );
	}

	private function element_id() {
		return substr( md5( uniqid( 'niwa_url_', true ) ), 0, 7 );
	}
}
