<?php
/**
 * Form shortcode: [govalid_form id="X"]
 *
 * @package GoValid_QR
 */

defined( 'ABSPATH' ) || exit;

class GoValid_Form_Shortcode {

	/**
	 * Register hooks.
	 */
	public function register(): void {
		add_shortcode( 'govalid_form', array( $this, 'render' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'maybe_enqueue_assets' ) );
	}

	/**
	 * Enqueue public form assets only when shortcode is present.
	 */
	public function maybe_enqueue_assets(): void {
		global $post;

		if ( ! is_a( $post, 'WP_Post' ) ) {
			return;
		}

		if ( has_shortcode( $post->post_content, 'govalid_form' ) || has_block( 'govalid-qr/form', $post ) ) {
			$this->enqueue_assets();
		}
	}

	/**
	 * Register & enqueue the public form CSS/JS.
	 */
	public function enqueue_assets(): void {
		wp_enqueue_style(
			'govalid-form',
			GOVALID_QR_PLUGIN_URL . 'public/css/govalid-form.css',
			array(),
			GOVALID_QR_VERSION
		);

		wp_enqueue_script(
			'govalid-form',
			GOVALID_QR_PLUGIN_URL . 'public/js/govalid-form.js',
			array(),
			GOVALID_QR_VERSION,
			true
		);

		wp_localize_script( 'govalid-form', 'govalidForm', array(
			'restUrl' => rest_url( 'govalid-qr/v1/' ),
			'nonce'   => wp_create_nonce( 'wp_rest' ),
		) );
	}

