<?php
/**
 * Schema.org JSON-LD Generator — единый @graph
 *
 * Генерирует микроразметку для страницы «Подготовка к школе».
 * Только динамические данные из Carbon Fields. Никаких хардкод-значений.
 *
 * @package Dekart
 * @since   2026-05-31
 * @see     https://schema.org/Course
 */

defined( 'ABSPATH' ) || exit;

/**
 * Отключаем Yoast SEO JSON-LD на странице Подготовки к школе,
 * чтобы избежать дублирования WebPage + Organization.
 * Аналогично page-camp.php.
 */
add_filter( 'wpseo_json_ld_output', '__return_false' );

/**
 * Принудительно меняем og:type на 'website' для страницы Подготовки к школе (ID=2),
 * чтобы избежать несоответствия article/website в социальных сниппетах.
 */
add_filter( 'wpseo_opengraph_type', 'dekart_schema_og_type', 10, 1 );
function dekart_schema_og_type( string $type ): string {
	if ( is_page( 2 ) ) {
		return 'website';
	}
	return $type;
}

/**
 * Главная функция: сборка @graph и вывод в wp_head.
 * Срабатывает только на странице ID=2 (Подготовка к школе).
 */
add_action( 'wp_head', 'dekart_schema_output', 1 );

function dekart_schema_output(): void {
	if ( ! is_page( 2 ) ) {
		return;
	}

	// Guard: без Carbon Fields — тихий выход (не может быть фатала)
	if ( ! function_exists( 'carbon_get_post_meta' ) || ! function_exists( 'carbon_get_theme_option' ) ) {
		return;
	}

	$graph = array();

	// 1. WebPage
	$webpage = dekart_schema_webpage();
	if ( $webpage ) {
		$graph[] = $webpage;
	}

	// 2. WebSite
	$website = dekart_schema_website();
	if ( $website ) {
		$graph[] = $website;
	}

	// 3. BreadcrumbList
	$breadcrumb = dekart_schema_breadcrumb();
	if ( $breadcrumb ) {
		$graph[] = $breadcrumb;
	}

	// 4. Parent Organization (юридическое лицо, без адреса)
	$organization = dekart_schema_organization();
	if ( $organization ) {
		$graph[] = $organization;
	}

	// 5. LocalBusiness (филиал/центр, с адресом)
	$local_business = dekart_schema_local_business();
	if ( $local_business ) {
		$graph[] = $local_business;
	}

	// 6. Course
	$course = dekart_schema_course();
	if ( $course ) {
		$graph[] = $course;
	}

	// 7. Offer / AggregateOffer (conditional)
	$offers = dekart_schema_offer();
	if ( ! empty( $offers ) ) {
		if ( isset( $offers[0] ) && is_array( $offers[0] ) ) {
			// Вернулся массив Offer'ов — добавляем каждый в граф
			foreach ( $offers as $offer ) {
				$graph[] = $offer;
			}
		} else {
			// Один Offer (для обратной совместимости)
			$graph[] = $offers;
		}
	}

	// 8. AggregateRating (conditional — есть ли отзывы с оценками)
	$rating = dekart_schema_rating();
	if ( $rating ) {
		$graph[] = $rating;
	}

	// 9. FAQPage (conditional — есть ли вопросы)
	$faq = dekart_schema_faq();
	if ( $faq ) {
		$graph[] = $faq;
	}

	// 10. Review[] — поштучные отзывы (conditional)
	$reviews = dekart_schema_reviews();
	if ( ! empty( $reviews ) && is_array( $reviews ) ) {
		foreach ( $reviews as $review ) {
			$graph[] = $review;
		}
	}

	// 11. Person[] — преподаватели (conditional)
	$teachers = dekart_schema_teachers();
	if ( ! empty( $teachers ) && is_array( $teachers ) ) {
		foreach ( $teachers as $person ) {
			$graph[] = $person;
		}
	}

	if ( empty( $graph ) ) {
		return;
	}

	$json = wp_json_encode(
		array(
			'@context' => 'https://schema.org',
			'@graph'   => $graph,
		),
		JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
	);

	if ( false === $json ) {
		return; // тихий выход вместо WSOD
	}

	echo '<script type="application/ld+json">' . "\n" . $json . "\n" . '</script>' . "\n";
}

/* =====================================================================
   Сущности @graph
   ===================================================================== */

/**
 * WebPage — текущая страница.
 * @see https://schema.org/WebPage
 */
