<?php
/**
 * Custom Post Type registration.
 *
 * @package Vachr Short-Term Rental Reservations
 * @license GPLv2 or later
 */
if (!defined('ABSPATH')) exit;

class VSTRB_CPT {
	// CPT metabox rendering/saving uses custom plugin table reads/writes.
	// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
	private static function add_inline_admin_script($script) {
		if (!is_string($script) || trim($script) === '') {
			return;
		}
		wp_enqueue_script('vstrb-admin');
		$script = preg_replace('#</?script[^>]*>#i', '', $script);
		wp_add_inline_script('vstrb-admin', trim((string) $script));
	}

	private static function post_array($key) {
		$value = filter_input(INPUT_POST, $key, FILTER_SANITIZE_FULL_SPECIAL_CHARS, ['flags' => FILTER_REQUIRE_ARRAY]);
		if (!is_array($value)) {
			return array();
		}
		return (array) $value;
	}

	public static function get_service_pricing_types() {
		return [
			'per_stay' => esc_html__('per stay', 'vachr-short-term-rental-reservations'),
			'per_night' => esc_html__('per night', 'vachr-short-term-rental-reservations'),
			'per_person' => esc_html__('per person', 'vachr-short-term-rental-reservations'),
			'per_person_night' => esc_html__('per person/night', 'vachr-short-term-rental-reservations'),
			'free' => esc_html__('free (service)', 'vachr-short-term-rental-reservations'),
		];
	}

	public static function register() {
		register_post_type('vstrb_unit', [
			'labels' => [
				'name' => esc_html__('Apartments', 'vachr-short-term-rental-reservations'),
				'singular_name' => esc_html__('Apartment', 'vachr-short-term-rental-reservations'),
				'add_new_item' => esc_html__('Add Apartment', 'vachr-short-term-rental-reservations'),
				'edit_item' => esc_html__('Edit Apartment', 'vachr-short-term-rental-reservations'),
			],
			'public' => false,
			'show_ui' => true,
			'menu_icon' => 'dashicons-building',
			'supports' => ['thumbnail'],
			'has_archive' => false,
			'show_in_rest' => true,
		]);

		// Taxonomy for amenities
		register_taxonomy('vstrb_amenity', ['vstrb_unit'], [
			'labels' => [
				'name' => esc_html__('Amenities', 'vachr-short-term-rental-reservations'),
				'singular_name' => esc_html__('Amenity', 'vachr-short-term-rental-reservations'),
			],
			'public' => false,
			'show_ui' => false,
			'hierarchical' => false,
			'show_in_rest' => true,
		]);

		add_action('add_meta_boxes', [__CLASS__, 'metaboxes']);
		add_action('save_post_vstrb_unit', [__CLASS__, 'save_meta'], 10, 2);

		// Custom columns
		add_filter('manage_vstrb_unit_posts_columns', [__CLASS__, 'columns_head']);
		add_action('manage_vstrb_unit_posts_custom_column', [__CLASS__, 'columns_content'], 10, 2);
		add_filter('post_row_actions', [__CLASS__, 'row_actions'], 10, 2);
		add_action('admin_action_vstrb_clone_unit', [__CLASS__, 'handle_clone_unit']);
		add_action('admin_menu', [__CLASS__, 'register_page_wizard']);
		add_action('admin_post_vstrb_create_unit_page', [__CLASS__, 'handle_create_unit_page']);
		add_action('admin_notices', [__CLASS__, 'admin_notices']);
		add_action('admin_footer', [__CLASS__, 'render_table_footer']);
		add_action('load-post-new.php', [__CLASS__, 'redirect_new_unit_to_wizard']);
		add_filter('redirect_post_location', [__CLASS__, 'redirect_after_wizard_save'], 10, 2);
		add_filter('postbox_classes_vstrb_unit_vstrb_unit_advanced', [__CLASS__, 'advanced_metabox_classes']);
	}