	/**
	 * Render the form shortcode.
	 *
	 * @param array $atts Shortcode attributes.
	 * @return string HTML output.
	 */
	public function render( $atts ): string {
		$atts = shortcode_atts( array( 'id' => 0 ), $atts, 'govalid_form' );
		$form_id = absint( $atts['id'] );

		if ( ! $form_id ) {
			return current_user_can( 'edit_posts' )
				? '<p class="govalid-pf-error">' . esc_html__( 'Please specify a form ID.', 'govalid-qr-validator' ) . '</p>'
				: '';
		}

		$form = GoValid_Form_Model::get_form( $form_id );
		if ( ! $form || 'published' !== $form->status ) {
			return current_user_can( 'edit_posts' )
				? '<p class="govalid-pf-error">' . esc_html__( 'Form not found or not published.', 'govalid-qr-validator' ) . '</p>'
				: '';
		}

		$fields   = GoValid_Form_Model::get_fields( $form_id );
		$settings = json_decode( $form->settings ?? '{}', true ) ?: array();

		$submit_text     = $settings['submit_text'] ?? __( 'Submit', 'govalid-qr-validator' );
		$success_message = $settings['success_message'] ?? __( 'Thank you! Your submission has been received.', 'govalid-qr-validator' );

		// Enqueue assets (in case shortcode is dynamically loaded).
		$this->enqueue_assets();

		// Detect if form has page breaks (multi-step).
		$has_page_breaks = false;
		foreach ( $fields as $f ) {
			if ( 'page_break' === $f->type ) {
				$has_page_breaks = true;
				break;
			}
		}

		// Split fields into steps if multi-step.
		$steps       = array();
		$step_labels = array();
		if ( $has_page_breaks ) {
			$current_step = array();
			$step_labels[] = '';
			foreach ( $fields as $field ) {
				if ( 'page_break' === $field->type ) {
					$steps[] = $current_step;
					$current_step = array();
					$pb_validation = json_decode( $field->validation ?? 'null', true );
					$step_labels[] = $pb_validation['step_label'] ?? '';
				} else {
					$current_step[] = $field;
				}
			}
			$steps[] = $current_step;
		}
		$total_steps = count( $steps );

		ob_start();
		?>
		<div class="govalid-pf-wrap" data-form-id="<?php echo esc_attr( $form_id ); ?>"
			 data-success-msg="<?php echo esc_attr( $success_message ); ?>">
			<form class="govalid-pf-form" novalidate>
				<?php if ( $has_page_breaks ) : ?>
					<!-- Progress indicator -->
					<div class="govalid-pf-progress" data-total="<?php echo esc_attr( $total_steps ); ?>">
						<?php for ( $s = 0; $s < $total_steps; $s++ ) :
							/* translators: %d: step number */
							$slabel = $step_labels[ $s ] ?: sprintf( __( 'Step %d', 'govalid-qr-validator' ), $s + 1 );
						?>
							<div class="govalid-pf-progress-step<?php echo esc_attr( 0 === $s ? ' govalid-pf-progress-step--active' : '' ); ?>" data-step="<?php echo esc_attr( $s ); ?>">
								<span class="govalid-pf-progress-num"><?php echo (int) ( $s + 1 ); ?></span>
								<span class="govalid-pf-progress-label"><?php echo esc_html( $slabel ); ?></span>
							</div>
							<?php if ( $s < $total_steps - 1 ) : ?>
								<div class="govalid-pf-progress-connector"></div>
							<?php endif; ?>
						<?php endfor; ?>
					</div>

					<!-- Step containers -->
					<?php foreach ( $steps as $step_index => $step_fields ) : ?>
						<div class="govalid-pf-step" data-step="<?php echo esc_attr( $step_index ); ?>"<?php echo esc_attr( $step_index > 0 ? ' style="display:none;"' : '' ); ?>>
							<?php foreach ( $step_fields as $field ) : ?>
								<?php echo wp_kses( $this->render_field( $field ), $this->get_kses_allowed_form_html() ); ?>
							<?php endforeach; ?>
						</div>
					<?php endforeach; ?>

					<!-- Step navigation -->
					<div class="govalid-pf-step-nav">
						<button type="button" class="govalid-pf-step-prev" style="display:none;">
							<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m15 18-6-6 6-6"/></svg>
							<span><?php esc_html_e( 'Previous', 'govalid-qr-validator' ); ?></span>
						</button>
						<button type="button" class="govalid-pf-step-next">
							<span><?php esc_html_e( 'Next', 'govalid-qr-validator' ); ?></span>
							<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m9 18 6-6-6-6"/></svg>
						</button>
					</div>
				<?php else : ?>
					<?php foreach ( $fields as $field ) : ?>
						<?php echo wp_kses( $this->render_field( $field ), $this->get_kses_allowed_form_html() ); ?>
					<?php endforeach; ?>
				<?php endif; ?>

				<!-- Honeypot -->
				<div style="position:absolute; left:-9999px; opacity:0;" aria-hidden="true">
					<input type="text" name="_govalid_hp" tabindex="-1" autocomplete="off" />
				</div>

				<div class="govalid-pf-submit-wrap"<?php echo $has_page_breaks ? ' style="display:none;"' : ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static string ?>>
					<button type="submit" class="govalid-pf-submit">
						<span><?php echo esc_html( $submit_text ); ?></span>
						<svg class="govalid-pf-submit-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
					</button>
				</div>

				<div class="govalid-pf-message" style="display:none;"></div>
			</form>
		</div>
		<?php
		return ob_get_clean();
	}