function dekart_schema_webpage(): ?array {
	$url = home_url( '/podgotovka-k-shkole/' );

	// _yoast_wpseo_title — Yoast SEO заголовок
	$yoast_title = get_post_meta( 2, '_yoast_wpseo_title', true );
	$name        = $yoast_title ?: wp_get_document_title();

	// _yoast_wpseo_metadesc — Yoast SEO описание
	$description = get_post_meta( 2, '_yoast_wpseo_metadesc', true );
	$description = $description ?: get_post_meta( 2, 'rank_math_description', true );
	$description = $description ?: carbon_get_post_meta( 2, 'banner_subtitle' );
	$description = $description ?: 'Подготовка к школе для детей 5–7 лет в Щёлково. Чтение, письмо, математика, логика.';

	// has_post_thumbnail — изображение страницы (OG-совместимость)
	$image_url = '';
	if ( has_post_thumbnail( 2 ) ) {
		$image_url = get_the_post_thumbnail_url( 2, 'full' );
	}

	// pksh_level — уровень образования (about)
	$about_text = carbon_get_post_meta( 2, 'pksh_level' ) ?: 'Подготовка к школе';

	// branch_city — город для about и keywords
	$city = dekart_branch_option( 'branch_city', 'Щёлково' );

	$webpage = array(
		'@type'       => 'WebPage',
		'@id'         => $url . '#webpage',
		'url'         => $url,
		'name'        => wp_strip_all_tags( $name ),
		'description' => wp_strip_all_tags( $description ),
		'inLanguage'  => str_replace( '_', '-', determine_locale() ?: 'ru' ),
		'isPartOf'    => array(
			'@id' => home_url( '/' ) . '#website',
		),
		'breadcrumb'  => array(
			'@id' => $url . '#breadcrumb',
		),
		// mainEntity — основной курс на странице
		'mainEntity'  => array(
			'@id' => $url . '#course',
		),
		// speakable — для голосовых помощников и AI-поиска
		'speakable'   => array(
			'@type'       => 'SpeakableSpecification',
			'cssSelector' => array(
				'h1.hero-pksh__title',
				'.hero-pksh__subtitle',
			),
		),
		// about — тематика страницы (образовательная услуга)
		'about'       => array(
			'@type' => 'Thing',
			'name'  => wp_strip_all_tags( sprintf( '%s в %s', $about_text, $city ) ),
		),
		// keywords — метки для поиска
		'keywords'    => wp_strip_all_tags(
			'подготовка к школе, дошкольное образование, обучение чтению, обучение письму, '
			. 'математика для дошкольников, развитие логики, курсы для детей 5-7 лет, '
			. $city
		),
	);

	if ( $image_url ) {
		$webpage['image'] = esc_url( $image_url );
	}

	// datePublished / dateModified — даты публикации страницы
	$post_obj = get_post( 2 );
	if ( $post_obj ) {
		$webpage['datePublished'] = get_the_date( 'c', $post_obj );
		$webpage['dateModified']  = get_the_modified_date( 'c', $post_obj );
	}

	// hasPart — ссылка на FAQPage этой страницы
	$faq_items = carbon_get_post_meta( 2, 'pksh_faq_items' );
	if ( ! empty( $faq_items ) && is_array( $faq_items ) ) {
		$webpage['hasPart'] = array(
			'@id' => $url . '#faq',
		);
	}

	return $webpage;
}

/**
 * WebSite — главный сайт.
 * name из branch_name (единый источник с лагерем), а не из site_name.
 */
function dekart_schema_website(): ?array {
	$site_name = dekart_branch_option( 'branch_name', 'Детский центр «Декарт»' );
	$home_url  = home_url( '/' );

	return array(
		'@type'           => 'WebSite',
		'@id'             => $home_url . '#website',
		'url'             => $home_url,
		'name'            => wp_strip_all_tags( $site_name ),
		'potentialAction' => array(
			'@type'       => 'SearchAction',
			'target'      => array(
				'@type'       => 'EntryPoint',
				'urlTemplate' => $home_url . '?s={search_term_string}',
			),
			'query-input' => 'required name=search_term_string',
		),
	);
}

/**
 * BreadcrumbList — хлебные крошки для страницы.
 */
function dekart_schema_breadcrumb(): ?array {
	$page_url = home_url( '/podgotovka-k-shkole/' );
	$home_url = home_url( '/' );

	return array(
		'@type'           => 'BreadcrumbList',
		'@id'             => $page_url . '#breadcrumb',
		'itemListElement' => array(
			array(
				'@type'    => 'ListItem',
				'position' => 1,
				'name'     => 'Главная',
				'item'     => $home_url,
			),
			array(
				'@type'    => 'ListItem',
				'position' => 2,
				'name'     => 'Подготовка к школе',
				'item'     => $page_url,
			),
		),
	);
}

/**
 * Helper: получить значение из Carbon Fields (schema_*) с fallback на Settings API (branch_*).
 * Работает с любыми типами (включая массивы complex/media_gallery).
 * Приоритет: carbon_get_theme_option('schema_*') → get_option('branch_*') → $default.
 */
function dekart_schema_option( string $schema_key, string $branch_key, $default = '' ) {
	$val = carbon_get_theme_option( $schema_key );
	if ( ! empty( $val ) ) {
		return $val;
	}
	$val = get_option( $branch_key );
	if ( ! empty( $val ) ) {
		return $val;
	}
	return $default;
}

/**
 * Helper: получить значение из Настроек филиала (branch_*).
 * Единый источник: get_option('branch_*').
 * Схема-поля (schema_*) читать через dekart_schema_option() — CF → Settings API.
 */
function dekart_branch_option( string $key, string $default = '' ): string {
	$val = get_option( $key );
	if ( ! empty( $val ) && is_string( $val ) ) {
		return trim( $val );
	}
	return $default;
}

/**
 * Parent Organization — юридическое лицо (без адреса филиала).
 * @id совпадает с лагерем — это одна организация.
 */
function dekart_schema_organization(): ?array {
	$home_url = home_url( '/' );
	$site_name = dekart_branch_option( 'branch_name', 'Детский центр «Декарт»' );

	// Логотип: единая цепочка с page-camp.php
	$logo_url = dekart_resolve_logo();

	$org = array(
		'@type' => 'Organization',
		'@id'   => $home_url . '#organization',
		'name'  => wp_strip_all_tags( $site_name ),
		'url'   => $home_url,
		'logo'  => esc_url( $logo_url ),
	);

	// legalName — юридическое наименование (CF schema_legal_name → Settings API branch_legal_name)
	$legal_name = dekart_schema_option( 'schema_legal_name', 'branch_legal_name' );
	if ( ! empty( $legal_name ) && is_string( $legal_name ) ) {
		$org['legalName'] = wp_strip_all_tags( trim( $legal_name ) );
	}

	// contactPoint — контактный телефон для связи
	$cp_phone = dekart_branch_option( 'branch_phone', '' );
	if ( $cp_phone ) {
		$org['contactPoint'] = array(
			'@type'             => 'ContactPoint',
			'telephone'         => wp_strip_all_tags( $cp_phone ),
			'contactType'       => 'customer service',
			'areaServed'        => 'RU',
			'availableLanguage' => 'Russian',
		);
	}

	return $org;
}

