<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

require_once VSERPB_PLUGIN_DIR . '/Schema_classes/class-vserpb-organization.php';
require_once VSERPB_PLUGIN_DIR . '/Schema_classes/class-vserpb-imageobject.php';
require_once VSERPB_PLUGIN_DIR . '/Schema_classes/class-vserpb-person.php';

/**
 * Class Vserpb_WebPage_Schema
 * @version 2.1.0
 */
class Vserpb_WebPage_Schema {

	public static function vserpb_generate_schema( $page_id ) {

		if ( ! is_page() ) {
			return;
		}

		$post = get_post( $page_id );
		if ( ! $post || get_post_type( $page_id ) !== 'page' ) {
			return;
		}

		$title          = sanitize_text_field( get_the_title( $page_id ) );
		$description    = self::vserpb_get_description( $page_id );
		$page_url       = get_permalink( $page_id );
		$published_date = get_the_date( 'c', $page_id );
		$modified_date  = get_the_modified_date( 'c', $page_id );
		$page_slug      = $post->post_name;
		$site_name      = get_bloginfo( 'name' );
		$site_url       = home_url();
		$language       = get_bloginfo( 'language' );
		$page_type      = self::vserpb_detect_page_type( $page_slug );
		$raw_content    = apply_filters( 'the_content', get_post_field( 'post_content', $page_id ) );
		$plain_content  = wp_strip_all_tags( $raw_content );
		$word_count     = str_word_count( $plain_content );
		$reading_time   = max( 1, (int) ceil( $word_count / 200 ) );

		$schema = array(
			'@context'           => 'https://schema.org/',
			'@type'              => $page_type,
			'@id'  				 => esc_url( $page_url . '#webpage' ),
			'url'                => esc_url( $page_url ),
			'name'               => $title,
			'headline'           => $title,
			'description'        => $description,
			'inLanguage'         => $language,
			'dateCreated'        => $published_date,
			'datePublished'      => $published_date,
			'dateModified'       => $modified_date,
			'encodingFormat'     => 'text/html',
			'creativeWorkStatus' => 'publish' === get_post_status( $page_id ) ? 'Published' : 'Draft',
		);

		$schema['isPartOf'] = array(
			'@type'       => 'WebSite',
			'@id'         => $site_url . '/#website',
			'url'         => esc_url( $site_url ),
			'name'        => $site_name,
			'description' => wp_strip_all_tags( get_bloginfo( 'description' ) ),
			'inLanguage'  => $language,
		);

		$schema['publisher']       = self::vserpb_get_org_schema( $site_name, $site_url );
		$schema['copyrightHolder'] = self::vserpb_get_org_schema( $site_name, $site_url );
		$schema['maintainer']      = self::vserpb_get_org_schema( $site_name, $site_url );
		$schema['copyrightYear']   = gmdate( 'Y', strtotime( $published_date ) );

		$author_schema = self::vserpb_get_author_schema( $post );
		if ( ! empty( $author_schema ) ) {
			$schema['author'] = $author_schema;
		}

		$image_obj    = new Vserpb_ImageObject( $page_id );
		$image_schema = $image_obj->vserpb_getSchema();
		if ( ! empty( $image_schema ) ) {
			$schema['image']              = $image_schema;
			$schema['thumbnailUrl']       = $image_schema['url'];
			$schema['primaryImageOfPage'] = array( '@id' => $page_url . '#primaryimage' );
		}

		$schema['breadcrumb'] = self::vserpb_get_breadcrumbs( $page_id, $title, $page_url );

		if ( ! empty( $plain_content ) ) {
			$schema['text']         = mb_substr( trim( $plain_content ), 0, 5000 );
			$schema['wordCount']    = $word_count;
			$schema['timeRequired'] = 'PT' . $reading_time . 'M';
		}

		$keywords = self::vserpb_get_keywords( $page_id );
		if ( ! empty( $keywords ) ) {
			$schema['keywords'] = $keywords;
		}

		$schema['identifier'] = array(
			array(
				'@type'      => 'PropertyValue',
				'propertyID' => 'slug',
				'value'      => $page_slug,
			),
			array(
				'@type'      => 'PropertyValue',
				'propertyID' => 'postID',
				'value'      => (string) $page_id,
			),
		);

		$access_modes = array( 'textual' );
		if ( ! empty( $image_schema ) ) {
			$access_modes[] = 'visual';
		}
		$schema['accessMode']           = $access_modes;
		$schema['accessibilityFeature'] = array(
			'structuralNavigation',
			'tableOfContents',
			'readingOrder',
		);
		$schema['accessibilityHazard']  = 'noFlashingHazard';
		$schema['accessibilitySummary'] = 'This page uses structured headings and semantic HTML for maximum accessibility.';

		$schema['about'] = self::vserpb_get_about_entities( $page_id, $post );

		$mentioned = self::vserpb_get_mentions( $raw_content );
		if ( ! empty( $mentioned ) ) {
			$schema['mentions'] = $mentioned;
		}

		$sig_links = self::vserpb_get_significant_links( $raw_content, $site_url );
		if ( ! empty( $sig_links ) ) {
			$schema['significantLink'] = $sig_links;
		}

		$sections = self::vserpb_get_content_sections( $raw_content, $page_url );
		if ( ! empty( $sections ) ) {
			$schema['hasPart'] = $sections;
		}

		$comment_count = (int) $post->comment_count;
		if ( $comment_count > 0 ) {
			$schema['interactionStatistic'] = array(
				'@type'                => 'InteractionCounter',
				'interactionType'      => 'https://schema.org/CommentAction',
				'userInteractionCount' => $comment_count,
			);
		}

		if ( 'publish' === get_post_status( $page_id ) && ! post_password_required( $page_id ) ) {
			$schema['potentialAction'] = array(
				array(
					'@type'  => 'ReadAction',
					'name'   => $title,
					'target' => esc_url( $page_url ),
				),
			);
		}

		$license = apply_filters( 'vserpb_webpage_license', '' );
		if ( ! empty( $license ) ) {
			$schema['license'] = esc_url( $license );
		}

		$country = apply_filters( 'vserpb_webpage_country', '' );
		if ( ! empty( $country ) ) {
			$schema['countryOfOrigin'] = array(
				'@type' => 'Country',
				'name'  => sanitize_text_field( $country ),
			);
		}

		$schema = self::vserpb_extend_by_type( $schema, $page_type, $post, $page_id, $page_url, $title, $site_url );
		$schema = apply_filters( 'vserpb_extend_webpage_schema', $schema, $page_id );

		return $schema;
	}