	public static function redirect_new_unit_to_wizard() {
		$post_type = isset($_GET['post_type']) ? sanitize_key(wp_unslash($_GET['post_type'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$wizard = isset($_GET['vstrb_wizard']) ? sanitize_key(wp_unslash($_GET['vstrb_wizard'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ($post_type !== 'vstrb_unit' || $wizard === '1') {
			return;
		}

		wp_safe_redirect(add_query_arg([
			'post_type' => 'vstrb_unit',
			'vstrb_wizard' => '1',
		], admin_url('post-new.php')));
		exit;
	}

	public static function redirect_after_wizard_save($location, $post_id) {
		$wizard_complete = filter_input(INPUT_POST, 'vstrb_wizard_complete', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
		if ((string) $wizard_complete !== '1' || get_post_type($post_id) !== 'vstrb_unit') {
			return $location;
		}
		if (
			!isset($_POST['vstrb_unit_meta_nonce']) ||
			!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['vstrb_unit_meta_nonce'])), 'vstrb_unit_meta')
		) {
			return $location;
		}

		return admin_url('edit.php?post_type=vstrb_unit');
	}

	public static function advanced_metabox_classes($classes) {
		$classes = is_array($classes) ? $classes : [];
		if (!in_array('closed', $classes, true)) {
			$classes[] = 'closed';
		}
		return $classes;
	}

	public static function row_actions($actions, $post) {
		if (!$post || $post->post_type !== 'vstrb_unit' || !current_user_can('edit_post', $post->ID)) {
			return $actions;
		}

		$clone_url = wp_nonce_url(
			admin_url('admin.php?action=vstrb_clone_unit&post=' . (int) $post->ID),
			'vstrb_clone_unit_' . (int) $post->ID
		);
		$actions['vstrb_clone'] = '<a href="' . esc_url($clone_url) . '" aria-label="' . esc_attr__('Clone Apartment', 'vachr-short-term-rental-reservations') . '">' . esc_html__('Clone', 'vachr-short-term-rental-reservations') . '</a>';
		$actions['vstrb_create_page'] = '<a href="' . esc_url(add_query_arg([
			'post_type' => 'vstrb_unit',
			'page' => 'vstrb-create-unit-page',
			'unit_id' => (int) $post->ID,
		], admin_url('edit.php'))) . '">' . esc_html__('Create apartment page', 'vachr-short-term-rental-reservations') . '</a>';

		return $actions;
	}

	public static function register_page_wizard() {
		add_submenu_page(
			'edit.php?post_type=vstrb_unit',
			esc_html__('Create apartment page', 'vachr-short-term-rental-reservations'),
			esc_html__('Create apartment page', 'vachr-short-term-rental-reservations'),
			'edit_pages',
			'vstrb-create-unit-page',
			[__CLASS__, 'render_page_wizard']
		);
	}

	private static function page_wizard_languages() {
		$languages = array_map('trim', explode(',', (string) get_option('vstrb_available_languages', 'cs,en,sk,de,pl,fr,es,it,nl,pt,ru,uk,ja,zh')));
		return array_values(array_unique(array_filter(array_map(function($language) {
			$language = str_replace('-', '_', sanitize_text_field($language));
			return preg_match('/^[a-z]{2,3}(?:_[A-Z]{2})?$/i', $language) ? $language : '';
		}, $languages))));
	}

	private static function page_wizard_currencies() {
		$currencies = array_map('trim', explode(',', (string) get_option('vstrb_available_currencies', 'EUR')));
		return array_values(array_unique(array_filter(array_map(function($currency) {
			$currency = strtoupper(sanitize_text_field($currency));
			return preg_match('/^[A-Z]{3}$/', $currency) ? $currency : '';
		}, $currencies))));
	}

	public static function render_page_wizard() {
		if (!current_user_can('edit_pages')) {
			wp_die(esc_html__('You are not allowed to create pages.', 'vachr-short-term-rental-reservations'));
		}
		$selected_unit = isset($_GET['unit_id']) ? absint($_GET['unit_id']) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$units = get_posts([
			'post_type' => 'vstrb_unit',
			'post_status' => ['publish', 'draft', 'private'],
			'numberposts' => -1,
			'orderby' => 'title',
			'order' => 'ASC',
		]);
		$languages = self::page_wizard_languages();
		$currencies = self::page_wizard_currencies();
		?>
		<div class="wrap vstrb-page-wizard-wrap">
			<h1><?php esc_html_e('Create apartment page', 'vachr-short-term-rental-reservations'); ?></h1>
			<p><?php esc_html_e('Create an editable WordPress page from a prepared apartment layout. You can finish the text and images in Gutenberg afterwards.', 'vachr-short-term-rental-reservations'); ?></p>
			<?php if (!$units) : ?>
				<div class="notice notice-warning inline"><p><?php esc_html_e('Create an apartment before creating its page.', 'vachr-short-term-rental-reservations'); ?></p></div>
			<?php else : ?>
			<form class="vstrb-page-wizard" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
				<input type="hidden" name="action" value="vstrb_create_unit_page">
				<?php wp_nonce_field('vstrb_create_unit_page', 'vstrb_create_unit_page_nonce'); ?>
				<ol class="vstrb-page-wizard__progress" aria-label="<?php esc_attr_e('Page creation progress', 'vachr-short-term-rental-reservations'); ?>">
					<li class="is-active" data-vstrb-page-progress="0"><span>1</span><?php esc_html_e('Apartment', 'vachr-short-term-rental-reservations'); ?></li>
					<li data-vstrb-page-progress="1"><span>2</span><?php esc_html_e('Language and currency', 'vachr-short-term-rental-reservations'); ?></li>
					<li data-vstrb-page-progress="2"><span>3</span><?php esc_html_e('Page layout', 'vachr-short-term-rental-reservations'); ?></li>
				</ol>
				<section data-vstrb-page-step="0">
					<h2><?php esc_html_e('Choose an apartment', 'vachr-short-term-rental-reservations'); ?></h2>
					<p><label><strong><?php esc_html_e('Apartment', 'vachr-short-term-rental-reservations'); ?></strong><br><select name="unit_id" required class="regular-text" data-vstrb-page-unit>
						<option value=""><?php esc_html_e('Select an apartment', 'vachr-short-term-rental-reservations'); ?></option>
						<?php foreach ($units as $unit) : ?><option value="<?php echo (int) $unit->ID; ?>" data-title="<?php echo esc_attr($unit->post_title); ?>" <?php selected($selected_unit, $unit->ID); ?>><?php echo esc_html($unit->post_title); ?></option><?php endforeach; ?>
					</select></label></p>
					<p><label><strong><?php esc_html_e('Page title', 'vachr-short-term-rental-reservations'); ?></strong><br><input type="text" name="page_title" class="regular-text" required data-vstrb-page-title></label></p>
				</section>
				<section data-vstrb-page-step="1" hidden>
					<h2><?php esc_html_e('Choose language and currency', 'vachr-short-term-rental-reservations'); ?></h2>
					<div class="vstrb-page-wizard__fields"><p><label><strong><?php esc_html_e('Language', 'vachr-short-term-rental-reservations'); ?></strong><br><select name="language" required><?php foreach ($languages as $language) : ?><option value="<?php echo esc_attr($language); ?>"><?php echo esc_html(strtoupper($language)); ?></option><?php endforeach; ?></select></label></p>
					<p><label><strong><?php esc_html_e('Currency', 'vachr-short-term-rental-reservations'); ?></strong><br><select name="currency" required><?php foreach ($currencies as $currency) : ?><option value="<?php echo esc_attr($currency); ?>"><?php echo esc_html($currency); ?></option><?php endforeach; ?></select></label></p></div>
					<p class="description"><?php esc_html_e('The selected values will be inserted into the booking, calendar, description, and amenities shortcodes.', 'vachr-short-term-rental-reservations'); ?></p>
				</section>
				<section data-vstrb-page-step="2" hidden>
					<h2><?php esc_html_e('Choose a starting layout', 'vachr-short-term-rental-reservations'); ?></h2>
					<div class="vstrb-layout-legend"><span class="is-gallery"><?php esc_html_e('Photos', 'vachr-short-term-rental-reservations'); ?></span><span class="is-calendar"><?php esc_html_e('Calendar', 'vachr-short-term-rental-reservations'); ?></span><span class="is-text"><?php esc_html_e('Description', 'vachr-short-term-rental-reservations'); ?></span><span class="is-list"><?php esc_html_e('Amenities', 'vachr-short-term-rental-reservations'); ?></span><span class="is-form"><?php esc_html_e('Booking', 'vachr-short-term-rental-reservations'); ?></span></div>
					<div class="vstrb-page-layouts">
						<label><input type="radio" name="layout" value="calendar_first" checked><span><strong><?php esc_html_e('Calendar first', 'vachr-short-term-rental-reservations'); ?></strong><span class="vstrb-layout-preview vstrb-layout-preview--calendar" aria-hidden="true"><i class="is-gallery"></i><i class="is-calendar"></i><i class="is-text"></i><i class="is-form"></i></span><small><?php esc_html_e('Photos, availability calendar, description, amenities, and booking form.', 'vachr-short-term-rental-reservations'); ?></small></span></label>
						<label><input type="radio" name="layout" value="description_first"><span><strong><?php esc_html_e('Description first', 'vachr-short-term-rental-reservations'); ?></strong><span class="vstrb-layout-preview vstrb-layout-preview--description" aria-hidden="true"><i class="is-gallery"></i><i class="is-text"></i><i class="is-list"></i><i class="is-calendar"></i><i class="is-form"></i></span><small><?php esc_html_e('Photos and description lead into amenities, availability, and booking.', 'vachr-short-term-rental-reservations'); ?></small></span></label>
						<label><input type="radio" name="layout" value="booking_first"><span><strong><?php esc_html_e('Booking first', 'vachr-short-term-rental-reservations'); ?></strong><span class="vstrb-layout-preview vstrb-layout-preview--booking" aria-hidden="true"><i class="is-gallery"></i><i class="is-form"></i><i class="is-text"></i><i class="is-calendar"></i></span><small><?php esc_html_e('Lead visitors directly from photos to the booking form.', 'vachr-short-term-rental-reservations'); ?></small></span></label>
						<label><input type="radio" name="layout" value="columns"><span><strong><?php esc_html_e('Two-column presentation', 'vachr-short-term-rental-reservations'); ?></strong><span class="vstrb-layout-preview vstrb-layout-preview--columns" aria-hidden="true"><i class="is-gallery"></i><i class="is-text"></i><i class="is-list"></i><i class="is-calendar"></i><i class="is-form"></i></span><small><?php esc_html_e('Description and amenities side by side, followed by calendar and booking.', 'vachr-short-term-rental-reservations'); ?></small></span></label>
						<label><input type="radio" name="layout" value="gallery_booking"><span><strong><?php esc_html_e('Photos with booking panel', 'vachr-short-term-rental-reservations'); ?></strong><span class="vstrb-layout-preview vstrb-layout-preview--split" aria-hidden="true"><i class="is-gallery"></i><i class="is-form"></i><i class="is-text"></i><i class="is-calendar"></i></span><small><?php esc_html_e('Place the photo area and booking form next to each other.', 'vachr-short-term-rental-reservations'); ?></small></span></label>
						<label><input type="radio" name="layout" value="compact_grid"><span><strong><?php esc_html_e('Compact grid', 'vachr-short-term-rental-reservations'); ?></strong><span class="vstrb-layout-preview vstrb-layout-preview--grid" aria-hidden="true"><i class="is-gallery"></i><i class="is-calendar"></i><i class="is-text"></i><i class="is-form"></i></span><small><?php esc_html_e('A balanced two-column layout for shorter apartment pages.', 'vachr-short-term-rental-reservations'); ?></small></span></label>
					</div>
					<p><label><input type="checkbox" name="publish_page" value="1"> <?php esc_html_e('Publish the page immediately', 'vachr-short-term-rental-reservations'); ?></label></p>
					<p class="description"><?php esc_html_e('If left unchecked, the page will be created as a draft.', 'vachr-short-term-rental-reservations'); ?></p>
				</section>
				<div class="vstrb-page-wizard__actions"><button type="button" class="button" data-vstrb-page-prev hidden><?php esc_html_e('Previous step', 'vachr-short-term-rental-reservations'); ?></button><span></span><button type="button" class="button button-primary" data-vstrb-page-next><?php esc_html_e('Next step', 'vachr-short-term-rental-reservations'); ?></button><button type="submit" class="button button-primary" data-vstrb-page-create hidden><?php esc_html_e('Create and edit page', 'vachr-short-term-rental-reservations'); ?></button></div>
			</form>
			<?php endif; ?>
		</div>
		<?php
	}

	private static function page_wizard_labels($language) {
		$language = strtolower(substr((string) $language, 0, 2));
		$labels = [
			'en' => ['gallery' => 'Photos', 'gallery_help' => 'Add or replace apartment photos here.', 'availability' => 'Availability', 'description' => 'About the apartment', 'amenities' => 'Amenities', 'booking' => 'Book your stay'],
			'cs' => ['gallery' => 'Fotografie', 'gallery_help' => 'Sem přidejte nebo nahraďte fotografie apartmánu.', 'availability' => 'Dostupnost', 'description' => 'O apartmánu', 'amenities' => 'Vybavení', 'booking' => 'Rezervujte si pobyt'],
			'sk' => ['gallery' => 'Fotografie', 'gallery_help' => 'Sem pridajte alebo nahraďte fotografie apartmánu.', 'availability' => 'Dostupnosť', 'description' => 'O apartmáne', 'amenities' => 'Vybavenie', 'booking' => 'Rezervujte si pobyt'],
			'de' => ['gallery' => 'Fotos', 'gallery_help' => 'Fügen Sie hier Fotos des Apartments hinzu oder ersetzen Sie sie.', 'availability' => 'Verfügbarkeit', 'description' => 'Über das Apartment', 'amenities' => 'Ausstattung', 'booking' => 'Aufenthalt buchen'],
			'pl' => ['gallery' => 'Zdjęcia', 'gallery_help' => 'Dodaj lub zamień tutaj zdjęcia apartamentu.', 'availability' => 'Dostępność', 'description' => 'O apartamencie', 'amenities' => 'Udogodnienia', 'booking' => 'Zarezerwuj pobyt'],
			'fr' => ['gallery' => 'Photos', 'gallery_help' => 'Ajoutez ou remplacez ici les photos de l’appartement.', 'availability' => 'Disponibilité', 'description' => 'À propos de l’appartement', 'amenities' => 'Équipements', 'booking' => 'Réserver votre séjour'],
			'es' => ['gallery' => 'Fotos', 'gallery_help' => 'Añada o sustituya aquí las fotos del apartamento.', 'availability' => 'Disponibilidad', 'description' => 'Sobre el apartamento', 'amenities' => 'Servicios', 'booking' => 'Reserve su estancia'],
			'it' => ['gallery' => 'Foto', 'gallery_help' => 'Aggiungi o sostituisci qui le foto dell’appartamento.', 'availability' => 'Disponibilità', 'description' => 'Informazioni sull’appartamento', 'amenities' => 'Servizi', 'booking' => 'Prenota il soggiorno'],
			'nl' => ['gallery' => 'Foto’s', 'gallery_help' => 'Voeg hier foto’s van het appartement toe of vervang ze.', 'availability' => 'Beschikbaarheid', 'description' => 'Over het appartement', 'amenities' => 'Voorzieningen', 'booking' => 'Boek uw verblijf'],
			'pt' => ['gallery' => 'Fotografias', 'gallery_help' => 'Adicione ou substitua aqui as fotografias do apartamento.', 'availability' => 'Disponibilidade', 'description' => 'Sobre o apartamento', 'amenities' => 'Comodidades', 'booking' => 'Reserve a sua estadia'],
			'ru' => ['gallery' => 'Фотографии', 'gallery_help' => 'Добавьте или замените здесь фотографии апартаментов.', 'availability' => 'Доступность', 'description' => 'Об апартаментах', 'amenities' => 'Удобства', 'booking' => 'Забронировать проживание'],
			'uk' => ['gallery' => 'Фотографії', 'gallery_help' => 'Додайте або замініть тут фотографії апартаментів.', 'availability' => 'Доступність', 'description' => 'Про апартаменти', 'amenities' => 'Зручності', 'booking' => 'Забронювати проживання'],
			'ja' => ['gallery' => '写真', 'gallery_help' => 'ここにアパートメントの写真を追加または置き換えます。', 'availability' => '空室状況', 'description' => 'アパートメントについて', 'amenities' => '設備', 'booking' => '滞在を予約'],
			'zh' => ['gallery' => '照片', 'gallery_help' => '在此添加或替换公寓照片。', 'availability' => '可订日期', 'description' => '公寓介绍', 'amenities' => '设施', 'booking' => '预订住宿'],
		];
		return $labels[$language] ?? $labels['en'];
	}

	private static function page_heading_block($text) {
		return '<!-- wp:heading --><h2 class="wp-block-heading">' . esc_html($text) . '</h2><!-- /wp:heading -->';
	}

	private static function page_shortcode_block($shortcode) {
		return "<!-- wp:shortcode -->\n" . $shortcode . "\n<!-- /wp:shortcode -->";
	}

	private static function build_unit_page_content($unit_id, $language, $currency, $layout) {
		$labels = self::page_wizard_labels($language);
		$unit_id = (int) $unit_id;
		$language = esc_attr($language);
		$currency = esc_attr($currency);
		$gallery = self::page_heading_block($labels['gallery']) . "\n" .
			'<!-- wp:paragraph {"className":"vstrb-apartment-gallery-help"} --><p class="vstrb-apartment-gallery-help"><em>' . esc_html($labels['gallery_help']) . '</em></p><!-- /wp:paragraph -->' . "\n" .
			'<!-- wp:gallery {"linkTo":"none"} --><figure class="wp-block-gallery has-nested-images columns-default is-cropped"></figure><!-- /wp:gallery -->';
		$description = self::page_heading_block($labels['description']) . "\n" . self::page_shortcode_block(sprintf('[vstrb_unit_description unit="%d" language="%s"]', $unit_id, $language));
		$amenities = self::page_heading_block($labels['amenities']) . "\n" . self::page_shortcode_block(sprintf('[vstrb_unit_amenity_list unit="%d" language="%s"]', $unit_id, $language));
		$calendar = self::page_heading_block($labels['availability']) . "\n" . self::page_shortcode_block(sprintf('[vstrb_availability_calendar unit="%d" months="3" grid="3" currency="%s" language="%s"]', $unit_id, $currency, $language));
		$booking = self::page_heading_block($labels['booking']) . "\n" . self::page_shortcode_block(sprintf('[vstrb_booking_form unit="%d" months="2" grid="2" currency="%s" language="%s"]', $unit_id, $currency, $language));

		if ($layout === 'description_first') {
			return implode("\n\n", [$gallery, $description, $amenities, $calendar, $booking]);
		}
		if ($layout === 'booking_first') {
			return implode("\n\n", [$gallery, $booking, $description, $amenities, $calendar]);
		}
		if ($layout === 'columns') {
			$columns = '<!-- wp:columns --><div class="wp-block-columns"><!-- wp:column --><div class="wp-block-column">' . $description . '</div><!-- /wp:column --><!-- wp:column --><div class="wp-block-column">' . $amenities . '</div><!-- /wp:column --></div><!-- /wp:columns -->';
			return implode("\n\n", [$gallery, $columns, $calendar, $booking]);
		}
		if ($layout === 'gallery_booking') {
			$columns = '<!-- wp:columns {"verticalAlignment":"top"} --><div class="wp-block-columns are-vertically-aligned-top"><!-- wp:column {"verticalAlignment":"top","width":"60%"} --><div class="wp-block-column is-vertically-aligned-top" style="flex-basis:60%">' . $gallery . '</div><!-- /wp:column --><!-- wp:column {"verticalAlignment":"top","width":"40%"} --><div class="wp-block-column is-vertically-aligned-top" style="flex-basis:40%">' . $booking . '</div><!-- /wp:column --></div><!-- /wp:columns -->';
			return implode("\n\n", [$columns, $description, $amenities, $calendar]);
		}
		if ($layout === 'compact_grid') {
			$top_columns = '<!-- wp:columns --><div class="wp-block-columns"><!-- wp:column --><div class="wp-block-column">' . $gallery . '</div><!-- /wp:column --><!-- wp:column --><div class="wp-block-column">' . $calendar . '</div><!-- /wp:column --></div><!-- /wp:columns -->';
			$bottom_columns = '<!-- wp:columns --><div class="wp-block-columns"><!-- wp:column --><div class="wp-block-column">' . $description . "\n" . $amenities . '</div><!-- /wp:column --><!-- wp:column --><div class="wp-block-column">' . $booking . '</div><!-- /wp:column --></div><!-- /wp:columns -->';
			return $top_columns . "\n\n" . $bottom_columns;
		}
		return implode("\n\n", [$gallery, $calendar, $description, $amenities, $booking]);
	}

	public static function handle_create_unit_page() {
		if (!current_user_can('edit_pages')) {
			wp_die(esc_html__('You are not allowed to create pages.', 'vachr-short-term-rental-reservations'));
		}
		check_admin_referer('vstrb_create_unit_page', 'vstrb_create_unit_page_nonce');
		$unit_id = isset($_POST['unit_id']) ? absint($_POST['unit_id']) : 0;
		$unit = get_post($unit_id);
		if (!$unit || $unit->post_type !== 'vstrb_unit') {
			wp_die(esc_html__('Select a valid apartment.', 'vachr-short-term-rental-reservations'));
		}
		$languages = self::page_wizard_languages();
		$language = isset($_POST['language']) ? sanitize_text_field(wp_unslash($_POST['language'])) : '';
		if (!in_array($language, $languages, true)) $language = $languages[0] ?? 'en';
		$currencies = self::page_wizard_currencies();
		$currency = isset($_POST['currency']) ? strtoupper(sanitize_text_field(wp_unslash($_POST['currency']))) : '';
		if (!in_array($currency, $currencies, true)) $currency = $currencies[0] ?? 'EUR';
		$layout = isset($_POST['layout']) ? sanitize_key(wp_unslash($_POST['layout'])) : 'calendar_first';
		if (!in_array($layout, ['calendar_first', 'description_first', 'booking_first', 'columns', 'gallery_booking', 'compact_grid'], true)) $layout = 'calendar_first';
		$page_title = isset($_POST['page_title']) ? sanitize_text_field(wp_unslash($_POST['page_title'])) : '';
		if ($page_title === '') $page_title = $unit->post_title;
		$publish = isset($_POST['publish_page']) && sanitize_key(wp_unslash($_POST['publish_page'])) === '1' && current_user_can('publish_pages');
		$page_id = wp_insert_post([
			'post_type' => 'page',
			'post_status' => $publish ? 'publish' : 'draft',
			'post_title' => $page_title,
			'post_content' => self::build_unit_page_content($unit_id, $language, $currency, $layout),
			'post_author' => get_current_user_id(),
		], true);
		if (is_wp_error($page_id)) {
			wp_die(esc_html($page_id->get_error_message()));
		}
		update_post_meta($page_id, '_vstrb_unit_page_id', $unit_id);
		update_post_meta($page_id, '_vstrb_unit_page_language', $language);
		update_post_meta($page_id, '_vstrb_unit_page_currency', $currency);
		update_post_meta($page_id, '_vstrb_unit_page_layout', $layout);
		$thumbnail_id = get_post_thumbnail_id($unit_id);
		if ($thumbnail_id) set_post_thumbnail($page_id, $thumbnail_id);
		wp_safe_redirect(add_query_arg([
			'post' => (int) $page_id,
			'action' => 'edit',
			'vstrb_page_created' => '1',
		], admin_url('post.php')));
		exit;
	}

	private static function clone_unit_table_rows($table, $source_id, $new_id, $skip_columns = []) {
		global $wpdb;

		$rows = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$table} WHERE unit_id=%d", $source_id), ARRAY_A);
		if (!$rows || !is_array($rows)) {
			return;
		}

		foreach ($rows as $row) {
			foreach ($skip_columns as $column) {
				unset($row[$column]);
			}
			$row['unit_id'] = (int) $new_id;
			$wpdb->insert($table, $row);
		}
	}

	private static function bump_rates_cache_version() {
		$v = wp_cache_get('vstrb_v_rates', 'vstrb_rates');
		if (false === $v) {
			wp_cache_set('vstrb_v_rates', 2, 'vstrb_rates', 3600);
			return;
		}
		wp_cache_incr('vstrb_v_rates', 1, 'vstrb_rates');
	}

	public static function handle_clone_unit() {
		$source_id = isset($_GET['post']) ? absint(wp_unslash($_GET['post'])) : 0;
		if ($source_id <= 0) {
			wp_die(esc_html__('Invalid or missing apartment.', 'vachr-short-term-rental-reservations'));
		}

		check_admin_referer('vstrb_clone_unit_' . $source_id);

		$source = get_post($source_id);
		if (!$source || $source->post_type !== 'vstrb_unit' || $source->post_status === 'trash') {
			wp_die(esc_html__('Invalid or missing apartment.', 'vachr-short-term-rental-reservations'));
		}
		if (!current_user_can('edit_post', $source_id) || !current_user_can('edit_posts')) {
			wp_die(esc_html__('Sorry, you are not allowed to clone this apartment.', 'vachr-short-term-rental-reservations'));
		}

		$copy_suffix = ' (' . __('copy', 'vachr-short-term-rental-reservations') . ')';
		$copy_title = $source->post_title . $copy_suffix;

		$new_id = wp_insert_post([
			'post_type' => 'vstrb_unit',
			'post_status' => 'draft',
			'post_title' => $copy_title,
			'post_content' => $source->post_content,
			'post_excerpt' => $source->post_excerpt,
			'post_author' => get_current_user_id(),
			'post_parent' => 0,
			'menu_order' => (int) $source->menu_order,
			'comment_status' => $source->comment_status,
			'ping_status' => $source->ping_status,
		], true);

		if (is_wp_error($new_id)) {
			wp_safe_redirect(add_query_arg('vstrb_unit_clone_error', '1', admin_url('edit.php?post_type=vstrb_unit')));
			exit;
		}

		$skip_meta = [
			'_edit_lock' => true,
			'_edit_last' => true,
			'_wp_old_slug' => true,
			'_vstrb_ical_token' => true,
		];
		$meta = get_post_meta($source_id);
		foreach ($meta as $key => $values) {
			if (isset($skip_meta[$key]) || !is_array($values)) {
				continue;
			}
			foreach ($values as $value) {
				add_post_meta($new_id, $key, maybe_unserialize($value));
			}
		}

		foreach (get_object_taxonomies('vstrb_unit') as $taxonomy) {
			$term_ids = wp_get_object_terms($source_id, $taxonomy, ['fields' => 'ids']);
			if (!is_wp_error($term_ids)) {
				wp_set_object_terms($new_id, array_map('intval', $term_ids), $taxonomy);
			}
		}

		$t = VSTRB_DB::tables();
		self::clone_unit_table_rows($t['unit_services'], $source_id, $new_id);
		self::clone_unit_table_rows($t['unit_amenities'], $source_id, $new_id);
		self::clone_unit_table_rows($t['unit_contact_fields'], $source_id, $new_id);
		self::clone_unit_table_rows($t['rates'], $source_id, $new_id, ['id', 'created_at', 'updated_at']);
		self::bump_rates_cache_version();

		wp_safe_redirect(add_query_arg([
			'post' => (int) $new_id,
			'action' => 'edit',
			'vstrb_unit_cloned' => '1',
			'vstrb_source_unit' => (int) $source_id,
		], admin_url('post.php')));
		exit;
	}

	public static function admin_notices() {
		$has_cloned = null !== filter_input(INPUT_GET, 'vstrb_unit_cloned', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
		$has_error = null !== filter_input(INPUT_GET, 'vstrb_unit_clone_error', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
		if (!$has_cloned && !$has_error) {
			return;
		}

		if ($has_error) {
			echo '<div class="notice notice-error is-dismissible"><p>' . esc_html__('Could not clone apartment.', 'vachr-short-term-rental-reservations') . '</p></div>';
			return;
		}

		echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('Apartment cloned as a draft. Review it and publish when ready.', 'vachr-short-term-rental-reservations') . '</p></div>';
	}

	public static function columns_head($columns) {
		// Add new column after title
		$new = [];
		foreach($columns as $key => $title) {
			$new[$key] = $title;
			if ($key === 'title') {
				$new['shortcodes'] = esc_html__('Shortcodes', 'vachr-short-term-rental-reservations');
			}
		}
		return $new;
	}

	public static function columns_content($column_name, $post_id) {
		if ($column_name == 'shortcodes') {
			echo '<div style="display:flex;align-items:center;gap:10px">';
			echo '<code style="font-size:11px;color:#0073aa;user-select:all;margin:0">' . esc_html(sprintf('[vstrb_booking_form unit="%d"]', (int)$post_id)) . '</code>';
			echo '<a href="#" style="font-size:10px;text-decoration:none;color:#555;white-space:nowrap" onclick="var s=this.parentNode.nextElementSibling;s.style.display=s.style.display==\'none\'?\'block\':\'none\';return false">'.esc_html__('Show more', 'vachr-short-term-rental-reservations').' &#9662;</a>';
			echo '</div>';
			
			// Hidden other shortcodes
			echo '<div style="display:none;margin-top:5px;padding:5px;background:#f0f0f1;border:1px solid #ddd">';
			echo '<code style="display:block;margin-bottom:2px;font-size:10px;user-select:all">' . esc_html(sprintf('[vstrb_availability_calendar unit="%d"]', (int)$post_id)) . '</code>';
			echo '<code style="display:block;margin-bottom:2px;font-size:10px;user-select:all">' . esc_html(sprintf('[vstrb_guest_registration unit="%d"]', (int)$post_id)) . '</code>';
			echo '<code style="display:block;margin-bottom:2px;font-size:10px;user-select:all">' . esc_html(sprintf('[vstrb_unit_description unit="%d"]', (int)$post_id)) . '</code>';
			echo '<code style="display:block;margin-bottom:2px;font-size:10px;user-select:all">' . esc_html(sprintf('[vstrb_unit_amenity_list unit="%d"]', (int)$post_id)) . '</code>';
			echo '<code style="display:block;margin-bottom:2px;font-size:10px;user-select:all">' . esc_html(sprintf('[vstrb_unit_arrival_instructions unit="%d"]', (int)$post_id)) . '</code>';
			echo '<code style="display:block;margin-bottom:2px;font-size:10px;user-select:all">' . esc_html(sprintf('[vstrb_unit_bank_transfer_instructions unit="%d"]', (int)$post_id)) . '</code>';
			echo '<code style="display:block;margin-bottom:2px;font-size:10px;user-select:all">' . esc_html('[vstrb_unit_arrival_instructions_by_code param="unit_code"]') . '</code>';
			echo '<code style="display:block;margin-bottom:2px;font-size:10px;user-select:all">' . esc_html(sprintf('[vstrb_unit_arrival_instructions_by_reservation unit="%d"]', (int)$post_id)) . '</code>';
			echo '</div>';
		}
	}

	public static function render_table_footer() {
		$screen = get_current_screen();
		if (!$screen || $screen->post_type !== 'vstrb_unit' || $screen->base !== 'edit') return;
		
		// Output as hidden template then move with JS to proper location
		?>
		<a id="vstrb-add-with-wizard" class="page-title-action" hidden href="<?php echo esc_url(add_query_arg(['post_type' => 'vstrb_unit', 'vstrb_wizard' => '1'], admin_url('post-new.php'))); ?>"><?php esc_html_e('Add Apartment', 'vachr-short-term-rental-reservations'); ?></a>
		<div id="vstrb-shortcode-help" style="display:none;margin-top:20px;color:#444;font-size:13px;background:#fff;padding:20px;border:1px solid #ccd0d4;border-left:4px solid #0073aa;box-shadow:0 1px 1px rgba(0,0,0,.04);max-width:1200px">
			<h3 style="margin:0 0 10px 0;font-size:14px;border-bottom:1px solid #eee;padding-bottom:5px"><?php esc_html_e('Shortcode Guide & Examples', 'vachr-short-term-rental-reservations'); ?></h3>
			<p style="margin-bottom:3px"><strong><?php esc_html_e('vstrb_booking_form / vstrb_availability_calendar', 'vachr-short-term-rental-reservations'); ?></strong></p>
			<ul style="list-style:disc;margin-left:20px;margin-bottom:10px">
				<li><code>months="NUMBER"</code> - <?php esc_html_e('Number of months to display (e.g. 1, 3, 6, 12). Default is 1.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>grid="NUMBER"</code> - <?php esc_html_e('How many months to show per row (grid layout). Useful for multi-month views.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>currency="CODE"</code> - <?php esc_html_e('Currency to use for prices (e.g. EUR, USD, CZK). Must be one of the defined active currencies.', 'vachr-short-term-rental-reservations'); ?></li>
                <li><code>enable_stripe="1"</code> - <?php esc_html_e('Enable credit card payments via Stripe.', 'vachr-short-term-rental-reservations'); ?></li>
                <li><code>enable_mollie="1"</code> - <?php esc_html_e('Enable credit card payments via Mollie.', 'vachr-short-term-rental-reservations'); ?></li>
                <li><code>enable_bank_transfer="1"</code> - <?php esc_html_e('Enable bank transfer payments.', 'vachr-short-term-rental-reservations'); ?></li>
                <li><code>enable_cash="1"</code> - <?php esc_html_e('Enable cash payments.', 'vachr-short-term-rental-reservations'); ?></li>
			</ul>
			<p style="margin:0 0 3px 0"><strong><?php esc_html_e('vstrb_guest_registration', 'vachr-short-term-rental-reservations'); ?></strong></p>
			<ul style="list-style:disc;margin-left:20px;margin-bottom:15px">
				<li><code>title="TEXT"</code> - <?php esc_html_e('Custom heading shown above the form. If omitted, the apartment name is used.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>language="LOCALE"</code> - <?php esc_html_e('Locale code for the form language (e.g. cs_CZ, de_DE, en_US). If omitted, the site language is used.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>param="accommodation"</code> - <?php esc_html_e('Resolve the apartment from a URL parameter, useful for Booking.com links with [PROPERTY_NAME].', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>match="contains"</code> - <?php esc_html_e('Allow one unique partial match when the external channel sends a longer or shorter name.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>fallback="TEXT"</code> - <?php esc_html_e('Text shown when no unique apartment is matched.', 'vachr-short-term-rental-reservations'); ?></li>
			</ul>
			<p style="margin:0 0 3px 0"><strong><?php esc_html_e('vstrb_unit_description / vstrb_unit_amenity_list / vstrb_unit_arrival_instructions / vstrb_unit_bank_transfer_instructions', 'vachr-short-term-rental-reservations'); ?></strong></p>
			<ul style="list-style:disc;margin-left:20px;margin-bottom:15px">
				<li><code>language="CODE"</code> - <?php esc_html_e('Language code for apartment content (e.g. cs, de, en or cs_CZ). If omitted, the site language is used.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><?php esc_html_e('These shortcodes display the saved apartment description, translated amenity list, arrival instructions, or bank transfer instructions.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><?php esc_html_e('Apartment-specific text has priority over the selected shared template.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><?php esc_html_e('Use [vstrb_unit_arrival_instructions_by_code] when you can put a stable apartment code into an external channel URL.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><?php esc_html_e('Use [vstrb_unit_arrival_instructions_by_name] only when the external channel can send only an accommodation name; add that exact external name to the apartment aliases.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><?php esc_html_e('Use [vstrb_unit_arrival_instructions_by_reservation] when an external channel can pass stay dates and guest name instead of a stable apartment code.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>param="NAME"</code> - <?php esc_html_e('GET parameter that contains the public code or accommodation name.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>date_from_param / date_to_param / first_name_param / last_name_param</code> - <?php esc_html_e('GET parameter names used by the reservation matching shortcode.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>available_days_before="1"</code> - <?php esc_html_e('How many days before arrival the reservation matching shortcode may show arrival instructions. Use 0 for arrival day only.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>unavailable_text="TEXT"</code> - <?php esc_html_e('Text shown when arrival instructions are requested too early.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>match="contains"</code> - <?php esc_html_e('Allow one unique partial match when the external channel sends a longer or shorter name.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>fallback="TEXT"</code> - <?php esc_html_e('Text shown when no unique apartment is matched.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>[vstrb_unit_arrival_instructions_by_code param="unit_code" language="cs"]</code> - <?php esc_html_e('Shows arrival instructions by matching a public apartment code from a URL parameter.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>[vstrb_unit_arrival_instructions_by_name param="accommodation" language="cs"]</code> - <?php esc_html_e('Shows arrival instructions by matching an accommodation name from a URL parameter to the apartment title or aliases.', 'vachr-short-term-rental-reservations'); ?></li>
				<li><code>[vstrb_unit_arrival_instructions_by_reservation date_from_param="checkin" date_to_param="checkout" first_name_param="guest_first_name" last_name_param="guest_last_name" language="cs" available_days_before="1"]</code> - <?php esc_html_e('Shows arrival instructions by matching stay dates and guest name against bookings and linked guest registrations.', 'vachr-short-term-rental-reservations'); ?></li>
			</ul>
			<strong><?php esc_html_e('Examples:', 'vachr-short-term-rental-reservations'); ?></strong><br>
			<div style="background:#f0f0f1;padding:10px;margin-top:5px;border:1px solid #ddd">
				<code>[vstrb_booking_form unit="123" currency="USD" months="3" grid="1" enable_stripe="1" enable_mollie="1" enable_bank_transfer="1" enable_cash="1"]</code> <span style="color:#666"> - <?php esc_html_e('Shows the booking form in USD with Stripe, Mollie, bank transfer, and cash payments enabled, displayed as a one-column grid for 3 months.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_availability_calendar unit="123" months="3" grid="3"]</code> <span style="color:#666"> - <?php esc_html_e('Shows a 3-month availability calendar in a single row.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_availability_calendar unit="123" months="12" grid="4"]</code> <span style="color:#666"> - <?php esc_html_e('Shows a full year calendar, 4 months per row.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_guest_registration unit="123" title="Apartment Blue House" language="en_US"]</code> <span style="color:#666"> - <?php esc_html_e('Guest registration form with a custom heading in English.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_guest_registration param="accommodation" language="en_US" fallback="Accommodation not found. Please contact us."]</code> <span style="color:#666"> - <?php esc_html_e('Guest registration form resolved from a Booking.com [PROPERTY_NAME] URL parameter.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_unit_description unit="123" language="cs"]</code> <span style="color:#666"> - <?php esc_html_e('Shows the apartment description in Czech when available.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_unit_amenity_list unit="123" language="de"]</code> <span style="color:#666"> - <?php esc_html_e('Shows the translated apartment amenity list in German when available.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_unit_arrival_instructions unit="123" language="en"]</code> <span style="color:#666"> - <?php esc_html_e('Shows the apartment arrival instructions in English when available.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_unit_bank_transfer_instructions unit="123" language="cs"]</code> <span style="color:#666"> - <?php esc_html_e('Shows the apartment bank transfer instructions in Czech when available.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_unit_arrival_instructions_by_code param="unit_code" language="cs" fallback="Accommodation not found. Please contact us."]</code> <span style="color:#666"> - <?php esc_html_e('Shows arrival instructions by matching the unit_code URL parameter to a public apartment code.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_unit_arrival_instructions_by_name param="accommodation" language="cs" fallback="Accommodation not found. Please contact us."]</code> <span style="color:#666"> - <?php esc_html_e('Shows arrival instructions by matching the accommodation URL parameter to an apartment title or alias.', 'vachr-short-term-rental-reservations'); ?></span><br>
				<code>[vstrb_unit_arrival_instructions_by_reservation date_from_param="checkin" date_to_param="checkout" first_name_param="guest_first_name" last_name_param="guest_last_name" language="cs" available_days_before="1" unavailable_text="Arrival instructions are not available yet." fallback="Arrival instructions are not available. Please contact us."]</code> <span style="color:#666"> - <?php esc_html_e('Shows arrival instructions by matching stay dates and guest name against bookings and linked guest registrations.', 'vachr-short-term-rental-reservations'); ?></span>
			</div>
		</div>
		<?php
		self::add_inline_admin_script("
			document.addEventListener('DOMContentLoaded', function() {
				var helpInfo = document.getElementById('vstrb-shortcode-help');
				var wrapper = document.querySelector('.wrap');
				if (helpInfo && wrapper) {
					wrapper.appendChild(helpInfo);
					helpInfo.style.display = 'block';
				}
			});
		");
	}

	public static function metaboxes() {
		add_meta_box('vstrb_unit_basic', esc_html__('Basic Apartment Settings', 'vachr-short-term-rental-reservations'), [__CLASS__, 'render_basic'], 'vstrb_unit', 'normal', 'high');
		add_meta_box('vstrb_unit_advanced', esc_html__('Advanced Apartment Settings', 'vachr-short-term-rental-reservations'), [__CLASS__, 'render_advanced'], 'vstrb_unit', 'normal', 'default');
	}

	private static function field($name, $value, $type='text', $placeholder='') {
		$name_attr = esc_attr($name);
		$val = esc_attr($value);
		$ph = esc_attr($placeholder);
		return "<input style=\"width:100%\" type=\"$type\" name=\"$name_attr\" value=\"$val\" placeholder=\"$ph\" />";
	}

	private static function allowed_input_html() {
		return [
			'input' => [
				'style' => true,
				'type' => true,
				'name' => true,
				'value' => true,
				'placeholder' => true,
			],
		];
	}

	private static function normalize_language_code($locale) {
		$locale = strtolower(trim((string) $locale));
		if (preg_match('/^([a-z]{2})/', $locale, $m)) {
			return $m[1];
		}
		return '';
	}

	private static function get_language_options() {
		$raw = (string) get_option('vstrb_available_languages', 'cs,en,sk,de,pl,fr,es,it,nl,pt,ru,uk,ja,zh');
		$options = [];
		foreach (explode(',', $raw) as $part) {
			$code = self::normalize_language_code($part);
			if ($code !== '' && !isset($options[$code])) {
				$options[$code] = strtoupper($code);
			}
		}
		if (!$options) {
			$options = ['cs' => 'CS', 'en' => 'EN', 'de' => 'DE'];
		}
		return $options;
	}

	private static function get_content_templates() {
		$templates = get_option('vstrb_unit_content_templates', []);
		return is_array($templates) ? $templates : [];
	}

	private static function sanitize_i18n_textarea_array($key) {
		$raw = self::post_array($key);
		$out = [];
		foreach ($raw as $lang => $value) {
			if (is_array($value)) {
				continue;
			}
			$lang = self::normalize_language_code($lang);
			if ($lang === '') {
				continue;
			}
			$text = wp_kses_post(html_entity_decode((string) $value, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
			if (trim(wp_strip_all_tags($text)) !== '') {
				$out[$lang] = $text;
			}
		}
		return $out;
	}

	private static function render_i18n_textareas($name, $values, $rows = 4) {
		$values = is_array($values) ? $values : [];
		foreach (self::get_language_options() as $lang => $label) {
			echo '<p style="margin:8px 0"><label><strong>' . esc_html($label) . '</strong><br>';
			echo '<textarea style="width:100%;min-height:' . (int) max(80, $rows * 24) . 'px" name="' . esc_attr($name) . '[' . esc_attr($lang) . ']">' . esc_textarea($values[$lang] ?? '') . '</textarea>';
			echo '</label></p>';
		}
	}

	private static function render_template_select($name, $selected, $templates) {
		echo '<select name="' . esc_attr($name) . '" style="width:100%;max-width:420px">';
		echo '<option value="">' . esc_html__('No template', 'vachr-short-term-rental-reservations') . '</option>';
		foreach ($templates as $template_id => $template) {
			$title = is_array($template) ? (string) ($template['title'] ?? $template_id) : (string) $template_id;
			echo '<option value="' . esc_attr($template_id) . '" ' . selected((string) $selected, (string) $template_id, false) . '>' . esc_html($title) . '</option>';
		}
		echo '</select>';
	}

	private static function sanitize_placeholder_key($key) {
		$key = strtolower(trim((string) $key));
		$key = preg_replace('/[^a-z0-9_]/', '_', $key);
		$key = preg_replace('/_+/', '_', (string) $key);
		return trim((string) $key, '_');
	}

	private static function sanitize_content_placeholders_from_post() {
		$raw = self::post_array('vstrb_content_placeholders');
		$out = [];
		foreach ($raw as $row) {
			if (!is_array($row)) {
				continue;
			}
			$key = self::sanitize_placeholder_key($row['key'] ?? '');
			if ($key === '') {
				continue;
			}
			$value = wp_kses_post(html_entity_decode((string) ($row['value'] ?? ''), ENT_QUOTES | ENT_HTML5, 'UTF-8'));
			$i18n = [];
			$lang_values = isset($row['i18n']) && is_array($row['i18n']) ? $row['i18n'] : [];
			foreach ($lang_values as $lang => $lang_value) {
				if (is_array($lang_value)) {
					continue;
				}
				$lang = self::normalize_language_code($lang);
				if ($lang === '') {
					continue;
				}
				$text = wp_kses_post(html_entity_decode((string) $lang_value, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
				if (trim(wp_strip_all_tags($text)) !== '') {
					$i18n[$lang] = $text;
				}
			}
			if (trim(wp_strip_all_tags($value)) !== '' || $i18n) {
				$out[$key] = [
					'value' => $value,
					'i18n' => $i18n,
				];
			}
		}
		return $out;
	}

	private static function render_content_placeholders($placeholders) {
		$placeholders = is_array($placeholders) ? $placeholders : [];
		if (!$placeholders) {
			$placeholders = [
				'door_pin' => ['value' => '', 'i18n' => []],
			];
		}
		echo '<details style="margin:10px 0;padding:10px;border:1px solid #dcdcde;background:#fff"><summary><strong>' . esc_html__('Template Placeholders', 'vachr-short-term-rental-reservations') . '</strong></summary>';
		echo '<p style="color:#666">' . esc_html__('Use {{iban}} for IBAN, {{account_number}} or {{bank_account}} for the domestic account number, and {{account_country}} for the country code. Other payment keys include {{invoice_number}}, {{variable_symbol}}, {{payment_amount}}, and {{payment_currency}}.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<div id="vstrb-content-placeholders">';
		$idx = 0;
		foreach ($placeholders as $key => $row) {
			$row = is_array($row) ? $row : ['value' => (string) $row, 'i18n' => []];
			$row_i18n = isset($row['i18n']) && is_array($row['i18n']) ? $row['i18n'] : [];
			echo '<div class="vstrb-content-placeholder" style="border:1px solid #dcdcde;background:#f6f7f7;padding:10px;margin:0 0 10px 0">';
			echo '<div style="display:grid;grid-template-columns:minmax(150px,220px) 1fr auto;gap:10px;align-items:start">';
			echo '<label>' . esc_html__('Key', 'vachr-short-term-rental-reservations') . '<br><input type="text" name="vstrb_content_placeholders[' . (int) $idx . '][key]" value="' . esc_attr($key) . '" placeholder="door_pin" style="width:100%"></label>';
			echo '<label>' . esc_html__('Global value', 'vachr-short-term-rental-reservations') . '<br><textarea name="vstrb_content_placeholders[' . (int) $idx . '][value]" rows="2" style="width:100%">' . esc_textarea($row['value'] ?? '') . '</textarea></label>';
			echo '<button type="button" class="button" onclick="this.closest(\'.vstrb-content-placeholder\').remove()">' . esc_html__('Remove', 'vachr-short-term-rental-reservations') . '</button>';
			echo '</div>';
			echo '<details style="margin-top:8px"><summary>' . esc_html__('Language-specific values', 'vachr-short-term-rental-reservations') . '</summary>';
			foreach (self::get_language_options() as $lang => $label) {
				echo '<label style="display:block;margin:6px 0"><strong>' . esc_html($label) . '</strong><br><textarea name="vstrb_content_placeholders[' . (int) $idx . '][i18n][' . esc_attr($lang) . ']" rows="2" style="width:100%">' . esc_textarea($row_i18n[$lang] ?? '') . '</textarea></label>';
			}
			echo '</details></div>';
			$idx++;
		}
		echo '</div><p><button type="button" class="button" onclick="vstrbAddContentPlaceholder()">' . esc_html__('Add Placeholder', 'vachr-short-term-rental-reservations') . '</button></p>';
		echo '</details>';

		$langs_json = wp_json_encode(self::get_language_options());
		self::add_inline_admin_script("
			window.vstrbAddContentPlaceholder = function() {
				const wrap = document.getElementById('vstrb-content-placeholders');
				if (!wrap) return;
				const idx = Date.now();
				const langs = {$langs_json} || {};
				const langFields = Object.entries(langs).map(function(entry) {
					return '<label style=\"display:block;margin:6px 0\"><strong>' + entry[1] + '</strong><br><textarea name=\"vstrb_content_placeholders[' + idx + '][i18n][' + entry[0] + ']\" rows=\"2\" style=\"width:100%\"></textarea></label>';
				}).join('');
				const div = document.createElement('div');
				div.className = 'vstrb-content-placeholder';
				div.style = 'border:1px solid #dcdcde;background:#f6f7f7;padding:10px;margin:0 0 10px 0';
				div.innerHTML = '<div style=\"display:grid;grid-template-columns:minmax(150px,220px) 1fr auto;gap:10px;align-items:start\"><label>" . esc_js(__('Key', 'vachr-short-term-rental-reservations')) . "<br><input type=\"text\" name=\"vstrb_content_placeholders[' + idx + '][key]\" value=\"\" placeholder=\"door_pin\" style=\"width:100%\"></label><label>" . esc_js(__('Global value', 'vachr-short-term-rental-reservations')) . "<br><textarea name=\"vstrb_content_placeholders[' + idx + '][value]\" rows=\"2\" style=\"width:100%\"></textarea></label><button type=\"button\" class=\"button\" onclick=\"this.closest(\\'.vstrb-content-placeholder\\').remove()\">" . esc_js(__('Remove', 'vachr-short-term-rental-reservations')) . "</button></div><details style=\"margin-top:8px\"><summary>" . esc_js(__('Language-specific values', 'vachr-short-term-rental-reservations')) . "</summary>' + langFields + '</details>';
				wrap.appendChild(div);
			};
		");
	}

	private static function sanitize_closed_periods_from_post() {
		$periods = [];

		$date_ranges = self::post_array('vstrb_closed_date_ranges');
		foreach ($date_ranges as $row) {
			if (!is_array($row)) {
				continue;
			}
			$date_from = sanitize_text_field($row['from'] ?? '');
			$date_to = sanitize_text_field($row['to'] ?? '');
			if (
				preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_from) &&
				preg_match('/^\d{4}-\d{2}-\d{2}$/', $date_to) &&
				strtotime($date_to) >= strtotime($date_from)
			) {
				$periods[] = [
					'type' => 'date_range',
					'from' => $date_from,
					'to' => $date_to,
				];
			}
		}

		$uses_available_months = null !== filter_input(INPUT_POST, 'vstrb_available_months_present', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
		$submitted_months = self::post_array($uses_available_months ? 'vstrb_available_months' : 'vstrb_closed_months');
		$months = [];
		foreach ($submitted_months as $month) {
			$m = (int) sanitize_text_field((string) $month);
			if ($m >= 1 && $m <= 12 && !in_array($m, $months, true)) {
				$months[] = $m;
			}
		}
		if ($uses_available_months) {
			$months = array_values(array_diff(range(1, 12), $months));
		}
		sort($months, SORT_NUMERIC);
		if ($months) {
			$periods[] = [
				'type' => 'months',
				'months' => $months,
			];
		}

		$day_ranges = self::post_array('vstrb_closed_day_ranges');
		foreach ($day_ranges as $row) {
			if (!is_array($row)) {
				continue;
			}
			$from = sanitize_text_field($row['from'] ?? '');
			$to = sanitize_text_field($row['to'] ?? '');
			if (preg_match('/^\d{2}-\d{2}$/', $from) && preg_match('/^\d{2}-\d{2}$/', $to)) {
				$from_dt = DateTime::createFromFormat('!Y-m-d', '2000-' . $from);
				$to_dt = DateTime::createFromFormat('!Y-m-d', '2000-' . $to);
				if ($from_dt && $from_dt->format('m-d') === $from && $to_dt && $to_dt->format('m-d') === $to) {
					$periods[] = [
						'type' => 'days_of_year',
						'from' => $from,
						'to' => $to,
					];
				}
			}
		}

		return $periods;
	}

	public static function render_basic($post) {
		wp_nonce_field('vstrb_unit_meta', 'vstrb_unit_meta_nonce');
		echo '<input type="hidden" name="vstrb_wizard_complete" value="0" data-vstrb-wizard-complete>';
		$checkin  = get_post_meta($post->ID, '_vstrb_checkin', true);
		$checkout = get_post_meta($post->ID, '_vstrb_checkout', true);
		$capacity = get_post_meta($post->ID, '_vstrb_capacity', true);
		$min_stay = get_post_meta($post->ID, '_vstrb_min_stay', true);
		$max_stay = get_post_meta($post->ID, '_vstrb_max_stay', true);
		if (isset($post->post_status) && $post->post_status === 'auto-draft') {
			$checkin = $checkin !== '' ? $checkin : '14:00';
			$checkout = $checkout !== '' ? $checkout : '10:00';
			$capacity = $capacity !== '' ? $capacity : '2';
		}

		$is_paused = get_post_meta($post->ID, '_vstrb_is_paused', true);
		$closed_periods = get_post_meta($post->ID, '_vstrb_closed_periods', true);
		$closed_periods = is_array($closed_periods) ? $closed_periods : [];
		$closed_date_ranges = array_values(array_filter($closed_periods, function($row) {
			return is_array($row) && ($row['type'] ?? '') === 'date_range';
		}));
		$closed_month_period = array_values(array_filter($closed_periods, function($row) {
			return is_array($row) && ($row['type'] ?? '') === 'months';
		}));
		$closed_months = isset($closed_month_period[0]['months']) && is_array($closed_month_period[0]['months']) ? array_map('intval', $closed_month_period[0]['months']) : [];
		$closed_day_ranges = array_values(array_filter($closed_periods, function($row) {
			return is_array($row) && ($row['type'] ?? '') === 'days_of_year';
		}));

		$wizard_autostart = isset($_GET['vstrb_wizard']) && sanitize_key(wp_unslash($_GET['vstrb_wizard'])) === '1'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		echo '<div class="vstrb-unit-wizard-launch" data-vstrb-wizard-autostart="' . ($wizard_autostart ? '1' : '0') . '">';
		echo '<div><strong>' . esc_html__('Apartment setup wizard', 'vachr-short-term-rental-reservations') . '</strong><p>' . esc_html__('Set up the apartment in a few explained steps. You can leave optional advanced settings for later.', 'vachr-short-term-rental-reservations') . '</p></div>';
		echo '<button type="button" class="button button-primary" data-vstrb-wizard-start>' . esc_html__('Start setup wizard', 'vachr-short-term-rental-reservations') . '</button>';
		echo '</div>';
		echo '<div class="vstrb-unit-wizard" hidden>';
		echo '<div class="vstrb-unit-wizard__top"><div><span class="vstrb-unit-wizard__eyebrow">' . esc_html__('Apartment setup', 'vachr-short-term-rental-reservations') . '</span><h2 data-vstrb-wizard-title></h2><p data-vstrb-wizard-description></p></div><button type="button" class="button-link" data-vstrb-wizard-exit>' . esc_html__('Exit wizard', 'vachr-short-term-rental-reservations') . '</button></div>';
		echo '<ol class="vstrb-unit-wizard__steps" aria-label="' . esc_attr__('Setup progress', 'vachr-short-term-rental-reservations') . '">';
		$wizard_steps = [
			['stay', __('Basic information', 'vachr-short-term-rental-reservations'), __('Name the apartment and set the guest capacity, arrival and departure times, and usual stay length.', 'vachr-short-term-rental-reservations')],
			['availability', __('Availability', 'vachr-short-term-rental-reservations'), __('Select the months when guests can book. More precise availability rules are optional.', 'vachr-short-term-rental-reservations')],
			['pricing', __('Base pricing', 'vachr-short-term-rental-reservations'), __('Set the normal price. Seasonal prices can still be added later under Rates.', 'vachr-short-term-rental-reservations')],
			['services', __('Services', 'vachr-short-term-rental-reservations'), __('Choose services that guests can order with their booking.', 'vachr-short-term-rental-reservations')],
			['amenities', __('Amenities', 'vachr-short-term-rental-reservations'), __('Select the equipment and amenities already included in the apartment.', 'vachr-short-term-rental-reservations')],
			['advanced', __('Advanced (optional)', 'vachr-short-term-rental-reservations'), __('Configure guest content, arrival instructions, contact fields, and calendar synchronization only if you need them.', 'vachr-short-term-rental-reservations')],
		];
		foreach ($wizard_steps as $index => $wizard_step) {
			echo '<li><button type="button" data-vstrb-wizard-go="' . esc_attr($wizard_step[0]) . '" data-title="' . esc_attr($wizard_step[1]) . '" data-description="' . esc_attr($wizard_step[2]) . '"><span>' . (int) ($index + 1) . '</span><b>' . esc_html($wizard_step[1]) . '</b></button></li>';
		}
		echo '</ol>';
		echo '<div class="vstrb-unit-wizard__notice"><span class="dashicons dashicons-info-outline" aria-hidden="true"></span><p data-vstrb-wizard-help></p></div>';
		echo '<div class="vstrb-unit-wizard__validation" data-vstrb-validation-summary data-error-heading="' . esc_attr__('Please complete the apartment setup', 'vachr-short-term-rental-reservations') . '" data-success-heading="' . esc_attr__('The apartment setup is complete and ready to save.', 'vachr-short-term-rental-reservations') . '" data-error-title="' . esc_attr__('Enter the apartment name.', 'vachr-short-term-rental-reservations') . '" data-error-capacity="' . esc_attr__('Capacity must be at least 1 guest.', 'vachr-short-term-rental-reservations') . '" data-error-times="' . esc_attr__('Enter check-in and check-out times in HH:MM format.', 'vachr-short-term-rental-reservations') . '" data-error-stay="' . esc_attr__('Maximum stay cannot be shorter than minimum stay.', 'vachr-short-term-rental-reservations') . '" data-error-months="' . esc_attr__('Select at least one month available for booking.', 'vachr-short-term-rental-reservations') . '" data-error-price="' . esc_attr__('Enter a price greater than zero for the selected pricing method in every currency.', 'vachr-short-term-rental-reservations') . '" data-error-negative="' . esc_attr__('Prices and deposits cannot be negative.', 'vachr-short-term-rental-reservations') . '" data-error-discount="' . esc_attr__('For a basic discount, enter at least 2 nights and a percentage greater than 0 and no more than 100, or leave both fields empty.', 'vachr-short-term-rental-reservations') . '" data-error-pending="' . esc_attr__('Use the plus button to add the item you entered, or clear the unfinished field.', 'vachr-short-term-rental-reservations') . '"><strong>' . esc_html__('Please complete the apartment setup', 'vachr-short-term-rental-reservations') . '</strong><ul></ul></div>';
		echo '</div>';

		echo '<section class="vstrb-unit-wizard-section" data-vstrb-wizard-section="availability">';
		echo '<h3>' . esc_html__('Months available for booking', 'vachr-short-term-rental-reservations') . '</h3>';
		echo '<p class="description">' . esc_html__('Select the months when guests can book the apartment. Keep all months selected for year-round bookings.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<input type="hidden" name="vstrb_available_months_present" value="1">';
		echo '<div class="vstrb-wizard-months">';
		for ($m = 1; $m <= 12; $m++) {
			$label = date_i18n('F', mktime(0, 0, 0, $m, 1));
			echo '<label><input type="checkbox" name="vstrb_available_months[]" value="'.(int)$m.'" '.checked(!in_array($m, $closed_months, true), true, false).'><span>'.esc_html($label).'</span></label>';
		}
		echo '</div>';
		echo '<details class="vstrb-wizard-availability-advanced">';
		echo '<summary><strong>' . esc_html__('Advanced availability options', 'vachr-short-term-rental-reservations') . '</strong><span>' . esc_html__('Pause bookings or close specific dates', 'vachr-short-term-rental-reservations') . '</span></summary>';
		echo '<div class="vstrb-wizard-availability-advanced__content">';

		echo '<p><label><strong>'.esc_html__('Pause Apartment', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
			 '<label><input type="checkbox" name="_vstrb_is_paused" value="1" '.checked($is_paused, 1, false).' /> '.esc_html__('Temporarily stop new bookings', 'vachr-short-term-rental-reservations').'</label></p>';

		echo '<hr><p><strong>'.esc_html__('Specific date ranges', 'vachr-short-term-rental-reservations').'</strong></p>';
		for ($i = 0; $i < 3; $i++) {
			$row = $closed_date_ranges[$i] ?? [];
			echo '<p style="display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:520px">';
			echo '<label>'.esc_html__('From', 'vachr-short-term-rental-reservations').'<br><input style="width:100%" type="date" name="vstrb_closed_date_ranges['.(int)$i.'][from]" value="'.esc_attr($row['from'] ?? '').'"></label>';
			echo '<label>'.esc_html__('To', 'vachr-short-term-rental-reservations').'<br><input style="width:100%" type="date" name="vstrb_closed_date_ranges['.(int)$i.'][to]" value="'.esc_attr($row['to'] ?? '').'"></label>';
			echo '</p>';
		}
		echo '<p><strong>'.esc_html__('Recurring day ranges every year', 'vachr-short-term-rental-reservations').'</strong></p>';
		echo '<p style="color:#666">'.esc_html__('Enter month and day as MM-DD, for example 12-20 to 03-15. Ranges may cross New Year.', 'vachr-short-term-rental-reservations').'</p>';
		for ($i = 0; $i < 3; $i++) {
			$row = $closed_day_ranges[$i] ?? [];
			echo '<p style="display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:520px">';
			echo '<label>'.esc_html__('From', 'vachr-short-term-rental-reservations').'<br><input style="width:100%" type="text" name="vstrb_closed_day_ranges['.(int)$i.'][from]" value="'.esc_attr($row['from'] ?? '').'" placeholder="12-20"></label>';
			echo '<label>'.esc_html__('To', 'vachr-short-term-rental-reservations').'<br><input style="width:100%" type="text" name="vstrb_closed_day_ranges['.(int)$i.'][to]" value="'.esc_attr($row['to'] ?? '').'" placeholder="03-15"></label>';
			echo '</p>';
		}
		echo '</div></details>';
		echo '</section>';

		echo '<section class="vstrb-unit-wizard-section" data-vstrb-wizard-section="stay">';
		echo '<h3>' . esc_html__('Basic accommodation details', 'vachr-short-term-rental-reservations') . '</h3>';
		echo '<p class="description">' . esc_html__('Start with the name, guest capacity, and usual arrival and departure times.', 'vachr-short-term-rental-reservations') . '</p>';
		$unit_name = $post->post_status === 'auto-draft' ? '' : get_the_title($post);
		echo '<p><label><strong>' . esc_html__('Apartment name', 'vachr-short-term-rental-reservations') . '</strong><br><input id="vstrb-wizard-apartment-name" name="vstrb_unit_name" type="text" class="widefat" value="' . esc_attr($unit_name) . '" placeholder="' . esc_attr__('e.g. Blue House', 'vachr-short-term-rental-reservations') . '"></label></p>';
		echo '<div class="vstrb-wizard-time-grid">';
		echo '<p><label><strong>'.esc_html__('Check-in time', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
			 wp_kses(self::field('_vstrb_checkin', $checkin, 'time'), self::allowed_input_html()).'</p>';
		echo '<p><label><strong>'.esc_html__('Check-out time', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
			 wp_kses(self::field('_vstrb_checkout', $checkout, 'time'), self::allowed_input_html()).'</p>';
		echo '</div>';
		echo '<div class="vstrb-wizard-stay-grid">';
		echo '<p><label><strong>'.esc_html__('Capacity (guests)', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
			 wp_kses(self::field('_vstrb_capacity', $capacity, 'number', '2'), self::allowed_input_html()).'</p>';
		echo '<p><label><strong>'.esc_html__('Minimum stay (days)', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
			 wp_kses(self::field('_vstrb_min_stay', $min_stay, 'number', 'e.g. 2'), self::allowed_input_html()).'</p>';
		echo '<p><label><strong>'.esc_html__('Maximum stay (days)', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
			 wp_kses(self::field('_vstrb_max_stay', $max_stay, 'number', 'e.g. 30'), self::allowed_input_html()).'</p>';
		echo '</div>';
		$cal_mode_unit = get_post_meta($post->ID, '_vstrb_cal_price_mode', true);
		echo '<p><label><strong>'.esc_html__('Calendar Price Display', 'vachr-short-term-rental-reservations').'</strong></label><br/>';
		echo '<select name="_vstrb_cal_price_mode">';
		echo '<option value="">' . esc_html__('Global default', 'vachr-short-term-rental-reservations') . '</option>';
		echo '<option value="off"' . selected($cal_mode_unit, 'off', false) . '>' . esc_html__('Hidden', 'vachr-short-term-rental-reservations') . '</option>';
		echo '<option value="price"' . selected($cal_mode_unit, 'price', false) . '>' . esc_html__('Show nightly price', 'vachr-short-term-rental-reservations') . '</option>';
		echo '<option value="diff"' . selected($cal_mode_unit, 'diff', false) . '>' . esc_html__('Show discount / surcharge only', 'vachr-short-term-rental-reservations') . '</option>';
		echo '</select></p>';
		echo '</section>';

		echo '<section class="vstrb-unit-wizard-section" data-vstrb-wizard-section="pricing">';
		echo '<h3>' . esc_html__('Set the standard price', 'vachr-short-term-rental-reservations') . '</h3>';
		echo '<p class="description">' . esc_html__('Choose one pricing method and fill its amount for each active currency. Other amount fields may stay empty.', 'vachr-short-term-rental-reservations') . '</p>';
		self::render_pricing($post);
		echo '</section>';
		echo '<section class="vstrb-unit-wizard-section" data-vstrb-wizard-section="services">';
		echo '<h3>' . esc_html__('Services', 'vachr-short-term-rental-reservations') . '</h3>';
		echo '<p class="description">' . esc_html__('Choose optional or mandatory services offered to guests. Services may affect the booking price.', 'vachr-short-term-rental-reservations') . '</p>';
		self::render_services($post);
		echo '</section>';
		echo '<section class="vstrb-unit-wizard-section" data-vstrb-wizard-section="amenities">';
		echo '<h3>' . esc_html__('Amenities', 'vachr-short-term-rental-reservations') . '</h3>';
		echo '<p class="description">' . esc_html__('Select equipment and amenities that are already included and available in the apartment.', 'vachr-short-term-rental-reservations') . '</p>';
		self::render_amenities($post);
		echo '</section>';
		echo '<div class="vstrb-unit-wizard__actions" hidden><button type="button" class="button" data-vstrb-wizard-previous>' . esc_html__('Previous step', 'vachr-short-term-rental-reservations') . '</button><span class="vstrb-unit-wizard__spacer"></span><button type="button" class="button button-primary" data-vstrb-wizard-next>' . esc_html__('Next step', 'vachr-short-term-rental-reservations') . '</button><button type="button" class="button button-primary" data-vstrb-wizard-save hidden>' . esc_html__('Save apartment', 'vachr-short-term-rental-reservations') . '</button></div>';
	}

	public static function render_advanced($post) {
		$instructions = get_post_meta($post->ID, '_vstrb_instructions', true);
		$instructions_i18n = get_post_meta($post->ID, '_vstrb_instructions_i18n', true);
		$description_i18n = get_post_meta($post->ID, '_vstrb_description_i18n', true);
		$amenities_i18n = get_post_meta($post->ID, '_vstrb_amenities_i18n', true);
		$bank_transfer_i18n = get_post_meta($post->ID, '_vstrb_bank_transfer_instructions_i18n', true);
		$instructions_template = get_post_meta($post->ID, '_vstrb_instructions_template_id', true);
		$description_template = get_post_meta($post->ID, '_vstrb_description_template_id', true);
		$amenities_template = get_post_meta($post->ID, '_vstrb_amenities_template_id', true);
		$bank_transfer_template = get_post_meta($post->ID, '_vstrb_bank_transfer_instructions_template_id', true);
		$content_placeholders = get_post_meta($post->ID, '_vstrb_content_placeholders', true);
		$send_enabled = get_post_meta($post->ID, '_vstrb_arrival_instructions_enabled', true);
		$send_days = get_post_meta($post->ID, '_vstrb_arrival_instructions_days_before', true);
		$public_lookup_code = get_post_meta($post->ID, '_vstrb_public_lookup_code', true);
		if ($public_lookup_code === '') {
			$public_code_base = sanitize_title(get_the_title($post));
			if ($public_code_base === '') $public_code_base = 'apartment';
			$public_code_suffix = '-' . (int) $post->ID;
			$public_lookup_code = substr($public_code_base, 0, max(1, 64 - strlen($public_code_suffix))) . $public_code_suffix;
		}
		$public_lookup_names = get_post_meta($post->ID, '_vstrb_public_lookup_names', true);
		$templates = self::get_content_templates();

		echo '<b>' . esc_html__('Guest-facing Apartment Content', 'vachr-short-term-rental-reservations') . '</b>';
		echo '<div style="background:#fff;border:1px solid #dcdcde;border-left:4px solid #2271b1;padding:10px 12px;margin:8px 0 12px 0">';
		echo '<p style="margin:0 0 8px 0;color:#444">' . esc_html__('Choose a shared template for reusable descriptions, amenities, arrival instructions, and bank transfer instructions. Then fill only the apartment-specific overrides or private placeholders here.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<ol style="margin:0 0 8px 20px;color:#555">';
		echo '<li>' . esc_html__('Create or edit shared templates in Bookings -> Apartment Content.', 'vachr-short-term-rental-reservations') . '</li>';
		echo '<li>' . esc_html__('Assign a template below for this apartment.', 'vachr-short-term-rental-reservations') . '</li>';
		echo '<li>' . esc_html__('Add custom placeholders such as door_pin or wifi_password for values unique to this apartment.', 'vachr-short-term-rental-reservations') . '</li>';
		echo '<li>' . esc_html__('Fill language override text only when this apartment should differ from the shared template.', 'vachr-short-term-rental-reservations') . '</li>';
		echo '</ol>';
		echo '<p style="margin:0"><a class="button button-secondary" href="' . esc_url(admin_url('admin.php?page=vstrb-unit-content')) . '">' . esc_html__('Manage Apartment Content Templates', 'vachr-short-term-rental-reservations') . '</a></p>';
		echo '<p style="margin:12px 0 6px 0"><strong>' . esc_html__('Frontend shortcodes for this apartment', 'vachr-short-term-rental-reservations') . '</strong></p>';
		echo '<p style="margin:0 0 6px 0;color:#666">' . esc_html__('Use these examples on pages where you want to show the saved apartment content. Add language="CODE" to force a specific language.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<div style="background:#f6f7f7;border:1px solid #dcdcde;padding:8px;margin:0">';
		echo '<code style="display:block;margin-bottom:3px;user-select:all">' . esc_html(sprintf('[vstrb_unit_description unit="%d" language="cs"]', (int) $post->ID)) . '</code>';
		echo '<code style="display:block;margin-bottom:3px;user-select:all">' . esc_html(sprintf('[vstrb_unit_amenity_list unit="%d" language="cs"]', (int) $post->ID)) . '</code>';
		echo '<code style="display:block;margin-bottom:3px;user-select:all">' . esc_html(sprintf('[vstrb_unit_arrival_instructions unit="%d" language="cs"]', (int) $post->ID)) . '</code>';
		echo '<code style="display:block;margin-bottom:3px;user-select:all">' . esc_html(sprintf('[vstrb_unit_bank_transfer_instructions unit="%d" language="cs"]', (int) $post->ID)) . '</code>';
		$example_code = get_post_meta($post->ID, '_vstrb_public_lookup_code', true);
		$example_code = $example_code !== '' ? $example_code : 'blue-house';
		echo '<code style="display:block;margin-bottom:3px;user-select:all">' . esc_html(sprintf('[vstrb_unit_arrival_instructions_by_code code="%s" language="cs"]', $example_code)) . '</code>';
		echo '<code style="display:block;user-select:all">' . esc_html(sprintf('[vstrb_unit_arrival_instructions_by_reservation unit="%d" date_from_param="checkin" date_to_param="checkout" first_name_param="guest_first_name" last_name_param="guest_last_name" language="cs" available_days_before="1"]', (int) $post->ID)) . '</code>';
		echo '</div>';
		echo '</div>';
		echo '<details style="margin:10px 0;padding:10px;border:1px solid #dcdcde;background:#fff"><summary><strong>' . esc_html__('External identification (optional)', 'vachr-short-term-rental-reservations') . '</strong></summary>';
		echo '<p><label><strong>' . esc_html__('Public apartment code', 'vachr-short-term-rental-reservations') . '</strong><br>' . wp_kses(self::field('_vstrb_public_lookup_code', $public_lookup_code, 'text', 'e.g. blue-house-123'), self::allowed_input_html()) . '</label></p>';
		echo '<p style="color:#666">' . esc_html__('The code is generated automatically from the accommodation name. Change it only when an external link requires a specific value.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<p><strong>' . esc_html__('External accommodation names / aliases', 'vachr-short-term-rental-reservations') . '</strong></p>';
		echo '<p style="color:#666;margin-top:0">' . esc_html__('One name per line. Used when an external channel sends its own accommodation name instead of your WordPress apartment title.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<p><textarea style="width:100%;min-height:90px" name="_vstrb_public_lookup_names" placeholder="Booking.com Apartment Name">' . esc_textarea($public_lookup_names) . '</textarea></p>';
		echo '</details>';
		self::render_content_placeholders($content_placeholders);

		echo '<details open style="margin:10px 0;padding:10px;border:1px solid #dcdcde;background:#fff"><summary><strong>' . esc_html__('Arrival Instructions', 'vachr-short-term-rental-reservations') . '</strong></summary>';
		echo '<p><label>' . esc_html__('Template', 'vachr-short-term-rental-reservations') . '<br>';
		self::render_template_select('_vstrb_instructions_template_id', $instructions_template, $templates);
		echo '</label></p>';
		echo '<p><label><input type="checkbox" name="_vstrb_arrival_instructions_enabled" value="1" ' . checked($send_enabled, 1, false) . '> ' . esc_html__('Email arrival instructions automatically before check-in', 'vachr-short-term-rental-reservations') . '</label></p>';
		echo '<p><label>' . esc_html__('Send days before arrival', 'vachr-short-term-rental-reservations') . '<br>' . wp_kses(self::field('_vstrb_arrival_instructions_days_before', ($send_days !== '' ? $send_days : '1'), 'number', '1'), self::allowed_input_html()) . '</label></p>';
		echo '<p><label><strong>' . esc_html__('Legacy / default text', 'vachr-short-term-rental-reservations') . '</strong><br><textarea style="width:100%;min-height:120px" name="_vstrb_instructions">' . esc_textarea($instructions) . '</textarea></label></p>';
		self::render_i18n_textareas('vstrb_instructions_i18n', $instructions_i18n, 5);
		echo '</details>';

		echo '<details style="margin:10px 0;padding:10px;border:1px solid #dcdcde;background:#fff"><summary><strong>' . esc_html__('Apartment Description', 'vachr-short-term-rental-reservations') . '</strong></summary>';
		echo '<p><label>' . esc_html__('Template', 'vachr-short-term-rental-reservations') . '<br>';
		self::render_template_select('_vstrb_description_template_id', $description_template, $templates);
		echo '</label></p>';
		self::render_i18n_textareas('vstrb_description_i18n', $description_i18n, 5);
		echo '</details>';

		echo '<details style="margin:10px 0;padding:10px;border:1px solid #dcdcde;background:#fff"><summary><strong>' . esc_html__('Apartment Amenities Text', 'vachr-short-term-rental-reservations') . '</strong></summary>';
		echo '<p><label>' . esc_html__('Template', 'vachr-short-term-rental-reservations') . '<br>';
		self::render_template_select('_vstrb_amenities_template_id', $amenities_template, $templates);
		echo '</label></p>';
		self::render_i18n_textareas('vstrb_amenities_i18n', $amenities_i18n, 4);
		echo '</details>';

		echo '<details style="margin:10px 0;padding:10px;border:1px solid #dcdcde;background:#fff"><summary><strong>' . esc_html__('Bank Transfer Instructions', 'vachr-short-term-rental-reservations') . '</strong></summary>';
		echo '<p><label>' . esc_html__('Template', 'vachr-short-term-rental-reservations') . '<br>';
		self::render_template_select('_vstrb_bank_transfer_instructions_template_id', $bank_transfer_template, $templates);
		echo '</label></p>';
		echo '<p style="color:#666">' . esc_html__('Shown to guests after choosing bank transfer. Use {{iban}} for IBAN; {{account_number}} or {{bank_account}} for the domestic account number; {{account_country}} for the country; and {{variable_symbol}} or {{invoice_number}} as the reference.', 'vachr-short-term-rental-reservations') . '</p>';
		self::render_i18n_textareas('vstrb_bank_transfer_instructions_i18n', $bank_transfer_i18n, 5);
		echo '</details>';
		echo '<p style="color:#666">'.esc_html__('Use the Amenities section in Basic Apartment Settings for the translated amenity list.', 'vachr-short-term-rental-reservations').'</p>';

		echo '<hr/><h3>' . esc_html__('Contact Fields', 'vachr-short-term-rental-reservations') . '</h3>';
		self::render_contacts($post);
		echo '<hr/><h3>' . esc_html__('iCal Sync', 'vachr-short-term-rental-reservations') . '</h3>';
		self::render_ical($post);
	}

	public static function render_pricing($post) {
		$mode = get_post_meta($post->ID, '_vstrb_price_mode', true) ?: 'per_unit_night';
		$basic_discount_min_nights = get_post_meta($post->ID, '_vstrb_basic_discount_min_nights', true);
		$basic_discount_percent = get_post_meta($post->ID, '_vstrb_basic_discount_percent', true);
		$pppn = get_post_meta($post->ID, '_vstrb_price_per_person_night', true);
		$pun  = get_post_meta($post->ID, '_vstrb_price_per_unit_night', true);
		$per_stay = get_post_meta($post->ID, '_vstrb_price_per_stay', true);
		$per_night = get_post_meta($post->ID, '_vstrb_price_per_night', true);
		$per_person = get_post_meta($post->ID, '_vstrb_price_per_person', true);


		echo '<p><label><strong>'.esc_html__('Price mode', 'vachr-short-term-rental-reservations').'</strong></label><br/>';
		echo '<select name="_vstrb_price_mode" style="width:100%">';
		$opts = [
			'per_unit_night' => esc_html__('Price per unit / night', 'vachr-short-term-rental-reservations'),
			'per_person_night' => esc_html__('Price per person / night', 'vachr-short-term-rental-reservations'),
			'per_stay' => esc_html__('Price per stay (one-time)', 'vachr-short-term-rental-reservations'),
			'per_night' => esc_html__('Price per night', 'vachr-short-term-rental-reservations'),
			'per_person' => esc_html__('Price per person (one-time)', 'vachr-short-term-rental-reservations'),
		];
		foreach ($opts as $k=>$lab) {
			printf('<option value="%s" %s>%s</option>', esc_attr($k), selected($mode,$k,false), esc_html($lab));
		}
		echo '</select></p>';

		$currencies = array_map('trim', explode(',', get_option('vstrb_available_currencies', 'EUR')));
		$currencies = array_values(array_filter(array_unique(array_map('strtoupper', $currencies))));
		if (!$currencies) $currencies = ['EUR'];
		echo '<div data-vstrb-currency-list data-currencies="' . esc_attr(implode(',', $currencies)) . '">';
		foreach ($currencies as $c) {
			$c_pppn = get_post_meta($post->ID, '_vstrb_price_per_person_night_' . $c, true);
			$c_pun  = get_post_meta($post->ID, '_vstrb_price_per_unit_night_' . $c, true);
			$c_per_stay = get_post_meta($post->ID, '_vstrb_price_per_stay_' . $c, true);
			$c_per_night = get_post_meta($post->ID, '_vstrb_price_per_night_' . $c, true);
			$c_per_person = get_post_meta($post->ID, '_vstrb_price_per_person_' . $c, true);
			$c_refundable_deposit = get_post_meta($post->ID, '_vstrb_refundable_deposit_' . $c, true);

			echo '<div class="vstrb-currency-pricing" data-currency="' . esc_attr($c) . '" style="background:#f9f9f9;padding:10px;margin-bottom:10px;border:1px solid #ddd;">';
			echo '<h4>' . sprintf(
				/* translators: %s: currency code */
				esc_html__('Prices in %s', 'vachr-short-term-rental-reservations'), 
				esc_html($c)
			) . '</h4>';
			echo '<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">';
			echo '<p data-vstrb-price-field="per_person_night"><label><strong>'.esc_html__('Price per person / night', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
				 wp_kses(self::field('_vstrb_price_per_person_night_'.$c, $c_pppn, 'number', 'e.g. 35'), self::allowed_input_html()).'</p>';
			echo '<p data-vstrb-price-field="per_unit_night"><label><strong>'.esc_html__('Price per unit / night', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
				 wp_kses(self::field('_vstrb_price_per_unit_night_'.$c, $c_pun, 'number', 'e.g. 120'), self::allowed_input_html()).'</p>';
			echo '<p data-vstrb-price-field="per_stay"><label><strong>'.esc_html__('Price per stay (one-time)', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
				 wp_kses(self::field('_vstrb_price_per_stay_'.$c, $c_per_stay, 'number', 'e.g. 500'), self::allowed_input_html()).'</p>';
			echo '<p data-vstrb-price-field="per_night"><label><strong>'.esc_html__('Price per night', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
				 wp_kses(self::field('_vstrb_price_per_night_'.$c, $c_per_night, 'number', 'e.g. 100'), self::allowed_input_html()).'</p>';
			echo '<p data-vstrb-price-field="per_person"><label><strong>'.esc_html__('Price per person (one-time)', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
				 wp_kses(self::field('_vstrb_price_per_person_'.$c, $c_per_person, 'number', 'e.g. 50'), self::allowed_input_html()).'</p>';
			echo '</div>';
			echo '<details class="vstrb-price-advanced"><summary>' . esc_html__('Refundable deposit (optional)', 'vachr-short-term-rental-reservations') . '</summary><p><label><strong>'.esc_html__('Refundable deposit (per stay)', 'vachr-short-term-rental-reservations').'</strong></label><br/>'.
				 wp_kses(self::field('_vstrb_refundable_deposit_'.$c, $c_refundable_deposit, 'number', 'e.g. 200'), self::allowed_input_html()).'</p></details>';
			echo '</div>';
		}
		echo '</div>';
		echo '<div class="vstrb-wizard-inline-add"><div><strong>' . esc_html__('Add another currency', 'vachr-short-term-rental-reservations') . '</strong><span>' . esc_html__('Enter a three-letter currency code, for example USD or GBP.', 'vachr-short-term-rental-reservations') . '</span></div><div class="vstrb-wizard-inline-add__controls"><input type="text" maxlength="3" data-vstrb-new-currency placeholder="USD"><button type="button" class="button" data-vstrb-add-currency><span class="dashicons dashicons-plus-alt2" aria-hidden="true"></span> ' . esc_html__('Add currency', 'vachr-short-term-rental-reservations') . '</button></div><input type="hidden" name="vstrb_wizard_currencies" value="' . esc_attr(implode(',', $currencies)) . '"><p class="vstrb-wizard-inline-error" data-vstrb-currency-error hidden>' . esc_html__('Enter a new three-letter currency code.', 'vachr-short-term-rental-reservations') . '</p></div>';

		echo '<div class="vstrb-basic-discount">';
		echo '<div><strong>' . esc_html__('Basic long-stay discount', 'vachr-short-term-rental-reservations') . '</strong><p>' . esc_html__('Optionally offer one percentage discount when a reservation reaches a minimum number of nights.', 'vachr-short-term-rental-reservations') . '</p></div>';
		echo '<input type="hidden" name="vstrb_basic_discount_present" value="1">';
		echo '<div class="vstrb-basic-discount__fields"><label>' . esc_html__('Apply from (nights)', 'vachr-short-term-rental-reservations') . '<input type="number" min="2" step="1" name="_vstrb_basic_discount_min_nights" value="' . esc_attr($basic_discount_min_nights) . '" placeholder="7"></label><label>' . esc_html__('Discount (%)', 'vachr-short-term-rental-reservations') . '<input type="number" min="0" max="100" step="0.1" name="_vstrb_basic_discount_percent" value="' . esc_attr($basic_discount_percent) . '" placeholder="10"></label></div>';
		echo '</div>';
		echo '<div class="vstrb-advanced-pricing-link"><div><strong>' . esc_html__('Need advanced pricing policies?', 'vachr-short-term-rental-reservations') . '</strong><p>' . esc_html__('Configure seasons, weekends, holidays, date ranges, surcharges, and additional discounts in the full pricing section.', 'vachr-short-term-rental-reservations') . '</p></div><a class="button button-secondary" target="_blank" rel="noopener noreferrer" href="' . esc_url(add_query_arg(['page' => 'vstrb-rates', 'unit_id' => (int) $post->ID], admin_url('admin.php'))) . '">' . esc_html__('Open advanced pricing', 'vachr-short-term-rental-reservations') . '</a></div>';
	}

	public static function render_services($post) {
		global $wpdb;
		$t = VSTRB_DB::tables();

		$cache_key_all_services = 'vstrb_cpt_services_all';
		$all_services = wp_cache_get($cache_key_all_services, 'vstrb_cpt');
		if (false === $all_services) {
			$all_services = $wpdb->get_results("SELECT * FROM {$t['services']} WHERE is_active=1 ORDER BY sort_order ASC, name ASC");
			wp_cache_set($cache_key_all_services, $all_services, 'vstrb_cpt', 300);
		}
		// Get dictionary of unit services keyed by service_id and currency
		$cache_key_unit_services = 'vstrb_cpt_unit_services_' . (int) $post->ID;
		$unit_services_raw = wp_cache_get($cache_key_unit_services, 'vstrb_cpt');
		if (false === $unit_services_raw) {
			$unit_services_raw = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$t['unit_services']} WHERE unit_id=%d", $post->ID));
			wp_cache_set($cache_key_unit_services, $unit_services_raw, 'vstrb_cpt', 300);
		}
		$my_services = [];
		foreach ($unit_services_raw as $us) {
			$my_services[$us->service_id][$us->currency] = $us;
		}

		$currencies = array_map('trim', explode(',', get_option('vstrb_available_currencies', 'EUR')));
		$pricing_options = self::get_service_pricing_types();

		echo '<p style="color:#666">'.esc_html__('Select services available for this unit. You can override price or make them mandatory.', 'vachr-short-term-rental-reservations').'</p>';
		echo '<div class="vstrb-wizard-catalog-add" data-vstrb-catalog-add="service">';
		echo '<strong>' . esc_html__('Add a new service', 'vachr-short-term-rental-reservations') . '</strong><p class="description">' . esc_html__('The service will be added to the shared catalog and selected for this apartment after saving.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<div class="vstrb-wizard-catalog-add__fields"><label>' . esc_html__('Service name', 'vachr-short-term-rental-reservations') . '<input type="text" data-vstrb-item-name placeholder="' . esc_attr__('e.g. Breakfast', 'vachr-short-term-rental-reservations') . '"></label><label>' . esc_html__('Pricing Type', 'vachr-short-term-rental-reservations') . '<select data-vstrb-item-type>';
		foreach ($pricing_options as $key => $label) {
			echo '<option value="' . esc_attr($key) . '">' . esc_html($label) . '</option>';
		}
		echo '</select></label><label>' . esc_html__('Default Price', 'vachr-short-term-rental-reservations') . '<input type="number" min="0" step="0.01" data-vstrb-item-price value="0"></label><button type="button" class="button" data-vstrb-add-catalog-item><span class="dashicons dashicons-plus-alt2" aria-hidden="true"></span> ' . esc_html__('Add service', 'vachr-short-term-rental-reservations') . '</button></div><div class="vstrb-wizard-new-items" data-vstrb-new-items></div><p class="vstrb-wizard-inline-error" data-vstrb-item-error hidden>' . esc_html__('Enter a name before adding the item.', 'vachr-short-term-rental-reservations') . '</p></div>';

		if (!$all_services) {
			echo '<p>'.esc_html__('No active services defined yet.', 'vachr-short-term-rental-reservations').'</p>';
			return;
		}

		echo '<table class="widefat striped">';
		echo '<thead><tr>
			<th style="width:30px;"><input type="checkbox" onclick="jQuery(\'.vstrb-svc-cb\').prop(\'checked\',this.checked)"></th>
			<th>'.esc_html__('Service', 'vachr-short-term-rental-reservations').'</th>
			<th>'.esc_html__('Currency', 'vachr-short-term-rental-reservations').'</th>
			<th>'.esc_html__('Default Price', 'vachr-short-term-rental-reservations').'</th>
			<th>'.esc_html__('Custom Price', 'vachr-short-term-rental-reservations').'</th>
			<th>'.esc_html__('Pricing Type', 'vachr-short-term-rental-reservations').'</th>
			<th>'.esc_html__('Mode', 'vachr-short-term-rental-reservations').'</th>
		</tr></thead><tbody>';

		foreach ($all_services as $s) {
			$row_span = count($currencies);
			$first_c = true;
			foreach ($currencies as $c) {
				$has = isset($my_services[$s->id][$c]);
				$us = $has ? $my_services[$s->id][$c] : null;
				$checked = $has ? 'checked' : '';
				
				// Custom Price
				$cust_price = ($has && !is_null($us->price_override)) ? (float)$us->price_override : '';
				
				// Custom Pricing Type
				$cust_pricing_type = ($has && !empty($us->pricing_type)) ? $us->pricing_type : '';

				// Optional/Mandatory
				$is_opt = ($has && isset($us->is_optional)) ? (int)$us->is_optional : (isset($s->is_optional) ? (int)$s->is_optional : 1); 

				// Default Price from service meta
				$cache_key_def_price = 'vstrb_cpt_service_price_' . (int) $s->id . '_' . $c;
				$def_price = wp_cache_get($cache_key_def_price, 'vstrb_cpt');
				if (false === $def_price) {
					$def_price = $wpdb->get_var($wpdb->prepare("SELECT meta_value FROM {$t['service_meta']} WHERE service_id=%d AND meta_key=%s", $s->id, 'price_' . $c));
					wp_cache_set($cache_key_def_price, $def_price, 'vstrb_cpt', 300);
				}

				echo '<tr>';
				if ($first_c) {
					echo '<td rowspan="'.(int)$row_span.'"><input type="checkbox" class="vstrb-svc-cb" name="vstrb_services['.(int)$s->id.'][enable]" value="1" '.esc_attr(isset($my_services[$s->id])?'checked':'').'></td>';
					echo '<td rowspan="'.(int)$row_span.'"><strong>'.esc_html($s->name).'</strong><br><small style="color:#888">'.esc_html($s->code).'</small></td>';
				}
				echo '<td><strong>'.esc_html($c).'</strong></td>';
				echo '<td>'.esc_html($def_price ?: 0).' <small>('.esc_html($pricing_options[$s->pricing_type] ?? $s->pricing_type).')</small></td>';
				echo '<td><input type="number" step="0.01" name="vstrb_services['.(int)$s->id.']['.esc_attr($c).'][price]" value="'.esc_attr($cust_price).'" placeholder="'.esc_attr($def_price ?: 0).'" style="width:80px"></td>';
				echo '<td>
					<select name="vstrb_services['.(int)$s->id.']['.esc_attr($c).'][pricing_type]">
						<option value="">-- ' . esc_html__('Default', 'vachr-short-term-rental-reservations') . ' --</option>';
				foreach ($pricing_options as $pk => $pv) {
					printf('<option value="%s" %s>%s</option>', esc_attr($pk), selected($cust_pricing_type, $pk, false), esc_html($pv));
				}
				echo '</select>
				</td>';
				echo '<td>
					<select name="vstrb_services['.(int)$s->id.']['.esc_attr($c).'][optional]">
						<option value="1" '.selected($is_opt,1,false).'>'.esc_html__('Optional', 'vachr-short-term-rental-reservations').'</option>
						<option value="0" '.selected($is_opt,0,false).'>'.esc_html__('Mandatory', 'vachr-short-term-rental-reservations').'</option>
					</select>
				</td>';
				echo '</tr>';
				$first_c = false;
			}
		}
		echo '</tbody></table>';
	}

	private static function amenity_category_labels() {
		return [
			'general' => esc_html__('General', 'vachr-short-term-rental-reservations'),
			'connectivity' => esc_html__('Connectivity', 'vachr-short-term-rental-reservations'),
			'entertainment' => esc_html__('Entertainment', 'vachr-short-term-rental-reservations'),
			'comfort' => esc_html__('Comfort', 'vachr-short-term-rental-reservations'),
			'bedroom' => esc_html__('Bedroom', 'vachr-short-term-rental-reservations'),
			'kitchen' => esc_html__('Kitchen', 'vachr-short-term-rental-reservations'),
			'bathroom' => esc_html__('Bathroom', 'vachr-short-term-rental-reservations'),
			'laundry' => esc_html__('Laundry', 'vachr-short-term-rental-reservations'),
			'outdoor' => esc_html__('Outdoor', 'vachr-short-term-rental-reservations'),
			'wellness' => esc_html__('Wellness', 'vachr-short-term-rental-reservations'),
			'parking' => esc_html__('Parking', 'vachr-short-term-rental-reservations'),
			'access' => esc_html__('Access', 'vachr-short-term-rental-reservations'),
			'accessibility' => esc_html__('Accessibility', 'vachr-short-term-rental-reservations'),
			'family' => esc_html__('Family', 'vachr-short-term-rental-reservations'),
			'safety' => esc_html__('Safety', 'vachr-short-term-rental-reservations'),
			'rules' => esc_html__('Rules', 'vachr-short-term-rental-reservations'),
		];
	}

	public static function render_amenities($post) {
		global $wpdb;
		$t = VSTRB_DB::tables();
		$all_amenities = $wpdb->get_results("SELECT * FROM {$t['amenities']} WHERE is_active=1 ORDER BY sort_order ASC, category ASC, name ASC");
		$my_amenities = array_map('intval', (array) $wpdb->get_col($wpdb->prepare("SELECT amenity_id FROM {$t['unit_amenities']} WHERE unit_id=%d", $post->ID)));
		$labels = self::amenity_category_labels();

		echo '<p style="color:#666">' . esc_html__('Select amenities available in this apartment. Labels can be translated globally in Bookings -> Amenities.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<div class="vstrb-wizard-catalog-add" data-vstrb-catalog-add="amenity">';
		echo '<strong>' . esc_html__('Add new equipment or amenity', 'vachr-short-term-rental-reservations') . '</strong><p class="description">' . esc_html__('The item will be added to the shared catalog and selected for this apartment after saving.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<div class="vstrb-wizard-catalog-add__fields"><label>' . esc_html__('Name', 'vachr-short-term-rental-reservations') . '<input type="text" data-vstrb-item-name placeholder="' . esc_attr__('e.g. Coffee machine', 'vachr-short-term-rental-reservations') . '"></label><label>' . esc_html__('Category', 'vachr-short-term-rental-reservations') . '<select data-vstrb-item-category>';
		foreach ($labels as $key => $label) {
			echo '<option value="' . esc_attr($key) . '">' . esc_html($label) . '</option>';
		}
		echo '</select></label><button type="button" class="button" data-vstrb-add-catalog-item><span class="dashicons dashicons-plus-alt2" aria-hidden="true"></span> ' . esc_html__('Add amenity', 'vachr-short-term-rental-reservations') . '</button></div><div class="vstrb-wizard-new-items" data-vstrb-new-items></div><p class="vstrb-wizard-inline-error" data-vstrb-item-error hidden>' . esc_html__('Enter a name before adding the item.', 'vachr-short-term-rental-reservations') . '</p></div>';
		if (!$all_amenities) {
			echo '<p>' . esc_html__('No active amenities defined yet.', 'vachr-short-term-rental-reservations') . '</p>';
			return;
		}

		$grouped = [];
		foreach ($all_amenities as $amenity) {
			$category = sanitize_key((string) ($amenity->category ?? 'general'));
			if ($category === '') {
				$category = 'general';
			}
			if (!isset($grouped[$category])) {
				$grouped[$category] = [];
			}
			$grouped[$category][] = $amenity;
		}

		echo '<div class="vstrb-admin-amenities-grid">';
		foreach ($grouped as $category => $items) {
			echo '<fieldset class="vstrb-admin-amenity-group">';
			echo '<legend>' . esc_html($labels[$category] ?? $category) . '</legend>';
			foreach ($items as $item) {
				$checked = in_array((int) $item->id, $my_amenities, true) ? 'checked' : '';
				printf(
					'<label><input type="checkbox" name="vstrb_amenities[]" value="%d" %s> <span>%s</span> <small><code>%s</code></small></label>',
					(int) $item->id,
					esc_attr($checked),
					// phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText -- Amenity names are seeded/user-defined labels with bundled PO fallbacks.
					esc_html(__((string) $item->name, 'vachr-short-term-rental-reservations')),
					esc_html($item->code)
				);
			}
			echo '</fieldset>';
		}
		echo '</div>';
	}

	public static function render_contacts($post) {
		global $wpdb;
		$t = VSTRB_DB::tables();

		$cache_key_all_fields = 'vstrb_cpt_contact_fields_all';
		$all_fields = wp_cache_get($cache_key_all_fields, 'vstrb_cpt');
		if (false === $all_fields) {
			$all_fields = $wpdb->get_results("SELECT id, field_key FROM {$t['contact_fields']} WHERE is_active=1 ORDER BY sort_order ASC");
			wp_cache_set($cache_key_all_fields, $all_fields, 'vstrb_cpt', 300);
		}
		$cache_key_my_fields = 'vstrb_cpt_unit_contact_fields_' . (int) $post->ID;
		$my_fields = wp_cache_get($cache_key_my_fields, 'vstrb_cpt');
		if (false === $my_fields) {
			$my_fields = $wpdb->get_col($wpdb->prepare("SELECT field_id FROM {$t['unit_contact_fields']} WHERE unit_id=%d", $post->ID));
			wp_cache_set($cache_key_my_fields, $my_fields, 'vstrb_cpt', 300);
		}

		echo '<p style="color:#666">'.esc_html__('Select custom contact fields to be displayed in the booking form for this apartment.', 'vachr-short-term-rental-reservations').'</p>';
		echo '<div class="vstrb-wizard-catalog-add" data-vstrb-catalog-add="contact">';
		echo '<strong>' . esc_html__('Add a contact field', 'vachr-short-term-rental-reservations') . '</strong><p class="description">' . esc_html__('The field will be added to the shared catalog and selected for this apartment after saving.', 'vachr-short-term-rental-reservations') . '</p>';
		echo '<div class="vstrb-wizard-catalog-add__fields"><label>' . esc_html__('Field label', 'vachr-short-term-rental-reservations') . '<input type="text" data-vstrb-item-name placeholder="' . esc_attr__('e.g. Arrival note', 'vachr-short-term-rental-reservations') . '"></label><button type="button" class="button" data-vstrb-add-catalog-item><span class="dashicons dashicons-plus-alt2" aria-hidden="true"></span> ' . esc_html__('Add field', 'vachr-short-term-rental-reservations') . '</button></div><div class="vstrb-wizard-new-items" data-vstrb-new-items></div><p class="vstrb-wizard-inline-error" data-vstrb-item-error hidden>' . esc_html__('Enter a name before adding the item.', 'vachr-short-term-rental-reservations') . '</p></div>';

		if (!$all_fields) {
			echo '<p>'.esc_html__('No active contact fields defined yet.', 'vachr-short-term-rental-reservations').'</p>';
			return;
		}

		echo '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px">';
		foreach ($all_fields as $f) {
			$checked = in_array($f->id, $my_fields) ? 'checked' : '';
			// Get a label for current user language to show what it is
			$user_locale = substr(get_user_locale(), 0, 2);
			$cache_key_label_locale = 'vstrb_cpt_field_label_' . (int) $f->id . '_' . $user_locale;
			$label = wp_cache_get($cache_key_label_locale, 'vstrb_cpt');
			if (false === $label) {
				$label = $wpdb->get_var($wpdb->prepare("SELECT meta_value FROM {$t['contact_field_meta']} WHERE field_id=%d AND meta_key='label' AND locale=%s", $f->id, $user_locale));
				wp_cache_set($cache_key_label_locale, $label, 'vstrb_cpt', 300);
			}
			if (!$label) {
				$cache_key_label_global = 'vstrb_cpt_field_label_' . (int) $f->id . '_global';
				$label = wp_cache_get($cache_key_label_global, 'vstrb_cpt');
				if (false === $label) {
					$label = $wpdb->get_var($wpdb->prepare("SELECT meta_value FROM {$t['contact_field_meta']} WHERE field_id=%d AND meta_key='label' AND (locale IS NULL OR locale='')", $f->id));
					wp_cache_set($cache_key_label_global, $label, 'vstrb_cpt', 300);
				}
			}
			if (!$label) $label = $f->field_key;

			printf('<label><input type="checkbox" name="vstrb_contact_fields[]" value="%d" %s> %s <small>(<code>%s</code>)</small></label>', (int)$f->id, esc_attr($checked), esc_html($label), esc_html($f->field_key));
		}
		echo '</div>';
	}

	public static function render_ical($post) {
		$token = get_post_meta($post->ID, '_vstrb_ical_token', true);
		if (!$token) {
			$token = wp_generate_password(20, false, false);
			update_post_meta($post->ID, '_vstrb_ical_token', $token);
		}
		$export_url = rest_url("vstrb/v1/ical/export/{$post->ID}/{$token}.ics");
		$direct_export_url = rest_url("vstrb/v1/ical/export/direct/{$post->ID}/{$token}.ics");

		echo '<p><strong>'.esc_html__('iCal Export URL', 'vachr-short-term-rental-reservations').'</strong></p>';
		echo '<p><strong>'.esc_html__('All reservations, including imported iCal blocks', 'vachr-short-term-rental-reservations').'</strong></p>';
		echo '<p><input style="width:100%" readonly value="'.esc_attr($export_url).'" /></p>';
		echo '<p><strong>'.esc_html__('Direct booking-system reservations only', 'vachr-short-term-rental-reservations').'</strong></p>';
		echo '<p><input style="width:100%" readonly value="'.esc_attr($direct_export_url).'" /></p>';
		echo '<p style="color:#666">'.esc_html__('Paste this URL into Airbnb/Booking as a calendar import.', 'vachr-short-term-rental-reservations').'</p>';
		echo '<p><a class="button button-secondary" href="'.esc_url(admin_url('admin.php?page=vstrb-ical&unit_id='.(int) $post->ID)).'">'.esc_html__('Configure Imports', 'vachr-short-term-rental-reservations').'</a></p>';
	}

	public static function save_meta($post_id, $post) {
		if (
			!isset($_POST['vstrb_unit_meta_nonce']) ||
			!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['vstrb_unit_meta_nonce'])), 'vstrb_unit_meta')
		) return;
		if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
		if (!current_user_can('edit_post', $post_id)) return;

		$unit_name_raw = filter_input(INPUT_POST, 'vstrb_unit_name', FILTER_UNSAFE_RAW);
		if (null !== $unit_name_raw && false !== $unit_name_raw) {
			$unit_name = sanitize_text_field(wp_unslash((string) $unit_name_raw));
			if ($unit_name !== '' && $unit_name !== $post->post_title) {
				remove_action('save_post_vstrb_unit', [__CLASS__, 'save_meta'], 10);
				wp_update_post([
					'ID' => $post_id,
					'post_title' => $unit_name,
				]);
				add_action('save_post_vstrb_unit', [__CLASS__, 'save_meta'], 10, 2);
				$post->post_title = $unit_name;
			}
		}

		$fields = [
			'_vstrb_checkin','_vstrb_checkout','_vstrb_instructions','_vstrb_capacity',
			'_vstrb_price_mode',

			'_vstrb_min_stay', '_vstrb_max_stay', '_vstrb_cal_price_mode',
			'_vstrb_basic_discount_min_nights', '_vstrb_basic_discount_percent',
			'_vstrb_public_lookup_code',
			'_vstrb_instructions_template_id', '_vstrb_description_template_id', '_vstrb_amenities_template_id', '_vstrb_bank_transfer_instructions_template_id',
			'_vstrb_arrival_instructions_days_before'
		];
		$currencies = array_map('trim', explode(',', get_option('vstrb_available_currencies', 'EUR')));
		$wizard_currencies_raw = filter_input(INPUT_POST, 'vstrb_wizard_currencies', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
		if (null !== $wizard_currencies_raw && false !== $wizard_currencies_raw) {
			foreach (explode(',', (string) $wizard_currencies_raw) as $currency) {
				$currency = strtoupper(trim(sanitize_text_field($currency)));
				if (preg_match('/^[A-Z]{3}$/', $currency)) {
					$currencies[] = $currency;
				}
			}
			$currencies = array_values(array_unique(array_filter($currencies)));
			update_option('vstrb_available_currencies', implode(',', $currencies));
		}
		foreach ($currencies as $c) {
			$fields[] = '_vstrb_price_per_person_night_' . $c;
			$fields[] = '_vstrb_price_per_unit_night_' . $c;
			$fields[] = '_vstrb_price_per_stay_' . $c;
			$fields[] = '_vstrb_price_per_night_' . $c;
			$fields[] = '_vstrb_price_per_person_' . $c;
			$fields[] = '_vstrb_refundable_deposit_' . $c;
		}

		foreach ($fields as $f) {
			$raw = filter_input(INPUT_POST, $f, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
			if (null === $raw || false === $raw) continue;
			if ($f === '_vstrb_instructions') $val = wp_kses_post(html_entity_decode((string) $raw, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
			elseif ($f === '_vstrb_public_lookup_code') $val = preg_replace('/[^a-z0-9_\-]/', '', strtolower(trim(sanitize_text_field((string) $raw))));
			elseif (in_array($f, ['_vstrb_checkin', '_vstrb_checkout'], true)) {
				$val = sanitize_text_field((string) $raw);
				if (!preg_match('/^([01]\d|2[0-3]):[0-5]\d$/', $val)) continue;
			}
			elseif ($f === '_vstrb_capacity') $val = (string) max(1, absint($raw));
			elseif (in_array($f, ['_vstrb_min_stay', '_vstrb_max_stay', '_vstrb_arrival_instructions_days_before'], true)) $val = (string) max(0, absint($raw));
			elseif ($f === '_vstrb_basic_discount_min_nights') $val = (string) max(0, absint($raw));
			elseif ($f === '_vstrb_basic_discount_percent') $val = (string) min(100, max(0, (float) $raw));
			elseif ($f === '_vstrb_price_mode') {
				$val = sanitize_key((string) $raw);
				if (!in_array($val, ['per_unit_night', 'per_person_night', 'per_stay', 'per_night', 'per_person'], true)) $val = 'per_unit_night';
			}
			elseif (preg_match('/^_vstrb_(?:price_|refundable_deposit_)/', $f)) $val = (string) max(0, (float) $raw);
			else $val = sanitize_text_field((string) $raw);
			update_post_meta($post_id, $f, $val);
		}
		$stored_min_stay = max(0, (int) get_post_meta($post_id, '_vstrb_min_stay', true));
		$stored_max_stay = max(0, (int) get_post_meta($post_id, '_vstrb_max_stay', true));
		if ($stored_min_stay > 0 && $stored_max_stay > 0 && $stored_max_stay < $stored_min_stay) {
			update_post_meta($post_id, '_vstrb_max_stay', $stored_min_stay);
		}
		if ((string) get_post_meta($post_id, '_vstrb_public_lookup_code', true) === '') {
			$code_base = sanitize_title(get_the_title($post_id));
			if ($code_base === '') $code_base = 'apartment';
			$code_suffix = '-' . (int) $post_id;
			$code_base = substr($code_base, 0, max(1, 64 - strlen($code_suffix)));
			update_post_meta($post_id, '_vstrb_public_lookup_code', $code_base . $code_suffix);
		}

		// Save Pause status
		$is_paused = (isset($_POST['_vstrb_is_paused']) && '1' === sanitize_text_field(wp_unslash($_POST['_vstrb_is_paused']))) ? 1 : 0;
		update_post_meta($post_id, '_vstrb_is_paused', $is_paused);
		$send_enabled = (isset($_POST['_vstrb_arrival_instructions_enabled']) && '1' === sanitize_text_field(wp_unslash($_POST['_vstrb_arrival_instructions_enabled']))) ? 1 : 0;
		update_post_meta($post_id, '_vstrb_arrival_instructions_enabled', $send_enabled);
		update_post_meta($post_id, '_vstrb_instructions_i18n', self::sanitize_i18n_textarea_array('vstrb_instructions_i18n'));
		update_post_meta($post_id, '_vstrb_description_i18n', self::sanitize_i18n_textarea_array('vstrb_description_i18n'));
		update_post_meta($post_id, '_vstrb_amenities_i18n', self::sanitize_i18n_textarea_array('vstrb_amenities_i18n'));
		update_post_meta($post_id, '_vstrb_bank_transfer_instructions_i18n', self::sanitize_i18n_textarea_array('vstrb_bank_transfer_instructions_i18n'));
		$public_names_raw = filter_input(INPUT_POST, '_vstrb_public_lookup_names', FILTER_UNSAFE_RAW);
		if (null !== $public_names_raw && false !== $public_names_raw) {
			$public_names = array_filter(array_map(static function($line) {
				return sanitize_text_field(trim((string) $line));
			}, preg_split('/\r\n|\r|\n/', wp_unslash((string) $public_names_raw))));
			update_post_meta($post_id, '_vstrb_public_lookup_names', implode("\n", array_values($public_names)));
		}
		update_post_meta($post_id, '_vstrb_content_placeholders', self::sanitize_content_placeholders_from_post());
		update_post_meta($post_id, '_vstrb_closed_periods', self::sanitize_closed_periods_from_post());
		$v = wp_cache_get('vstrb_v_' . $post_id, 'vstrb_calendar');
		if (false === $v) {
			wp_cache_set('vstrb_v_' . $post_id, 2, 'vstrb_calendar', 3600);
		} else {
			wp_cache_incr('vstrb_v_' . $post_id, 1, 'vstrb_calendar');
		}

		// Save Services
		global $wpdb;
		$t = VSTRB_DB::tables();
		$has_basic_discount = null !== filter_input(INPUT_POST, 'vstrb_basic_discount_present', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
		if ($has_basic_discount) {
			$basic_discount_name = 'Long-stay discount';
			$basic_discount_priority = 9876;
			$wpdb->delete($t['rates'], [
				'unit_id' => $post_id,
				'name' => $basic_discount_name,
				'priority' => $basic_discount_priority,
			]);
			$discount_min_nights = max(0, (int) get_post_meta($post_id, '_vstrb_basic_discount_min_nights', true));
			$discount_percent = min(100, max(0, (float) get_post_meta($post_id, '_vstrb_basic_discount_percent', true)));
			if ($discount_min_nights >= 2 && $discount_percent > 0) {
				$discount_labels = wp_json_encode([
					'en' => __('Long-stay discount', 'vachr-short-term-rental-reservations'),
					'cs' => 'Sleva za delší pobyt',
				]);
				foreach ($currencies as $currency) {
					$wpdb->insert($t['rates'], [
						'unit_id' => $post_id,
						'name' => $basic_discount_name,
						'date_from' => '1970-01-01',
						'date_to' => '9999-12-31',
						'apply_mode' => 'always',
						'months_mask' => '',
						'rule' => 'fixed',
						'operation' => 'discount_percent_stay',
						'price' => $discount_percent,
						'currency' => $currency,
						'min_nights' => $discount_min_nights,
						'priority' => $basic_discount_priority,
						'highlight_in_calendar' => 0,
						'is_active' => 1,
						'show_name_in_breakdown' => 1,
						'breakdown_label_i18n' => $discount_labels,
					]);
				}
			}
			self::bump_rates_cache_version();
		}
		$wpdb->delete($t['unit_services'], ['unit_id' => $post_id]);
		
		$posted_services = self::post_array('vstrb_services');
		if (!empty($posted_services)) {
			foreach ($posted_services as $sid => $svc_data) {
				if (empty($svc_data['enable'])) continue;

				foreach ($currencies as $c) {
					$c_data = $svc_data[$c] ?? [];
					$price_override = (isset($c_data['price']) && $c_data['price'] !== '') ? max(0, (float) sanitize_text_field($c_data['price'])) : null;
					$is_opt = (isset($c_data['optional']) && '0' === sanitize_text_field($c_data['optional'])) ? 0 : 1;
					$pricing_type = !empty($c_data['pricing_type']) ? sanitize_text_field($c_data['pricing_type']) : null;
					
					$wpdb->insert($t['unit_services'], [
						'unit_id' => $post_id,
						'service_id' => (int)$sid,
						'currency' => $c,
						'is_optional' => $is_opt,
						'price_override' => $price_override,
						'pricing_type' => $pricing_type
					]);
				}
			}
		}
		$new_services = self::post_array('vstrb_new_services');
		foreach ($new_services as $new_service) {
			if (!is_array($new_service)) continue;
			$name = sanitize_text_field($new_service['name'] ?? '');
			if ($name === '') continue;
			$pricing_type = sanitize_key($new_service['pricing_type'] ?? 'per_stay');
			if (!array_key_exists($pricing_type, self::get_service_pricing_types())) $pricing_type = 'per_stay';
			$price = max(0, (float) sanitize_text_field($new_service['price'] ?? '0'));
			$base_code = strtoupper(str_replace('-', '_', sanitize_title($name)));
			$base_code = substr($base_code !== '' ? $base_code : 'SERVICE', 0, 54);
			$code = $base_code;
			$suffix = 2;
			while ($wpdb->get_var($wpdb->prepare("SELECT id FROM {$t['services']} WHERE code=%s", $code))) {
				$code = substr($base_code, 0, 54) . '_' . $suffix++;
			}
			$wpdb->insert($t['services'], ['name' => $name, 'code' => $code, 'pricing_type' => $pricing_type, 'price' => $price, 'is_active' => 1, 'is_optional' => 1]);
			$service_id = (int) $wpdb->insert_id;
			if ($service_id > 0) {
				foreach ($currencies as $currency) {
					// These are column names in a plugin-owned metadata table, not a metadata lookup query.
					// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value
					$wpdb->insert($t['service_meta'], ['service_id' => $service_id, 'meta_key' => 'price_' . $currency, 'meta_value' => $price, 'locale' => null]);
					$wpdb->insert($t['unit_services'], ['unit_id' => $post_id, 'service_id' => $service_id, 'currency' => $currency, 'is_optional' => 1, 'price_override' => null, 'pricing_type' => null]);
				}
			}
		}
		wp_cache_delete('vstrb_cpt_services_all', 'vstrb_cpt');
		wp_cache_delete('vstrb_cpt_unit_services_' . (int) $post_id, 'vstrb_cpt');

		// Save Amenities
		$wpdb->delete($t['unit_amenities'], ['unit_id' => $post_id]);
		$posted_amenities = self::post_array('vstrb_amenities');
		if (!empty($posted_amenities)) {
			foreach ($posted_amenities as $amenity_id) {
				$amenity_id = (int) $amenity_id;
				if ($amenity_id <= 0) {
					continue;
				}
				$wpdb->insert($t['unit_amenities'], [
					'unit_id' => $post_id,
					'amenity_id' => $amenity_id,
				]);
			}
		}
		$new_amenities = self::post_array('vstrb_new_amenities');
		$amenity_categories = self::amenity_category_labels();
		foreach ($new_amenities as $new_amenity) {
			if (!is_array($new_amenity)) continue;
			$name = sanitize_text_field($new_amenity['name'] ?? '');
			if ($name === '') continue;
			$category = sanitize_key($new_amenity['category'] ?? 'general');
			if (!isset($amenity_categories[$category])) $category = 'general';
			$base_code = strtoupper(str_replace('-', '_', sanitize_title($name)));
			$base_code = substr($base_code !== '' ? $base_code : 'AMENITY', 0, 54);
			$code = $base_code;
			$suffix = 2;
			while ($wpdb->get_var($wpdb->prepare("SELECT id FROM {$t['amenities']} WHERE code=%s", $code))) {
				$code = substr($base_code, 0, 54) . '_' . $suffix++;
			}
			$wpdb->insert($t['amenities'], ['name' => $name, 'code' => $code, 'category' => $category, 'is_active' => 1]);
			$amenity_id = (int) $wpdb->insert_id;
			if ($amenity_id > 0) $wpdb->insert($t['unit_amenities'], ['unit_id' => $post_id, 'amenity_id' => $amenity_id]);
		}

		// Save Contact Fields
		$wpdb->delete($t['unit_contact_fields'], ['unit_id' => $post_id]);
		$posted_contact_fields = self::post_array('vstrb_contact_fields');
		if (!empty($posted_contact_fields)) {
			foreach ($posted_contact_fields as $fid) {
				$wpdb->insert($t['unit_contact_fields'], [
					'unit_id' => $post_id,
					'field_id' => (int)$fid
				]);
			}
		}
		$new_contact_fields = self::post_array('vstrb_new_contact_fields');
		foreach ($new_contact_fields as $new_field) {
			if (!is_array($new_field)) continue;
			$label = sanitize_text_field($new_field['name'] ?? '');
			if ($label === '') continue;
			$base_key = sanitize_key($label);
			$base_key = substr($base_key !== '' ? $base_key : 'custom_field', 0, 54);
			$field_key = $base_key;
			$suffix = 2;
			while ($wpdb->get_var($wpdb->prepare("SELECT id FROM {$t['contact_fields']} WHERE field_key=%s", $field_key))) {
				$field_key = substr($base_key, 0, 54) . '_' . $suffix++;
			}
			$wpdb->insert($t['contact_fields'], ['field_key' => $field_key, 'is_active' => 1]);
			$field_id = (int) $wpdb->insert_id;
			if ($field_id > 0) {
				// These are column names in a plugin-owned metadata table, not a metadata lookup query.
				// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value
				$wpdb->insert($t['contact_field_meta'], ['field_id' => $field_id, 'meta_key' => 'label', 'meta_value' => $label, 'locale' => null]);
				$wpdb->insert($t['unit_contact_fields'], ['unit_id' => $post_id, 'field_id' => $field_id]);
			}
		}
		wp_cache_delete('vstrb_cpt_contact_fields_all', 'vstrb_cpt');
		wp_cache_delete('vstrb_cpt_unit_contact_fields_' . (int) $post_id, 'vstrb_cpt');
	}
	// phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
}

add_action('init', ['VSTRB_CPT', 'register']);