/**
 * Helper: цепочка разрешения логотипа (аналог page-camp.php:92-103).
 * branch_logo (ACF) → banner_img (CF) → custom_logo (WP) → post_thumbnail → fallback SVG.
 */
function dekart_resolve_logo(): string {
	// 1. branch_logo — ACF опция филиала
	$branch_logo = get_option( 'branch_logo' );
	if ( ! empty( $branch_logo ) ) {
		if ( is_array( $branch_logo ) && isset( $branch_logo['url'] ) ) {
			return $branch_logo['url'];
		}
		$url = wp_get_attachment_url( $branch_logo );
		if ( $url ) {
			return $url;
		}
	}

	// 2. banner_img — CF поле страницы
	$banner_img = carbon_get_post_meta( 2, 'banner_img' );
	if ( ! empty( $banner_img ) ) {
		if ( is_array( $banner_img ) && isset( $banner_img['url'] ) ) {
			return $banner_img['url'];
		}
		$url = wp_get_attachment_url( $banner_img );
		if ( $url ) {
			return $url;
		}
	}

	// 3. custom_logo — WP Customizer
	$logo_id = get_theme_mod( 'custom_logo' );
	if ( $logo_id ) {
		$url = wp_get_attachment_url( $logo_id );
		if ( $url ) {
			return $url;
		}
	}

	// 4. post_thumbnail страницы
	if ( has_post_thumbnail( 2 ) ) {
		$url = get_the_post_thumbnail_url( 2, 'full' );
		if ( $url ) {
			return $url;
		}
	}

	// 5. fallback — SVG из темы
	return get_template_directory_uri() . '/assets/images/logo-svg.svg';
}

/**
 * LocalBusiness — филиал/центр с адресом, телефоном.
 * Данные из Настроек филиала (branch_*), единый источник с лагерем.
 * @id на уровне страницы, parentOrganization → #organization.
 *
 * @see https://schema.org/LocalBusiness
 * @see https://schema.org/EducationalOrganization
 */