	// -----------------------------------------------------------------------
	// PAGE-TYPE DETECTION
	// -----------------------------------------------------------------------

	private static function vserpb_detect_page_type( $slug ) {
		$map = array(
			'about'                      => 'AboutPage',
			'about-us'                   => 'AboutPage',
			'our-story'                  => 'AboutPage',
			'who-we-are'                 => 'AboutPage',
			'company'                    => 'AboutPage',
			'team'                       => 'AboutPage',
			'contact'                    => 'ContactPage',
			'contact-us'                 => 'ContactPage',
			'get-in-touch'               => 'ContactPage',
			'reach-us'                   => 'ContactPage',
			'support'                    => 'ContactPage',
			'faq'                        => 'FAQPage',
			'faqs'                       => 'FAQPage',
			'frequently-asked-questions' => 'FAQPage',
			'help'                       => 'FAQPage',
			'questions'                  => 'FAQPage',
			'checkout'                   => 'CheckoutPage',
			'cart'                       => 'CheckoutPage',
			'basket'                     => 'CheckoutPage',
			'services'                   => 'WebPage',
			'service'                    => 'WebPage',
			'what-we-do'                 => 'WebPage',
			'solutions'                  => 'WebPage',
			'pricing'                    => 'WebPage',
			'profile'                    => 'ProfilePage',
			'author'                     => 'ProfilePage',
			'bio'                        => 'ProfilePage',
			'portfolio'                  => 'CollectionPage',
			'projects'                   => 'CollectionPage',
			'gallery'                    => 'CollectionPage',
			'resources'                  => 'CollectionPage',
			'blog'                       => 'CollectionPage',
			'search'                     => 'SearchResultsPage',
			'search-results'             => 'SearchResultsPage',
			'health'                     => 'MedicalWebPage',
			'medical'                    => 'MedicalWebPage',
			'wellness'                   => 'MedicalWebPage',
			'privacy-policy'             => 'WebPage',
			'terms-of-service'           => 'WebPage',
			'terms-and-conditions'       => 'WebPage',
			'cookie-policy'              => 'WebPage',
			'disclaimer'                 => 'WebPage',
			'refund-policy'              => 'WebPage',
			'return-policy'              => 'WebPage',
			'home'                       => 'WebPage',
		);

		$map = apply_filters( 'vserpb_page_type_map', $map );
		return isset( $map[ $slug ] ) ? $map[ $slug ] : 'WebPage';
	}