	/**
	 * Detect social-media / platform name in a field label.
	 *
	 * Returns array with 'type' (data-icon-type value) and 'svg' (icon markup),
	 * or null if no platform is detected.
	 */
	private function detect_platform_icon( string $label ): ?array {
		$lower = strtolower( trim( $label ) );

		$platforms = array(
			'instagram' => array(
				'type' => 'instagram',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="20" x="2" y="2" rx="5"/><circle cx="12" cy="12" r="5"/><circle cx="17.5" cy="6.5" r="1.5" fill="currentColor" stroke="none"/></svg>',
			),
			'tiktok' => array(
				'type' => 'tiktok',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 12a4 4 0 1 0 4 4V4a5 5 0 0 0 5 5"/></svg>',
			),
			'facebook' => array(
				'type' => 'facebook',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg>',
			),
			'youtube' => array(
				'type' => 'youtube',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 17a24.12 24.12 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.56 49.56 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24.12 24.12 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.55 49.55 0 0 1-16.2 0A2 2 0 0 1 2.5 17"/><path d="m10 15 5-3-5-3z"/></svg>',
			),
			'twitter' => array(
				'type' => 'twitter',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4l6.6 8.5L4 20h2l5.6-6.3L16 20h4l-7-9L19.4 4H17.4l-5 5.6L8 4H4z"/></svg>',
			),
			'linkedin' => array(
				'type' => 'linkedin',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect width="4" height="12" x="2" y="9"/><circle cx="4" cy="4" r="2"/></svg>',
			),
			'whatsapp' => array(
				'type' => 'whatsapp',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21l1.65-3.8a9 9 0 1 1 3.4 2.9L3 21"/><path d="M9 10a.5.5 0 0 0 1 0V9a.5.5 0 0 0-1 0v1a5 5 0 0 0 5 5h1a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1"/></svg>',
			),
			'telegram' => array(
				'type' => 'telegram',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21.2 4.4L2.4 10.8c-.6.2-.6 1.1.1 1.3l4.6 1.5 1.7 5.5c.1.4.6.6 1 .3l2.4-2 4.3 3.2c.5.3 1.1 0 1.2-.5L21.9 5.4c.2-.7-.4-1.2-1-.9z"/><path d="M8 13.5l9-6.5"/></svg>',
			),
			'snapchat' => array(
				'type' => 'snapchat',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2C9 2 7 4.5 7 7v2.5c0 .3-.2.5-.5.5H5c-.6 0-1 .4-1 1s.4 1 1 1h1c.6 0 1 .3 1 .8-.2 1.2-1.8 2.5-4 3.2-.3.1-.5.3-.5.6 0 .4.3.8.8.9 1.5.2 2.4.5 2.8 1 .2.2.2.5.1.7-.1.3.1.6.4.7.3.1.7 0 1-.2.7-.5 1.5-.8 2.4-.8.6 0 1.2.2 2 .7.9.5 1.7.9 2.5.9s1.6-.4 2.5-.9c.8-.5 1.4-.7 2-.7.9 0 1.7.3 2.4.8.3.2.7.3 1 .2.3-.1.5-.4.4-.7-.1-.2-.1-.5.1-.7.4-.5 1.3-.8 2.8-1 .5-.1.8-.5.8-.9 0-.3-.2-.5-.5-.6-2.2-.7-3.8-2-4-3.2-.1-.5.4-.8 1-.8h1c.6 0 1-.4 1-1s-.4-1-1-1h-1.5c-.3 0-.5-.2-.5-.5V7c0-2.5-2-5-5-5z"/></svg>',
			),
			'pinterest' => array(
				'type' => 'pinterest',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.5 14.5c-.3 1.5-.5 3-.5 3l-1 3"/><path d="M9 10c0-2.2 1.8-3 3-3s3 .8 3 3c0 2-1 3.5-2 5-1 1.5-1.5 2.5-1 4"/></svg>',
			),
			'spotify' => array(
				'type' => 'spotify',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M8 11.8c2.5-.7 5.2-.5 7.5.6"/><path d="M7 15c2-.5 4.2-.4 6 .5"/><path d="M9 8.5c2.8-.8 6-.6 8.5.8"/></svg>',
			),
			'discord' => array(
				'type' => 'discord',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9.1 4.8a17 17 0 0 0-4.2 1.3 17.4 17.4 0 0 0-3 12.7A17 17 0 0 0 7.2 22a13 13 0 0 0 1.1-1.8 11.1 11.1 0 0 1-1.7-.8l.4-.3a12.2 12.2 0 0 0 10 0l.4.3c-.6.3-1.1.6-1.7.8.3.7.7 1.3 1.1 1.8a17 17 0 0 0 5.3-3.2 17.4 17.4 0 0 0-3-12.7 17 17 0 0 0-4.2-1.3l-.5 1a13 13 0 0 0-3.8 0l-.5-1z"/><circle cx="9.5" cy="13.5" r="1.5"/><circle cx="14.5" cy="13.5" r="1.5"/></svg>',
			),
			'twitch' => array(
				'type' => 'twitch',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2H3v16h5v4l4-4h5l4-4V2zM11 11V7M16 11V7"/></svg>',
			),
			'github' => array(
				'type' => 'github',
				'svg'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65S8.93 17.38 9 18v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg>',
			),
			'website' => array(
				'type' => 'url',
				'svg'  => null, // Will use the existing url icon.
			),
		);

		foreach ( $platforms as $keyword => $data ) {
			if ( false !== strpos( $lower, $keyword ) ) {
				if ( null === $data['svg'] ) {
					return array( 'type' => $data['type'], 'svg' => $this->get_field_icon( $data['type'] ) );
				}
				return $data;
			}
		}

		return null;
	}