function dekart_schema_local_business(): ?array {
	$page_url = home_url( '/podgotovka-k-shkole/' );
	$home_url = home_url( '/' );

	// branch_name — название филиала (get_option)
	$site_name = dekart_branch_option( 'branch_name', 'Детский центр «Декарт»' );
	// branch_address — улица
	$street    = dekart_branch_option( 'branch_address', 'Комсомольская ул., 11' );
	// branch_city — город
	$city      = dekart_branch_option( 'branch_city', 'Щёлково' );
	// branch_region — регион/область
	$region    = dekart_branch_option( 'branch_region', 'Московская область' );
	// branch_phone — телефон
	$phone     = dekart_branch_option( 'branch_phone', '' );
	// branch_email — email
	$email     = dekart_branch_option( 'branch_email', '' );
	// branch_coords — координаты "lat, lng" из Настроек филиала (Settings API)
	$coords    = get_option( 'branch_coords' );
	$lat       = '';
	$lng       = '';
	if ( ! empty( $coords ) && is_string( $coords ) && false !== strpos( $coords, ',' ) ) {
		$parts = explode( ',', $coords );
		if ( count( $parts ) >= 2 ) {
			$lat = trim( $parts[0] );
			$lng = trim( $parts[1] );
		}
	}
	// branch_postal_code — почтовый индекс (CF schema_postal_code → Settings API)
	$zip         = dekart_schema_option( 'schema_postal_code', 'branch_postal_code' );
	// branch_price_range — диапазон цен (Settings API)
	$price_range = get_option( 'branch_price_range' );
	// branch_founded — год основания (CF schema_founded → Settings API)
	$founded     = dekart_schema_option( 'schema_founded', 'branch_founded' );
	// branch_map_url — ссылка на карту для hasMap (CF schema_map_url → Settings API)
	$map_url     = dekart_schema_option( 'schema_map_url', 'branch_map_url' );
	// branch_hours — часы работы: CF complex (schema_hours) → textarea (branch_hours)
	$hours_cf    = carbon_get_theme_option( 'schema_hours' );

	$lb = array(
		'@type' => array( 'EducationalOrganization', 'LocalBusiness' ),
		'@id'   => $page_url . '#localbusiness',
		'name'  => wp_strip_all_tags( $site_name ),
		'url'   => $home_url,
		'address' => array(
			'@type'           => 'PostalAddress',
			'streetAddress'   => wp_strip_all_tags( $street ),
			'addressLocality' => wp_strip_all_tags( $city ),
			'addressRegion'   => wp_strip_all_tags( $region ),
			'addressCountry'  => 'RU',
		),
		'parentOrganization' => array(
			'@id' => $home_url . '#organization',
		),
	);

	// postalCode — почтовый индекс (условно)
	if ( ! empty( $zip ) && is_string( $zip ) ) {
		$lb['address']['postalCode'] = trim( $zip );
	}

	// telephone — телефон (условно)
	if ( $phone ) {
		$lb['telephone'] = wp_strip_all_tags( $phone );
	}

	// email — почта (условно)
	if ( $email ) {
		$lb['email'] = wp_strip_all_tags( $email );
	}

	// geo — GeoCoordinates (условно: обе координаты не пусты)
	if ( ! empty( $lat ) && ! empty( $lng ) ) {
		$lb['geo'] = array(
			'@type'     => 'GeoCoordinates',
			'latitude'  => floatval( $lat ),
			'longitude' => floatval( $lng ),
		);
	}

	// hasMap — ссылка на карту (условно)
	if ( ! empty( $map_url ) && is_string( $map_url ) ) {
		$lb['hasMap'] = esc_url( trim( $map_url ) );
	}

	// openingHoursSpecification — часы работы
	// Формат 1 (CF complex, schema_hours): [['day_of_week' => 'Monday', 'opens' => '09:00', 'closes' => '20:00', 'day_off' => '']]
	// Формат 2 (Settings API textarea, branch_hours): "Monday|09:00|20:00"
	$hours_spec = array();
	if ( ! empty( $hours_cf ) && is_array( $hours_cf ) ) {
		foreach ( $hours_cf as $entry ) {
			$day = isset( $entry['day_of_week'] ) ? trim( $entry['day_of_week'] ) : '';
			if ( empty( $day ) ) {
				continue;
			}
			$is_off = ! empty( $entry['day_off'] );
			if ( $is_off ) {
				$hours_spec[] = array(
					'@type'     => 'OpeningHoursSpecification',
					'dayOfWeek' => $day,
					'opens'     => '00:00',
					'closes'    => '00:00',
				);
			} else {
				$from = isset( $entry['opens'] ) ? trim( $entry['opens'] ) : '';
				$to   = isset( $entry['closes'] ) ? trim( $entry['closes'] ) : '';
				if ( $from && $to ) {
					$hours_spec[] = array(
						'@type'     => 'OpeningHoursSpecification',
						'dayOfWeek' => $day,
						'opens'     => $from,
						'closes'    => $to,
					);
				}
			}
		}
	} else {
		$hours_raw = get_option( 'branch_hours' );
		if ( ! empty( $hours_raw ) && is_string( $hours_raw ) ) {
			$lines = explode( "\n", $hours_raw );
			foreach ( $lines as $line ) {
				$line = trim( $line );
				if ( empty( $line ) ) {
					continue;
				}
				$parts = explode( '|', $line );
				$day   = isset( $parts[0] ) ? trim( $parts[0] ) : '';
				if ( empty( $day ) ) {
					continue;
				}
				if ( isset( $parts[2] ) && 'closed' === strtolower( trim( $parts[2] ) ) ) {
					$hours_spec[] = array(
						'@type'     => 'OpeningHoursSpecification',
						'dayOfWeek' => $day,
						'opens'     => '00:00',
						'closes'    => '00:00',
					);
					continue;
				}
				$from = isset( $parts[1] ) ? trim( $parts[1] ) : '';
				$to   = isset( $parts[2] ) ? trim( $parts[2] ) : '';
				if ( $day && $from && $to ) {
					$hours_spec[] = array(
						'@type'     => 'OpeningHoursSpecification',
						'dayOfWeek' => $day,
						'opens'     => $from,
						'closes'    => $to,
					);
				}
			}
		}
	}
	if ( ! empty( $hours_spec ) ) {
		$lb['openingHoursSpecification'] = $hours_spec;
	}

	// priceRange — строка диапазона цен (условно)
	if ( ! empty( $price_range ) && is_string( $price_range ) ) {
		$lb['priceRange'] = trim( $price_range );
	}

	// areaServed — город присутствия (условно)
	$lb['areaServed'] = array(
		'@type' => 'City',
		'name'  => wp_strip_all_tags( $city ),
	);

	// foundingDate — год основания (условно)
	if ( ! empty( $founded ) && is_string( $founded ) ) {
		$lb['foundingDate'] = trim( $founded );
	}

	// sameAs — массив URL соцсетей
	// Формат 1 (CF schema_social complex): [['platform' => 'VK', 'url' => 'https://...']]
	// Формат 2 (Settings API branch_social): [['name' => 'VK', 'url' => 'https://...']]
	$social_cf = carbon_get_theme_option( 'schema_social' );
	$same_as   = array();
	if ( ! empty( $social_cf ) && is_array( $social_cf ) ) {
		foreach ( $social_cf as $entry ) {
			$url = isset( $entry['url'] ) ? trim( $entry['url'] ) : '';
			if ( $url && filter_var( $url, FILTER_VALIDATE_URL ) ) {
				$same_as[] = esc_url( $url );
			}
		}
	} else {
		$social_raw = get_option( 'branch_social' );
		if ( ! empty( $social_raw ) && is_array( $social_raw ) ) {
			foreach ( $social_raw as $entry ) {
				$url = isset( $entry['url'] ) ? trim( $entry['url'] ) : '';
				if ( $url && filter_var( $url, FILTER_VALIDATE_URL ) ) {
					$same_as[] = esc_url( $url );
				}
			}
		}
	}
	if ( ! empty( $same_as ) ) {
		$lb['sameAs'] = $same_as;
	}

	// image — массив URL фотографий филиала
	// Формат 1 (CF schema_photos media_gallery): [123, 456]
	// Формат 2 (Settings API branch_photos): "123, 456"
	$photos_cf = carbon_get_theme_option( 'schema_photos' );
	$images    = array();
	if ( ! empty( $photos_cf ) && is_array( $photos_cf ) ) {
		foreach ( $photos_cf as $id ) {
			if ( ! is_numeric( $id ) || $id <= 0 ) {
				continue;
			}
			$url = wp_get_attachment_url( (int) $id );
			if ( $url ) {
				$images[] = esc_url( $url );
			}
		}
	} else {
		$photos_raw = get_option( 'branch_photos' );
		if ( ! empty( $photos_raw ) && is_string( $photos_raw ) ) {
			$ids = explode( ',', $photos_raw );
			foreach ( $ids as $id ) {
				$id = trim( $id );
				if ( ! is_numeric( $id ) || $id <= 0 ) {
					continue;
				}
				$url = wp_get_attachment_url( (int) $id );
				if ( $url ) {
					$images[] = esc_url( $url );
				}
			}
		}
	}
	if ( ! empty( $images ) ) {
		$lb['image'] = $images;
	}

	// employee — преподаватели (Person), если есть
	$teachers = carbon_get_post_meta( 2, 'pksh_teachers' );
	if ( ! empty( $teachers ) && is_array( $teachers ) ) {
		$employee_ids = array();
		foreach ( $teachers as $i => $t ) {
			$name = isset( $t['teacher_name'] ) ? trim( $t['teacher_name'] ) : '';
			if ( $name ) {
				$employee_ids[] = array(
					'@id' => $page_url . '#teacher-' . $i,
				);
			}
		}
		if ( ! empty( $employee_ids ) ) {
			$lb['employee'] = $employee_ids;
		}
	}

	return $lb;
}