	// -----------------------------------------------------------------------
	// PAGE-TYPE SPECIFIC EXTENSIONS
	// -----------------------------------------------------------------------

	private static function vserpb_extend_by_type( $schema, $page_type, $post, $page_id, $page_url, $title, $site_url ) {

		switch ( $page_type ) {

			case 'FAQPage':
				// FIX: Always attempt FAQ extraction; guard against empty result
				$faq_items = self::vserpb_get_faq_items( $post );

				if ( ! empty( $faq_items ) ) {
					// FIX: mainEntity must be an array of Question items directly
					$schema['mainEntity'] = $faq_items;
				} else {
					/*
					 * FIX: If no FAQ items found, fall back to WebPage type so
					 * Google's Rich Results Test doesn't flag a missing mainEntity
					 * on an FAQPage. Better to have a valid generic page than
					 * a broken FAQPage schema.
					 */
					$schema['@type'] = 'WebPage';
				}
				break;

			case 'ContactPage':
				$schema['mainEntity'] = array(
					'@type' => 'ContactPage',
					'url'   => esc_url( $page_url ),
					'name'  => $title,
				);
				$contact_email = get_option( 'admin_email' );
				$contact_phone = apply_filters( 'vserpb_contact_phone', '' );
				if ( $contact_email || $contact_phone ) {
					$contact_point = array( '@type' => 'ContactPoint' );
					if ( $contact_email ) {
						$contact_point['email'] = sanitize_email( $contact_email );
					}
					if ( $contact_phone ) {
						$contact_point['telephone'] = sanitize_text_field( $contact_phone );
					}
					$contact_point['contactType']        = 'customer support';
					$contact_point['availableLanguage']  = array( '@type' => 'Language', 'name' => get_bloginfo( 'language' ) );
					$schema['publisher']['contactPoint'] = $contact_point;
				}
				break;

			case 'AboutPage':
				$schema['about']      = self::vserpb_get_org_schema( get_bloginfo( 'name' ), $site_url );
				$schema['mentions'][] = self::vserpb_get_org_schema( get_bloginfo( 'name' ), $site_url );
				$social_profiles      = apply_filters( 'vserpb_social_profiles', array() );
				if ( ! empty( $social_profiles ) ) {
					$schema['publisher']['sameAs'] = array_map( 'esc_url', $social_profiles );
				}
				break;

			case 'MedicalWebPage':
				$schema['audience']        = array( '@type' => 'Patient' );
				$schema['medicalAudience'] = array(
					'@type'        => 'MedicalAudience',
					'audienceType' => 'patient',
				);
				$schema['lastReviewed'] = get_the_modified_date( 'c', $page_id );
				$schema['reviewedBy']   = self::vserpb_get_org_schema( get_bloginfo( 'name' ), $site_url );
				break;

			case 'ProfilePage':
				$author = self::vserpb_get_author_schema( get_post( $page_id ) );
				if ( ! empty( $author ) ) {
					$schema['mainEntity'] = $author;
				}
				break;

			case 'CollectionPage':
				$schema['about'] = array(
					'@type' => 'Thing',
					'name'  => get_the_title( $page_id ),
				);
				break;

			case 'CheckoutPage':
				if ( function_exists( 'get_woocommerce_currency' ) ) {
					$schema['potentialAction'][] = array(
						'@type'  => 'OrderAction',
						'target' => array(
							'@type'          => 'EntryPoint',
							'urlTemplate'    => esc_url( wc_get_checkout_url() ),
							'actionPlatform' => array(
								'https://schema.org/DesktopWebPlatform',
								'https://schema.org/MobileWebPlatform',
							),
						),
						'result' => array( '@type' => 'Order' ),
					);
				}
				break;

			case 'WebPage':
			default:
				$legal_slugs = array( 'privacy-policy', 'terms-of-service', 'terms-and-conditions', 'disclaimer', 'cookie-policy', 'refund-policy', 'return-policy' );
				if ( in_array( get_post_field( 'post_name', $page_id ), $legal_slugs, true ) ) {
					$schema['audience'] = array( '@type' => 'Audience', 'audienceType' => 'general public' );
					$schema['genre']    = 'legal';
				}
				break;
		}

		return $schema;
	}