	/**
	 * SVG icon map for field types — each type has its own unique icon.
	 */
	private function get_field_icon( string $type ): string {
		$icons = array(
			// Pencil — writing/text.
			'text'     => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.85 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>',
			// Envelope — email.
			'email'    => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>',
			// Phone.
			'phone'    => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.362 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.338 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/></svg>',
			// Calculator/hash — number.
			'number'   => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="M15 3v18"/><path d="M3 9h18"/><path d="M3 15h18"/></svg>',
			// Calendar — date.
			'date'     => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M16 2v4"/><path d="M8 2v4"/><path d="M3 10h18"/><path d="M8 14h.01"/><path d="M12 14h.01"/><path d="M16 14h.01"/><path d="M8 18h.01"/><path d="M12 18h.01"/></svg>',
			// Clock — time.
			'time'     => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>',
			// Message/lines — textarea.
			'textarea' => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/><path d="M8 9h8"/><path d="M8 13h6"/></svg>',
			// List — select/dropdown.
			'select'   => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="m9 12 2 2 4-4"/><path d="m9 7 2 2 4-4"/><path d="m9 17 2 2 4-4"/></svg>',
			// User — person/name.
			'name'     => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
			// Globe — URL/website.
			'url'      => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>',
			// Map pin — address.
			'address'  => '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></svg>',
		);

		return $icons[ $type ] ?? $icons['text'];
	}

	/**
	 * Gender option icon SVGs.
	 */
	private function get_gender_icon( string $value ): string {
		$lower = strtolower( trim( $value ) );
		if ( in_array( $lower, array( 'male', 'laki-laki', 'pria', 'homme', 'masculino', 'hombre' ), true ) ) {
			// Male icon — Mars symbol.
			return '<svg class="govalid-pf-gender-icon govalid-pf-gender-icon--male" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="10" cy="14" r="5"/><path d="M19 5l-5.4 5.4"/><path d="M15 5h4v4"/></svg>';
		}
		if ( in_array( $lower, array( 'female', 'perempuan', 'wanita', 'femme', 'femenino', 'mujer' ), true ) ) {
			// Female icon — Venus symbol.
			return '<svg class="govalid-pf-gender-icon govalid-pf-gender-icon--female" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="9" r="5"/><path d="M12 14v7"/><path d="M9 18h6"/></svg>';
		}
		// Other / non-binary — generic person icon.
		return '<svg class="govalid-pf-gender-icon govalid-pf-gender-icon--other" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M20 21a8 8 0 0 0-16 0"/></svg>';
	}