/**
 * Course — основной курс «Подготовка к школе».
 * Данные из Carbon Fields: banner_h1, banner_subtitle.
 *
 * @see https://schema.org/Course
 * @see https://schema.org/CourseInstance
 */
function dekart_schema_course(): ?array {
	$page_url    = home_url( '/podgotovka-k-shkole/' );
	$home_url    = home_url( '/' );

	// banner_h1 — заголовок H1 (Course.name)
	$name        = carbon_get_post_meta( 2, 'banner_h1' ) ?: 'Подготовка к школе 5–7 лет в Щёлково';
	// pksh_description — полное описание курса для микроразметки (textarea)
	// Если заполнено — приоритет над banner_subtitle
	$pksh_description = carbon_get_post_meta( 2, 'pksh_description' );
	// banner_subtitle — подзаголовок (Course.description)
	$description = ! empty( $pksh_description ) ? $pksh_description : ( carbon_get_post_meta( 2, 'banner_subtitle' ) ?: 'Научим читать, считать и писать до школы. Бесплатное пробное занятие.' );
	// Пробное занятие — есть ли в подзаголовке упоминание бесплатно
	$has_trial   = ! empty( $description ) && false !== mb_stripos( $description, 'бесплатно' );
	// pksh_level — уровень образования
	$level       = carbon_get_post_meta( 2, 'pksh_level' );
	// pksh_age — возраст
	$age         = carbon_get_post_meta( 2, 'pksh_age' );
	// pksh_audience — целевая аудитория
	$audience    = carbon_get_post_meta( 2, 'pksh_audience' );
	// pksh_course_mode — формат обучения (очная/ONLINE/OFFLINE)
	$course_mode = carbon_get_post_meta( 2, 'pksh_course_mode' );
	// pksh_skills — список навыков
	$skills      = carbon_get_post_meta( 2, 'pksh_skills' );
	// banner_img — изображение курса
	$course_img  = carbon_get_post_meta( 2, 'banner_img' );
	// pksh_programs — программы (для timeRequired + CourseInstance)
	$programs    = carbon_get_post_meta( 2, 'pksh_programs' );

	$course = array(
		'@type'         => 'Course',
		'@id'           => $page_url . '#course',
		'name'          => wp_strip_all_tags( $name ),
		'description'   => wp_strip_all_tags( $description ),
		'inLanguage'    => str_replace( '_', '-', determine_locale() ?: 'ru' ),
		'provider'      => array(
			'@id' => $page_url . '#localbusiness',
		),
		'offers'        => array_merge(
			array( array( '@id' => $page_url . '#offer' ) ),
			$has_trial ? array( array( '@id' => $page_url . '#offer-trial' ) ) : array()
		),
		'aggregateRating' => array(
			'@id' => $page_url . '#rating',
		),
	);

	// educationalLevel — уровень образования (условно)
	if ( ! empty( $level ) && is_string( $level ) ) {
		$course['educationalLevel'] = wp_strip_all_tags( trim( $level ) );
	}

	// audience — EducationalAudience (условно)
	if ( ! empty( $audience ) && is_string( $audience ) ) {
		$course['audience'] = array(
			'@type' => 'EducationalAudience',
			'name'  => wp_strip_all_tags( trim( $audience ) ),
		);
	}

	// teaches — массив навыков (условно, из pksh_skills complex)
	if ( ! empty( $skills ) && is_array( $skills ) ) {
		$teaches = array();
		foreach ( $skills as $skill ) {
			$name = isset( $skill['skill'] ) ? trim( $skill['skill'] ) : '';
			if ( $name ) {
				$teaches[] = array(
					'@type' => 'Thing',
					'name'  => wp_strip_all_tags( $name ),
				);
			}
		}
		if ( ! empty( $teaches ) ) {
			$course['teaches'] = $teaches;
		}
	}

	// timeRequired — длительность курса из первой программы, где заполнено
	if ( ! empty( $programs ) && is_array( $programs ) ) {
		foreach ( $programs as $prog ) {
			if ( ! empty( $prog['program_time_required'] ) ) {
				$course['timeRequired'] = wp_strip_all_tags( trim( $prog['program_time_required'] ) );
				break;
			}
		}
	}

	// image — изображение курса (условно, из banner_img)
	if ( ! empty( $course_img ) ) {
		if ( is_array( $course_img ) && isset( $course_img['url'] ) ) {
			$course['image'] = esc_url( $course_img['url'] );
		} else {
			$url = wp_get_attachment_url( $course_img );
			if ( $url ) {
				$course['image'] = esc_url( $url );
			}
		}
	} elseif ( has_post_thumbnail( 2 ) ) {
		// fallback: post_thumbnail страницы
		$url = get_the_post_thumbnail_url( 2, 'full' );
		if ( $url ) {
			$course['image'] = esc_url( $url );
		}
	}

	// hasCourseInstance — по одной CourseInstance на каждую программу с данными
	$instances = array();
	if ( ! empty( $programs ) && is_array( $programs ) ) {
		foreach ( $programs as $prog ) {
			$prog_workload  = isset( $prog['program_workload'] ) ? trim( $prog['program_workload'] ) : '';
			$prog_intensity = isset( $prog['program_intensity'] ) ? trim( $prog['program_intensity'] ) : '';
			$prog_start     = isset( $prog['program_start_date'] ) ? trim( $prog['program_start_date'] ) : '';
			$prog_end       = isset( $prog['program_end_date'] ) ? trim( $prog['program_end_date'] ) : '';

			if ( empty( $prog_workload ) && empty( $prog_intensity ) && empty( $prog_start ) && empty( $prog_end ) ) {
				continue; // нет данных для CourseInstance
			}

			$instance = array(
				'@type'     => 'CourseInstance',
				'courseMode' => 'Onsite',
				'location'  => array(
					'@id' => $page_url . '#localbusiness',
				),
			);

			// courseMode — формат обучения (глобальный, единый для всех программ)
			// Допустимые Schema.org: Onsite, Online, Blended
			if ( ! empty( $course_mode ) && is_string( $course_mode ) ) {
				$cm = strtolower( trim( $course_mode ) );
				// Нормализация legacy-значений
				$mode_map = array(
					'offline' => 'Onsite',
					'online'  => 'Online',
					'очная'   => 'Onsite',
					'очно'    => 'Onsite',
				);
				$cm_normalized = isset( $mode_map[ $cm ] ) ? $mode_map[ $cm ] : ucfirst( $cm );
				$valid_modes   = array( 'Onsite', 'Online', 'Blended' );
				if ( in_array( $cm_normalized, $valid_modes, true ) ) {
					$instance['courseMode'] = $cm_normalized;
				}
			}

			// courseSchedule — расписание (Schedule)
			if ( $prog_workload || $prog_intensity || $prog_start || $prog_end ) {
				$schedule = array( '@type' => 'Schedule' );
				// repeatFrequency — ISO 8601 из program_repeat_frequency (новое поле)
				$repeat_freq = isset( $prog['program_repeat_frequency'] ) ? trim( $prog['program_repeat_frequency'] ) : '';
				if ( $repeat_freq ) {
					$schedule['repeatFrequency'] = wp_strip_all_tags( $repeat_freq );
				} elseif ( $prog_intensity ) {
					// fallback: program_intensity — только если нет ISO-поля
					$schedule['repeatFrequency'] = wp_strip_all_tags( $prog_intensity );
				}
				if ( $prog_start ) {
					$schedule['startDate'] = $prog_start;
				}
				if ( $prog_end ) {
					$schedule['endDate'] = $prog_end;
				}
				$instance['courseSchedule'] = $schedule;
			}

			// courseWorkload — учебная нагрузка
			if ( $prog_workload ) {
				$instance['courseWorkload'] = wp_strip_all_tags( $prog_workload );
			}

			$instances[] = $instance;
		}
	}

	// fallback: если нет программ с данными, но есть courseMode — одна общая CourseInstance
	if ( empty( $instances ) && ! empty( $course_mode ) && is_string( $course_mode ) ) {
		$cm = strtolower( trim( $course_mode ) );
		$mode_map = array(
			'offline' => 'Onsite',
			'online'  => 'Online',
			'очная'   => 'Onsite',
			'очно'    => 'Onsite',
		);
		$cm_normalized = isset( $mode_map[ $cm ] ) ? $mode_map[ $cm ] : ucfirst( $cm );
		$valid_modes   = array( 'Onsite', 'Online', 'Blended' );
		$instances[] = array(
			'@type'      => 'CourseInstance',
			'courseMode' => ( in_array( $cm_normalized, $valid_modes, true ) ) ? $cm_normalized : 'Onsite',
			'location'   => array(
				'@id' => $page_url . '#localbusiness',
			),
		);
	}

	if ( ! empty( $instances ) ) {
		$course['hasCourseInstance'] = $instances;
	}

	return $course;
}