	// -----------------------------------------------------------------------
	// ORGANIZATION SCHEMA
	// -----------------------------------------------------------------------

	private static function vserpb_get_org_schema( $name, $url ) {
		$org        = new Vserpb_Organization( array( 'name' => $name, 'url' => $url ) );
		$org_schema = $org->vserpb_getSchema();

		$logo_id = get_theme_mod( 'custom_logo' );
		if ( $logo_id ) {
			$logo_url  = wp_get_attachment_url( $logo_id );
			$logo_meta = wp_get_attachment_metadata( $logo_id );
			if ( $logo_url ) {
				$org_schema['logo'] = array(
					'@type'  => 'ImageObject',
					'@id'    => $url . '#logo',
					'url'    => esc_url( $logo_url ),
					'width'  => isset( $logo_meta['width'] ) ? (int) $logo_meta['width'] : null,
					'height' => isset( $logo_meta['height'] ) ? (int) $logo_meta['height'] : null,
				);
			}
		}

		$social_profiles = apply_filters( 'vserpb_social_profiles', array() );
		if ( ! empty( $social_profiles ) ) {
			$org_schema['sameAs'] = array_map( 'esc_url', $social_profiles );
		}

		$org_desc = wp_strip_all_tags( get_bloginfo( 'description' ) );
		if ( ! empty( $org_desc ) ) {
			$org_schema['description'] = $org_desc;
		}

		return $org_schema;
	}

	// -----------------------------------------------------------------------
	// AUTHOR SCHEMA
	// -----------------------------------------------------------------------