	/**
	 * Render a single field HTML.
	 */
	private function render_field( object $field ): string {
		$type        = $field->type;
		$label       = $field->label;
		$placeholder = $field->placeholder ?? '';
		$required    = (int) $field->required;
		$options     = json_decode( $field->options ?? 'null', true );
		$cond        = json_decode( $field->conditional_logic ?? 'null', true );
		$field_id    = 'govalid-pf-field-' . $field->id;

		$cond_attr = '';
		if ( $cond && ! empty( $cond['enabled'] ) ) {
			$cond_attr = ' data-cond="' . esc_attr( wp_json_encode( $cond ) ) . '"';
		}

		$required_attr = $required ? ' required' : '';
		$required_star = $required ? ' <span class="govalid-pf-required">*</span>' : '';

		$html = '<div class="govalid-pf-field govalid-pf-field--' . esc_attr( $type ) . '"'
			. ' data-field-id="' . esc_attr( $field->id ) . '"' . $cond_attr . '>';

		switch ( $type ) {
			case 'heading':
				$html .= '<h3 class="govalid-pf-heading">' . esc_html( $label ) . '</h3>';
				break;

			case 'separator':
				$validation = json_decode( $field->validation ?? 'null', true );
				$line_style = $validation['line_style'] ?? 'solid';
				$sep_height = (int) ( $validation['height'] ?? 16 );
				$style_parts = array();
				if ( 'none' === $line_style ) {
					$style_parts[] = 'border: none';
				} else {
					$style_parts[] = 'border: none';
					$style_parts[] = 'border-top: 1px ' . esc_attr( $line_style ) . ' var(--pf-gray-200)';
				}
				$style_parts[] = 'margin: ' . $sep_height . 'px 0';
				$html .= '<hr class="govalid-pf-separator" style="' . esc_attr( implode( '; ', $style_parts ) ) . '" />';
				break;

			case 'image_banner':
				$validation = json_decode( $field->validation ?? 'null', true );
				$banner_url = $validation['image_url'] ?? '';
				if ( $banner_url ) {
					$html .= '<img src="' . esc_url( $banner_url ) . '" alt="' . esc_attr( $label ) . '" class="govalid-pf-banner" />';
				}
				break;

			case 'gender':
				$html .= '<fieldset class="govalid-pf-fieldset">';
				$html .= '<legend class="govalid-pf-label">' . esc_html( $label ) . $required_star . '</legend>';
				$html .= '<div class="govalid-pf-options-grid govalid-pf-options-grid--horizontal">';
				if ( $options ) {
					foreach ( $options as $i => $opt ) {
						$opt_id = $field_id . '-' . $i;
						$html .= '<label class="govalid-pf-option-card govalid-pf-option-card--gender govalid-pf-radio-label" for="' . esc_attr( $opt_id ) . '">';
						$html .= '<input type="radio" name="field_' . esc_attr( $field->id ) . '" id="' . esc_attr( $opt_id ) . '" value="' . esc_attr( $opt ) . '"' . $required_attr . ' />';
						$html .= '<span class="govalid-pf-option-radio"></span>';
						$html .= $this->get_gender_icon( $opt );
						$html .= '<span class="govalid-pf-option-text">' . esc_html( $opt ) . '</span>';
						$html .= '</label>';
					}
				}
				$html .= '</div>';
				$html .= '</fieldset>';
				break;

			case 'textarea':
				$html .= '<label class="govalid-pf-label" for="' . esc_attr( $field_id ) . '">' . esc_html( $label ) . $required_star . '</label>';
				$html .= '<div class="govalid-pf-input-wrap govalid-pf-input-wrap--textarea">';
				$html .= '<span class="govalid-pf-input-icon" data-icon-type="textarea">' . $this->get_field_icon( 'textarea' ) . '</span>';
				$html .= '<textarea id="' . esc_attr( $field_id ) . '" class="govalid-pf-input govalid-pf-input--has-icon" placeholder="' . esc_attr( $placeholder ) . '" rows="4"' . $required_attr . '></textarea>';
				$html .= '</div>';
				break;

			case 'select':
				$html .= '<label class="govalid-pf-label" for="' . esc_attr( $field_id ) . '">' . esc_html( $label ) . $required_star . '</label>';
				$html .= '<div class="govalid-pf-input-wrap govalid-pf-input-wrap--select">';
				$html .= '<span class="govalid-pf-input-icon" data-icon-type="select">' . $this->get_field_icon( 'select' ) . '</span>';
				$html .= '<select id="' . esc_attr( $field_id ) . '" class="govalid-pf-input govalid-pf-input--has-icon govalid-pf-input--select"' . $required_attr . '>';
				$html .= '<option value="">' . esc_html( $placeholder ?: __( 'Select...', 'govalid-qr-validator' ) ) . '</option>';
				if ( $options ) {
					foreach ( $options as $opt ) {
						$html .= '<option value="' . esc_attr( $opt ) . '">' . esc_html( $opt ) . '</option>';
					}
				}
				$html .= '</select>';
				$html .= '<span class="govalid-pf-select-arrow"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg></span>';
				$html .= '</div>';
				break;

			case 'radio':
				$html .= '<fieldset class="govalid-pf-fieldset">';
				$html .= '<legend class="govalid-pf-label">' . esc_html( $label ) . $required_star . '</legend>';
				$html .= '<div class="govalid-pf-options-grid">';
				if ( $options ) {
					foreach ( $options as $i => $opt ) {
						$opt_id = $field_id . '-' . $i;
						$html .= '<label class="govalid-pf-option-card govalid-pf-radio-label" for="' . esc_attr( $opt_id ) . '">';
						$html .= '<input type="radio" name="field_' . esc_attr( $field->id ) . '" id="' . esc_attr( $opt_id ) . '" value="' . esc_attr( $opt ) . '"' . $required_attr . ' />';
						$html .= '<span class="govalid-pf-option-radio"></span>';
						$html .= '<span class="govalid-pf-option-text">' . esc_html( $opt ) . '</span>';
						$html .= '</label>';
					}
				}
				$html .= '</div>';
				$html .= '</fieldset>';
				break;

			case 'checkbox':
				$html .= '<fieldset class="govalid-pf-fieldset">';
				$html .= '<legend class="govalid-pf-label">' . esc_html( $label ) . $required_star . '</legend>';
				$html .= '<div class="govalid-pf-options-grid">';
				if ( $options ) {
					foreach ( $options as $i => $opt ) {
						$opt_id = $field_id . '-' . $i;
						$html .= '<label class="govalid-pf-option-card govalid-pf-checkbox-label" for="' . esc_attr( $opt_id ) . '">';
						$html .= '<input type="checkbox" name="field_' . esc_attr( $field->id ) . '[]" id="' . esc_attr( $opt_id ) . '" value="' . esc_attr( $opt ) . '" />';
						$html .= '<span class="govalid-pf-option-check"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></span>';
						$html .= '<span class="govalid-pf-option-text">' . esc_html( $opt ) . '</span>';
						$html .= '</label>';
					}
				}
				$html .= '</div>';
				$html .= '</fieldset>';
				break;

			case 'file':
				$html .= '<label class="govalid-pf-label">' . esc_html( $label ) . $required_star . '</label>';
				$html .= '<div class="govalid-pf-dropzone" id="' . esc_attr( $field_id ) . '-zone">';
				$html .= '<div class="govalid-pf-dropzone-inner">';
				$html .= '<svg class="govalid-pf-dropzone-icon" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/></svg>';
				$html .= '<p class="govalid-pf-dropzone-text">' . esc_html__( 'Drag & drop your file here', 'govalid-qr-validator' ) . '</p>';
				$html .= '<p class="govalid-pf-dropzone-hint">' . esc_html__( 'or', 'govalid-qr-validator' ) . ' <span class="govalid-pf-dropzone-browse">' . esc_html__( 'browse files', 'govalid-qr-validator' ) . '</span></p>';
				$html .= '<p class="govalid-pf-dropzone-formats">' . esc_html__( 'Images & PDF supported', 'govalid-qr-validator' ) . '</p>';
				$html .= '</div>';
				$html .= '<div class="govalid-pf-dropzone-preview" style="display:none;">';
				$html .= '<div class="govalid-pf-dropzone-file-info">';
				$html .= '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/></svg>';
				$html .= '<span class="govalid-pf-dropzone-filename"></span>';
				$html .= '<button type="button" class="govalid-pf-dropzone-remove" title="' . esc_attr__( 'Remove', 'govalid-qr-validator' ) . '">';
				$html .= '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
				$html .= '</button>';
				$html .= '</div>';
				$html .= '</div>';
				$html .= '<input type="file" id="' . esc_attr( $field_id ) . '" class="govalid-pf-input govalid-pf-file-hidden" accept="image/*,.pdf"' . $required_attr . ' />';
				$html .= '</div>';
				break;

			case 'rating':
				$validation = json_decode( $field->validation ?? 'null', true );
				$max_stars  = (int) ( $validation['max_stars'] ?? 5 );
				$max_stars  = max( 3, min( 10, $max_stars ) );

				$html .= '<label class="govalid-pf-label">' . esc_html( $label ) . $required_star . '</label>';
				$html .= '<div class="govalid-pf-rating" data-max="' . esc_attr( $max_stars ) . '">';
				for ( $i = 1; $i <= $max_stars; $i++ ) {
					$html .= '<span class="govalid-pf-star" data-value="' . $i . '">';
					$html .= '<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>';
					$html .= '</span>';
				}
				$html .= '<input type="hidden" class="govalid-pf-input govalid-pf-rating-value" value=""' . $required_attr . ' />';
				$html .= '</div>';
				break;

			case 'signature':
				$validation    = json_decode( $field->validation ?? 'null', true );
				$canvas_height = (int) ( $validation['canvas_height'] ?? 200 );
				$canvas_height = max( 100, min( 400, $canvas_height ) );

				$html .= '<label class="govalid-pf-label">' . esc_html( $label ) . $required_star . '</label>';
				$html .= '<div class="govalid-pf-signature" id="' . esc_attr( $field_id ) . '">';
				$html .= '<canvas class="govalid-pf-signature-canvas" width="600" height="' . esc_attr( $canvas_height ) . '"></canvas>';
				$html .= '<div class="govalid-pf-signature-actions">';
				$html .= '<button type="button" class="govalid-pf-signature-clear">';
				$html .= '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
				$html .= ' <span>' . esc_html__( 'Clear', 'govalid-qr-validator' ) . '</span>';
				$html .= '</button>';
				$html .= '</div>';
				$html .= '<input type="file" class="govalid-pf-signature-file" accept="image/png" style="display:none;"' . $required_attr . ' />';
				$html .= '</div>';
				break;

			case 'camera':
				$html .= '<label class="govalid-pf-label">' . esc_html( $label ) . $required_star . '</label>';
				$html .= '<div class="govalid-pf-camera" id="' . esc_attr( $field_id ) . '">';
				// Preview (hidden until photo taken).
				$html .= '<div class="govalid-pf-camera-preview" style="display:none;">';
				$html .= '<img class="govalid-pf-camera-img" alt="" />';
				$html .= '<button type="button" class="govalid-pf-camera-retake" title="' . esc_attr__( 'Retake', 'govalid-qr-validator' ) . '">';
				$html .= '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>';
				$html .= '</button>';
				$html .= '</div>';
				// Video element for live feed (hidden until started).
				$html .= '<video class="govalid-pf-camera-video" playsinline autoplay style="display:none;"></video>';
				$html .= '<canvas class="govalid-pf-camera-canvas" style="display:none;"></canvas>';
				// Buttons.
				$html .= '<div class="govalid-pf-camera-actions">';
				$html .= '<button type="button" class="govalid-pf-camera-open">';
				$html .= '<svg class="govalid-pf-camera-btn-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/></svg>';
				$html .= '<span>' . esc_html( $placeholder ?: __( 'Take Photo', 'govalid-qr-validator' ) ) . '</span>';
				$html .= '</button>';
				$html .= '<button type="button" class="govalid-pf-camera-snap" style="display:none;">';
				$html .= '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>';
				$html .= ' <span>' . esc_html__( 'Capture', 'govalid-qr-validator' ) . '</span>';
				$html .= '</button>';
				$html .= '</div>';
				// Hidden file input to hold the captured image.
				$html .= '<input type="file" class="govalid-pf-camera-file" accept="image/*" style="display:none;"' . $required_attr . ' />';
				$html .= '</div>';
				break;

			default:
				// text, email, phone, number, date, time — plus social-media label detection.
				$input_type = $type;
				if ( 'phone' === $type ) {
					$input_type = 'tel';
				}

				// Check if the label matches a social-media platform for a custom icon.
				$platform  = $this->detect_platform_icon( $label );
				$icon_type = $platform ? $platform['type'] : $type;
				$icon_svg  = $platform ? $platform['svg'] : $this->get_field_icon( $type );

				$html .= '<label class="govalid-pf-label" for="' . esc_attr( $field_id ) . '">' . esc_html( $label ) . $required_star . '</label>';
				$html .= '<div class="govalid-pf-input-wrap">';
				$html .= '<span class="govalid-pf-input-icon" data-icon-type="' . esc_attr( $icon_type ) . '">' . $icon_svg . '</span>';
				$html .= '<input type="' . esc_attr( $input_type ) . '" id="' . esc_attr( $field_id ) . '" class="govalid-pf-input govalid-pf-input--has-icon" placeholder="' . esc_attr( $placeholder ) . '"' . $required_attr . ' />';
				$html .= '</div>';
				break;
		}

		$html .= '<div class="govalid-pf-field-error" style="display:none;"></div>';
		$html .= '</div>';

		return $html;
	}