/**
 * AggregateOffer — цены программ + бесплатное пробное.
 * Извлекает числовые значения из program_price_subscription / program_price_single.
 * Если программ нет — возвращает Offer на бесплатное пробное.
 * Если есть программы — возвращает AggregateOffer + отдельный Offer для free trial.
 *
 * @see https://schema.org/AggregateOffer
 * @see https://schema.org/Offer
 */
function dekart_schema_offer(): ?array {
	$page_url = home_url( '/podgotovka-k-shkole/' );
	$programs = carbon_get_post_meta( 2, 'pksh_programs' );

	// priceValidUntil — самая поздняя дата из всех программ
	$price_valid = '';
	if ( ! empty( $programs ) && is_array( $programs ) ) {
		foreach ( $programs as $prog ) {
			if ( ! empty( $prog['program_price_valid'] ) ) {
				$d = trim( $prog['program_price_valid'] );
				if ( $d > $price_valid ) {
					$price_valid = $d;
				}
			}
		}
	}

	$prices = array();

	if ( ! empty( $programs ) && is_array( $programs ) ) {
		foreach ( $programs as $program ) {
			// program_price_subscription — цена абонемента
			if ( ! empty( $program['program_price_subscription'] ) ) {
				$num = dekart_parse_price( $program['program_price_subscription'] );
				if ( $num > 0 ) {
					$prices[] = $num;
				}
			}
			// program_price_single — цена разового занятия
			if ( ! empty( $program['program_price_single'] ) ) {
				$num = dekart_parse_price( $program['program_price_single'] );
				if ( $num > 0 ) {
					$prices[] = $num;
				}
			}
		}
	}

	if ( empty( $prices ) ) {
		// Нет цен в программах — Offer на бесплатное пробное
		$offer = array(
			'@type'         => 'Offer',
			'@id'           => $page_url . '#offer',
			'price'         => '0',
			'priceCurrency' => 'RUB',
			'availability'  => 'https://schema.org/LimitedAvailability',
			'description'   => 'Бесплатное пробное занятие',
		);

		// priceValidUntil — дата действия (условно)
		if ( ! empty( $price_valid ) ) {
			$offer['priceValidUntil'] = trim( $price_valid );
		}

		return $offer;
	}

	$low  = min( $prices );
	$high = max( $prices );

	$aggregate = array(
		'@type'         => 'AggregateOffer',
		'@id'           => $page_url . '#offer',
		'priceCurrency' => 'RUB',
		'lowPrice'      => (string) $low,
		'highPrice'     => (string) $high,
		'offerCount'    => (string) count( $prices ),
		'availability'  => 'https://schema.org/InStock',
		// category — категория услуги
		'category'      => 'Образовательные услуги',
		// offeredBy — кто предоставляет
		'offeredBy'     => array(
			'@id' => $page_url . '#localbusiness',
		),
	);

	// priceValidUntil — дата действия цен (условно)
	if ( ! empty( $price_valid ) ) {
		$aggregate['priceValidUntil'] = trim( $price_valid );
	}

	// Возвращаем массив: сначала AggregateOffer, потом (опционально) Offer на бесплатное пробное
	$offers = array( $aggregate );

	// banner_subtitle содержит фразу "бесплатное пробное" — создаём отдельный Offer
	$subtitle = carbon_get_post_meta( 2, 'banner_subtitle' );
	if ( ! empty( $subtitle ) && false !== mb_stripos( $subtitle, 'бесплатно' ) ) {
		$trial = array(
			'@type'         => 'Offer',
			'@id'           => $page_url . '#offer-trial',
			'price'         => '0',
			'priceCurrency' => 'RUB',
			'availability'  => 'https://schema.org/LimitedAvailability',
			'description'   => wp_strip_all_tags( $subtitle ),
			'category'      => 'Бесплатное пробное занятие',
			'offeredBy'     => array(
				'@id' => $page_url . '#localbusiness',
			),
		);
		if ( ! empty( $price_valid ) ) {
			$trial['priceValidUntil'] = trim( $price_valid );
		}
		$offers[] = $trial;
	}

	return $offers;
}