	private static function vserpb_get_author_schema( $post ) {
		if ( empty( $post->post_author ) ) {
			return array();
		}

		$author_id    = (int) $post->post_author;
		$author_name  = get_the_author_meta( 'display_name', $author_id );
		$author_url   = get_author_posts_url( $author_id );
		$author_bio   = get_the_author_meta( 'description', $author_id );
		$author_image = get_avatar_url( $author_id, array( 'size' => 300 ) );

		if ( class_exists( 'Vserpb_Person' ) ) {
			$person        = new Vserpb_Person( array( 'name' => $author_name, 'url' => $author_url ) );
			$person_schema = $person->vserpb_getSchema();
		} else {
			$person_schema = array(
				'@type' => 'Person',
				'name'  => sanitize_text_field( $author_name ),
				'url'   => esc_url( $author_url ),
			);
		}

		if ( ! empty( $author_bio ) ) {
			$person_schema['description'] = wp_strip_all_tags( $author_bio );
		}
		if ( ! empty( $author_image ) ) {
			$person_schema['image'] = array( '@type' => 'ImageObject', 'url' => esc_url( $author_image ) );
		}

		$author_social = apply_filters( 'vserpb_author_social_profiles', array(), $author_id );
		if ( ! empty( $author_social ) ) {
			$person_schema['sameAs'] = array_map( 'esc_url', $author_social );
		}

		return $person_schema;
	}

	// -----------------------------------------------------------------------
	// DESCRIPTION
	// -----------------------------------------------------------------------

	private static function vserpb_get_description( $page_id ) {
		$excerpt = get_the_excerpt( $page_id );
		if ( ! empty( $excerpt ) ) {
			return wp_strip_all_tags( do_shortcode( $excerpt ) );
		}

		if ( function_exists( 'YoastSEO' ) ) {
			$yoast_desc = get_post_meta( $page_id, '_yoast_wpseo_metadesc', true );
			if ( ! empty( $yoast_desc ) ) {
				return sanitize_text_field( $yoast_desc );
			}
		}

		$rm_desc = get_post_meta( $page_id, 'rank_math_description', true );
		if ( ! empty( $rm_desc ) ) {
			return sanitize_text_field( $rm_desc );
		}

		$content = wp_strip_all_tags( apply_filters( 'the_content', get_post_field( 'post_content', $page_id ) ) );
		return mb_substr( trim( $content ), 0, 160 );
	}

	// -----------------------------------------------------------------------
	// KEYWORDS
	// -----------------------------------------------------------------------