	/**
	 * Allowed HTML tags and attributes for wp_kses() on render_field() output.
	 *
	 * wp_kses_post() cannot be used because it strips form elements
	 * (<input>, <select>, <textarea>) and SVG tags needed by the form builder.
	 *
	 * @return array
	 */
	private function get_kses_allowed_form_html(): array {
		$svg_base = array(
			'viewbox'         => true,
			'fill'            => true,
			'stroke'          => true,
			'stroke-width'    => true,
			'stroke-linecap'  => true,
			'stroke-linejoin' => true,
			'width'           => true,
			'height'          => true,
			'class'           => true,
			'style'           => true,
		);
		$svg_shape = array(
			'd'      => true,
			'points' => true,
			'cx'     => true,
			'cy'     => true,
			'r'      => true,
			'x1'     => true,
			'x2'     => true,
			'y1'     => true,
			'y2'     => true,
			'x'      => true,
			'y'      => true,
		);
		return array(
			'div'      => array( 'class' => true, 'id' => true, 'style' => true, 'data-field-id' => true, 'data-cond' => true, 'data-max' => true, 'data-step' => true ),
			'h3'       => array( 'class' => true ),
			'hr'       => array( 'class' => true, 'style' => true ),
			'img'      => array( 'src' => true, 'alt' => true, 'class' => true ),
			'label'    => array( 'class' => true, 'for' => true ),
			'input'    => array( 'type' => true, 'name' => true, 'id' => true, 'class' => true, 'value' => true, 'required' => true, 'placeholder' => true, 'accept' => true, 'tabindex' => true, 'autocomplete' => true, 'style' => true ),
			'textarea' => array( 'id' => true, 'name' => true, 'class' => true, 'placeholder' => true, 'rows' => true, 'required' => true ),
			'select'   => array( 'id' => true, 'name' => true, 'class' => true, 'required' => true ),
			'option'   => array( 'value' => true ),
			'fieldset' => array( 'class' => true ),
			'legend'   => array( 'class' => true ),
			'span'     => array( 'class' => true, 'data-icon-type' => true, 'data-value' => true, 'style' => true, 'title' => true ),
			'button'   => array( 'type' => true, 'class' => true, 'data-id' => true, 'title' => true, 'style' => true ),
			'p'        => array( 'class' => true, 'style' => true ),
			'canvas'   => array( 'class' => true, 'width' => true, 'height' => true, 'style' => true ),
			'video'    => array( 'class' => true, 'playsinline' => true, 'autoplay' => true, 'style' => true ),
			'svg'      => $svg_base,
			'path'     => array_merge( $svg_base, $svg_shape ),
			'polyline' => array_merge( $svg_base, $svg_shape ),
			'polygon'  => array_merge( $svg_base, $svg_shape ),
			'circle'   => array_merge( $svg_base, $svg_shape ),
			'line'     => array_merge( $svg_base, $svg_shape ),
		);
	}
}