/**
 * AggregateRating — рейтинг из отзывов Carbon Fields.
 * Вычисляется динамически из pksh_reviews_list.
 * Если отзывов < 1 — блок не выводится.
 */
function dekart_schema_rating(): ?array {
	$page_url = home_url( '/podgotovka-k-shkole/' );
	$reviews  = carbon_get_post_meta( 2, 'pksh_reviews_list' );

	if ( empty( $reviews ) || ! is_array( $reviews ) ) {
		return null;
	}

	$ratings = array();
	foreach ( $reviews as $review ) {
		$rating = isset( $review['review_rating'] ) ? intval( $review['review_rating'] ) : 0;
		if ( $rating >= 1 && $rating <= 5 ) {
			$ratings[] = $rating;
		}
	}

	if ( count( $ratings ) < 1 ) {
		return null;
	}

	$average = round( array_sum( $ratings ) / count( $ratings ), 1 );

	return array(
		'@type'        => 'AggregateRating',
		'@id'          => $page_url . '#rating',
		'ratingValue'  => (string) $average,
		'bestRating'   => '5',
		'worstRating'  => '1',
		'ratingCount'  => (string) count( $ratings ),
		'itemReviewed'  => array(
			'@id' => $page_url . '#course',
		),
	);
}

/**
 * FAQPage — вопросы и ответы из pksh_faq_items.
 * Если вопросов нет — блок не выводится.
 */
function dekart_schema_faq(): ?array {
	$page_url = home_url( '/podgotovka-k-shkole/' );
	$faq_items = carbon_get_post_meta( 2, 'pksh_faq_items' );

	if ( empty( $faq_items ) || ! is_array( $faq_items ) ) {
		return null;
	}

	$main_entity = array();
	foreach ( $faq_items as $item ) {
		$question = isset( $item['faq_question'] ) ? trim( $item['faq_question'] ) : '';
		$answer   = isset( $item['faq_answer'] ) ? trim( $item['faq_answer'] ) : '';

		if ( empty( $question ) || empty( $answer ) ) {
			continue;
		}

		$main_entity[] = array(
			'@type'          => 'Question',
			'name'           => wp_strip_all_tags( $question ),
			'acceptedAnswer' => array(
				'@type' => 'Answer',
				'text'  => wp_strip_all_tags( $answer ),
			),
		);
	}

	if ( empty( $main_entity ) ) {
		return null;
	}

	return array(
		'@type'      => 'FAQPage',
		'@id'        => $page_url . '#faq',
		'mainEntity' => $main_entity,
	);
}

/**
 * Review[] — поштучные отзывы из pksh_reviews_list.
 * Каждый отзыв — отдельная сущность Review с автор-персоной и оценкой.
 * itemReviewed → Course. Фильтр некорректных оценок (>5 или <1).
 *
 * @return array[] Массив Review-сущностей для @graph.
 * @see https://schema.org/Review
 */