	private static function vserpb_get_keywords( $page_id ) {
		$keywords = array();

		$tags = get_the_tags( $page_id );
		if ( $tags && ! is_wp_error( $tags ) ) {
			foreach ( $tags as $tag ) {
				$keywords[] = $tag->name;
			}
		}

		$yoast_kw = get_post_meta( $page_id, '_yoast_wpseo_focuskw', true );
		if ( ! empty( $yoast_kw ) ) {
			$keywords[] = sanitize_text_field( $yoast_kw );
		}

		$rm_kw = get_post_meta( $page_id, 'rank_math_focus_keyword', true );
		if ( ! empty( $rm_kw ) ) {
			foreach ( explode( ',', $rm_kw ) as $kw ) {
				$keywords[] = trim( sanitize_text_field( $kw ) );
			}
		}

		$custom_keywords = apply_filters( 'vserpb_webpage_keywords', array(), $page_id );
		$keywords        = array_merge( $keywords, $custom_keywords );
		$keywords        = array_unique( array_filter( $keywords ) );

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

	// -----------------------------------------------------------------------
	// ABOUT ENTITIES
	// -----------------------------------------------------------------------

	private static function vserpb_get_about_entities( $page_id, $post ) {
		$entities = array();

		$entities[] = array(
			'@type' => 'Thing',
			'name'  => sanitize_text_field( get_the_title( $page_id ) ),
			'url'   => esc_url( get_permalink( $page_id ) ),
		);

		$categories = get_the_terms( $page_id, 'category' );
		if ( $categories && ! is_wp_error( $categories ) ) {
			foreach ( $categories as $cat ) {
				$entities[] = array(
					'@type' => 'Thing',
					'name'  => sanitize_text_field( $cat->name ),
					'url'   => esc_url( get_term_link( $cat ) ),
				);
			}
		}

		$entities = apply_filters( 'vserpb_about_entities', $entities, $page_id );
		return count( $entities ) === 1 ? $entities[0] : $entities;
	}

	// -----------------------------------------------------------------------
	// MENTIONS
	// -----------------------------------------------------------------------

	private static function vserpb_get_mentions( $raw_content ) {
		$mentions = array();

		if ( empty( $raw_content ) ) {
			return $mentions;
		}

		preg_match_all(
			'/<a[^>]+href=["\']https?:\/\/(?:www\.)?wikipedia\.org\/wiki\/([^"\'#]+)["\'][^>]*>([^<]+)<\/a>/i',
			$raw_content,
			$matches,
			PREG_SET_ORDER
		);

		foreach ( $matches as $match ) {
			$slug      = rawurldecode( $match[1] );
			$link_text = wp_strip_all_tags( $match[2] );
			$mentions[] = array(
				'@type'  => 'Thing',
				'name'   => sanitize_text_field( $link_text ?: str_replace( '_', ' ', $slug ) ),
				'sameAs' => 'https://en.wikipedia.org/wiki/' . rawurlencode( $slug ),
			);
		}

		$mentions = apply_filters( 'vserpb_page_mentions', $mentions, $raw_content );
		return array_slice( $mentions, 0, 10 );
	}

	// -----------------------------------------------------------------------
	// SIGNIFICANT LINKS
	// -----------------------------------------------------------------------

	private static function vserpb_get_significant_links( $raw_content, $site_url ) {
		if ( empty( $raw_content ) ) {
			return array();
		}

		preg_match_all( '/href=["\'](' . preg_quote( $site_url, '/' ) . '[^"\'#?\s]+)["\']/', $raw_content, $matches );

		if ( empty( $matches[1] ) ) {
			return array();
		}

		$links = array_unique( $matches[1] );
		$links = array_map( 'esc_url', $links );
		return array_slice( array_values( $links ), 0, 10 );
	}

	// -----------------------------------------------------------------------
	// CONTENT SECTIONS
	// -----------------------------------------------------------------------

	private static function vserpb_get_content_sections( $raw_content, $page_url ) {
		if ( empty( $raw_content ) ) {
			return array();
		}

		$parts = array();

		preg_match_all(
			'/<h2[^>]*(?:id=["\']([^"\']+)["\'])?[^>]*>(.*?)<\/h2>(.*?)(?=<h2|$)/si',
			$raw_content,
			$matches,
			PREG_SET_ORDER
		);

		foreach ( $matches as $index => $match ) {
			$section_id = ! empty( $match[1] ) ? $match[1] : 'section-' . ( $index + 1 );
			$heading    = wp_strip_all_tags( $match[2] );
			$body_text  = mb_substr( wp_strip_all_tags( $match[3] ), 0, 500 );

			if ( empty( trim( $heading ) ) ) {
				continue;
			}

			$part = array(
				'@type'       => 'WebPageElement',
				'@id'         => $page_url . '#' . $section_id,
				'name'        => sanitize_text_field( $heading ),
				'cssSelector' => '#' . $section_id,
			);

			if ( ! empty( $body_text ) ) {
				$part['text'] = trim( $body_text );
			}

			$parts[] = $part;
		}

		return $parts;
	}

	// -----------------------------------------------------------------------
	// BREADCRUMBS
	// -----------------------------------------------------------------------

	private static function vserpb_get_breadcrumbs( $page_id, $title, $page_url ) {
		$items    = array();
		$position = 1;

		$items[] = array(
			'@type'    => 'ListItem',
			'position' => $position++,
			'name'     => get_bloginfo( 'name' ),
			'item'     => array(
				'@type' => 'WebPage',
				'@id'   => home_url( '/' ),
				'url'   => home_url( '/' ),
				'name'  => get_bloginfo( 'name' ),
			),
		);

		$ancestors = get_post_ancestors( $page_id );
		foreach ( array_reverse( $ancestors ) as $ancestor_id ) {
			$items[] = array(
				'@type'    => 'ListItem',
				'position' => $position++,
				'name'     => sanitize_text_field( get_the_title( $ancestor_id ) ),
				'item'     => array(
					'@type' => 'WebPage',
					'@id'   => esc_url( get_permalink( $ancestor_id ) ),
					'url'   => esc_url( get_permalink( $ancestor_id ) ),
					'name'  => sanitize_text_field( get_the_title( $ancestor_id ) ),
				),
			);
		}

		$items[] = array(
			'@type'    => 'ListItem',
			'position' => $position,
			'name'     => $title,
			'item'     => array(
				'@type' => 'WebPage',
				'@id'   => esc_url( $page_url ),
				'url'   => esc_url( $page_url ),
				'name'  => $title,
			),
		);

		return array(
			'@type'           => 'BreadcrumbList',
			'@id'             => $page_url . '#breadcrumb',
			'name'            => 'Breadcrumbs',
			'itemListElement' => $items,
		);
	}

	// -----------------------------------------------------------------------
	// FAQ ITEMS — FIXED
	// -----------------------------------------------------------------------

	/**
	 * Extract FAQ Q&A pairs from all supported content formats.
	 *
	 * Priority order:
	 *   1. Yoast FAQ block
	 *   2. Rank Math FAQ block
	 *   3. Core Details block
	 *   4. UAG FAQ block
	 *   5. <details>/<summary> HTML
	 *   6. <dl>/<dt>/<dd> HTML
	 *   7. FIX: H2 + following paragraph (most common plain-content FAQ pattern)
	 *   8. FIX: H3 + following paragraph (fallback)
	 *
	 * @param WP_Post $post
	 * @return array
	 */
	private static function vserpb_get_faq_items( $post ) {

		$faq_list = array();

		// ── 1–4: Gutenberg blocks ─────────────────────────────────────────
		if ( has_blocks( $post->post_content ) ) {

			$blocks = parse_blocks( $post->post_content );

			foreach ( $blocks as $block ) {
				$name = $block['blockName'] ?? '';

				if ( 'yoast/faq-block' === $name && ! empty( $block['attrs']['questions'] ) ) {
					foreach ( $block['attrs']['questions'] as $q ) {
						$question = wp_strip_all_tags( $q['jsonQuestion'] ?? '' );
						$answer   = $q['jsonAnswer'] ?? '';
						if ( $question && $answer ) {
							$faq_list[] = self::vserpb_build_faq_item( $question, $answer );
						}
					}
				}

				if ( 'rank-math/faq-block' === $name && ! empty( $block['attrs']['questions'] ) ) {
					foreach ( $block['attrs']['questions'] as $q ) {
						$question = wp_strip_all_tags( $q['question'] ?? '' );
						$answer   = $q['answer'] ?? '';
						if ( $question && $answer ) {
							$faq_list[] = self::vserpb_build_faq_item( $question, $answer );
						}
					}
				}

				if ( 'core/details' === $name && ! empty( $block['attrs']['summary'] ) ) {
					$inner_text = '';
					foreach ( $block['innerBlocks'] as $inner ) {
						$inner_text .= render_block( $inner );
					}
					if ( ! empty( $inner_text ) ) {
						$faq_list[] = self::vserpb_build_faq_item( $block['attrs']['summary'], $inner_text );
					}
				}

				if ( 'uagb/faq' === $name && ! empty( $block['attrs']['schema'] ) ) {
					foreach ( $block['attrs']['schema'] as $q ) {
						$question = $q['question'] ?? '';
						$answer   = $q['answer'] ?? '';
						if ( $question && $answer ) {
							$faq_list[] = self::vserpb_build_faq_item( $question, $answer );
						}
					}
				}
			}
		}

		// Early return if blocks gave us results
		if ( ! empty( $faq_list ) ) {
			return $faq_list;
		}

		// ── 5: <details>/<summary> ────────────────────────────────────────
		$faq_list = self::vserpb_parse_html_faq( $post->post_content );
		if ( ! empty( $faq_list ) ) {
			return $faq_list;
		}

		// ── 7 FIX: H2 + next <p> ─────────────────────────────────────────
		// Use the RENDERED content (with shortcodes processed) for reliability
		$rendered = apply_filters( 'the_content', $post->post_content );

		preg_match_all(
			'/<h2[^>]*>(.*?)<\/h2>\s*(?:<[^>]+>)*\s*(.*?)\s*(?=<h2|<h3|$)/si',
			$rendered,
			$matches,
			PREG_SET_ORDER
		);

		foreach ( $matches as $match ) {
			$question = wp_strip_all_tags( $match[1] );
			// FIX: grab first <p> worth of text from whatever follows the heading
			$after_heading = $match[2];
			preg_match( '/<p[^>]*>(.*?)<\/p>/si', $after_heading, $p_match );
			$answer = ! empty( $p_match[1] ) ? $p_match[1] : $after_heading;
			$answer = trim( wp_strip_all_tags( $answer ) );

			if ( ! empty( $question ) && ! empty( $answer ) ) {
				$faq_list[] = self::vserpb_build_faq_item( $question, $answer );
			}
		}

		if ( ! empty( $faq_list ) ) {
			return $faq_list;
		}

		// ── 8 FIX: H3 + next <p> (last resort) ───────────────────────────
		preg_match_all(
			'/<h3[^>]*>(.*?)<\/h3>\s*(?:<[^>]+>)*\s*(.*?)\s*(?=<h3|<h2|$)/si',
			$rendered,
			$matches,
			PREG_SET_ORDER
		);

		foreach ( $matches as $match ) {
			$question = wp_strip_all_tags( $match[1] );
			preg_match( '/<p[^>]*>(.*?)<\/p>/si', $match[2], $p_match );
			$answer = ! empty( $p_match[1] ) ? $p_match[1] : $match[2];
			$answer = trim( wp_strip_all_tags( $answer ) );

			if ( ! empty( $question ) && ! empty( $answer ) ) {
				$faq_list[] = self::vserpb_build_faq_item( $question, $answer );
			}
		}

		return $faq_list;
	}

	private static function vserpb_parse_html_faq( $content ) {
		$faq_list = array();

		// <details><summary>Q</summary>A</details>
		preg_match_all(
			'/<details[^>]*>.*?<summary[^>]*>(.*?)<\/summary>(.*?)<\/details>/si',
			$content,
			$matches,
			PREG_SET_ORDER
		);
		foreach ( $matches as $m ) {
			$question = wp_strip_all_tags( $m[1] );
			$answer   = trim( wp_strip_all_tags( $m[2] ) );
			if ( $question && $answer ) {
				$faq_list[] = self::vserpb_build_faq_item( $question, $answer );
			}
		}

		if ( ! empty( $faq_list ) ) {
			return $faq_list;
		}

		// <dl><dt>Q</dt><dd>A</dd></dl>
		preg_match_all(
			'/<dt[^>]*>(.*?)<\/dt>\s*<dd[^>]*>(.*?)<\/dd>/si',
			$content,
			$matches,
			PREG_SET_ORDER
		);
		foreach ( $matches as $m ) {
			$question = wp_strip_all_tags( $m[1] );
			$answer   = trim( wp_strip_all_tags( $m[2] ) );
			if ( $question && $answer ) {
				$faq_list[] = self::vserpb_build_faq_item( $question, $answer );
			}
		}

		return $faq_list;
	}

	private static function vserpb_build_faq_item( $question, $answer ) {
		return array(
			'@type'          => 'Question',
			'name'           => sanitize_text_field( wp_strip_all_tags( $question ) ),
			'acceptedAnswer' => array(
				'@type' => 'Answer',
				// FIX: plain text answer is safer; kses_post is fine for rich answers
				'text'  => wp_kses_post( $answer ),
			),
		);
	}
}