function dekart_schema_reviews(): array {
	$page_url = home_url( '/podgotovka-k-shkole/' );
	// pksh_reviews_list — complex-поле отзывов
	$reviews  = carbon_get_post_meta( 2, 'pksh_reviews_list' );

	if ( empty( $reviews ) || ! is_array( $reviews ) ) {
		return array();
	}

	$result = array();
	$review_seq = 0;
	foreach ( $reviews as $i => $review ) {
		// review_author — имя автора (Person.name)
		$author_name = isset( $review['review_author'] ) ? trim( $review['review_author'] ) : '';
		// review_text — текст отзыва
		$body        = isset( $review['review_text'] ) ? trim( $review['review_text'] ) : '';
		// review_rating — оценка 1-5
		$rating_val  = isset( $review['review_rating'] ) ? intval( $review['review_rating'] ) : 0;

		// Пропускаем без автора или текста
		if ( empty( $author_name ) || empty( $body ) ) {
			continue;
		}

		// Фильтруем оценки вне диапазона 1-5
		if ( $rating_val < 1 || $rating_val > 5 ) {
			continue;
		}

		$review_seq++;

		$entity = array(
			'@type'         => 'Review',
			'@id'           => $page_url . '#review-' . $review_seq,
			'author'        => array(
				'@type' => 'Person',
				'name'  => wp_strip_all_tags( $author_name ),
			),
			'reviewBody'    => wp_strip_all_tags( $body ),
			'itemReviewed'  => array(
				'@id' => $page_url . '#course',
			),
			'reviewRating'  => array(
				'@type'       => 'Rating',
				'ratingValue' => (string) $rating_val,
				'bestRating'  => '5',
				'worstRating' => '1',
			),
		);

		// datePublished — дата отзыва (условно, из review_date)
		$review_date = isset( $review['review_date'] ) ? trim( $review['review_date'] ) : '';
		if ( $review_date ) {
			$entity['datePublished'] = sanitize_text_field( $review_date );
		}

		$result[] = $entity;
	}

	return $result;
}

/**
 * Person[] — преподаватели из pksh_teachers complex.
 * Каждый преподаватель — отдельная сущность Person.
 * Связаны с LocalBusiness через employee @id.
 *
 * @return array[] Массив Person-сущностей для @graph.
 * @see https://schema.org/Person
 */
function dekart_schema_teachers(): array {
	$page_url = home_url( '/podgotovka-k-shkole/' );
	// pksh_teachers — complex-поле преподавателей
	$teachers = carbon_get_post_meta( 2, 'pksh_teachers' );

	if ( empty( $teachers ) || ! is_array( $teachers ) ) {
		return array();
	}

	$result = array();
	foreach ( $teachers as $i => $teacher ) {
		// teacher_name — ФИО преподавателя
		$name    = isset( $teacher['teacher_name'] ) ? trim( $teacher['teacher_name'] ) : '';
		// teacher_subject — предмет / должность
		$subject = isset( $teacher['teacher_subject'] ) ? trim( $teacher['teacher_subject'] ) : '';
		// teacher_about — описание / биография
		$about   = isset( $teacher['teacher_about'] ) ? trim( $teacher['teacher_about'] ) : '';
		// teacher_photo — фото (attachment ID или массив)
		$photo   = $teacher['teacher_photo'] ?? 0;

		if ( empty( $name ) ) {
			continue;
		}

		$person = array(
			'@type' => 'Person',
			'@id'   => $page_url . '#teacher-' . $i,
			'name'  => wp_strip_all_tags( $name ),
		);

		// jobTitle — должность (условно)
		if ( ! empty( $subject ) ) {
			$person['jobTitle'] = wp_strip_all_tags( $subject );
		}

		// description — о преподавателе (условно)
		if ( ! empty( $about ) ) {
			$person['description'] = wp_strip_all_tags( $about );
		}

		// image — фото преподавателя (условно)
		if ( ! empty( $photo ) ) {
			if ( is_array( $photo ) && isset( $photo['url'] ) ) {
				$person['image'] = esc_url( $photo['url'] );
			} else {
				$url = wp_get_attachment_url( $photo );
				if ( $url ) {
					$person['image'] = esc_url( $url );
				}
			}
		}

		$result[] = $person;
	}

	return $result;
}

/* =====================================================================
   Вспомогательные функции
   ===================================================================== */

/**
 * Парсит цену из форматированной строки (например "7 500 ₽" → 7500).
 * Поддерживает "1 234,56" (comma-decimal) и "1,234.56" (dot-decimal).
 *
 * @param  string $raw Строка с ценой.
 * @return float       Числовое значение.
 */
function dekart_parse_price( string $raw ): float {
	// Удаляем всё кроме цифр, точки, запятой
	$clean = preg_replace( '/[^0-9.,]/', '', $raw );

	// Определяем формат: если последний разделитель — запятая → comma-decimal
	$last_comma = strrpos( $clean, ',' );
	$last_dot   = strrpos( $clean, '.' );

	if ( false !== $last_comma && ( false === $last_dot || $last_comma > $last_dot ) ) {
		// Европейский формат: 1.234,56 → убираем точки, меняем запятую на точку
		$clean = str_replace( '.', '', $clean );
		$clean = str_replace( ',', '.', $clean );
	} else {
		// Стандартный/русский: убираем всё кроме последней точки
		$clean = str_replace( ',', '', $clean );
		$clean = preg_replace( '/\.(?=.*\.)/', '', $clean );
	}

	return floatval( $clean );
}
