<?php
defined( 'ABSPATH' ) || exit;

class Formulate_Pricing_Renderer {

	/** @var bool */
	private static $form_on_page = false;

	public static function render( $form_id, $is_preview = false ) {
		$form = Formulate_Pricing_Form::get( $form_id );
		if ( ! $form ) {
			return '<p>' . esc_html__( 'Form not found.', 'formulate-pricing' ) . '</p>';
		}

		$config     = $form->form_config;
		$settings   = isset( $config['settings'] ) ? $config['settings'] : array();
		$styling    = isset( $config['styling'] ) ? $config['styling'] : array();
		$steps      = isset( $config['steps'] ) ? $config['steps'] : array();
		$multi_step = ! empty( $settings['multi_step'] ) && count( $steps ) > 1;

		self::enqueue_assets( $is_preview );

		$css_vars = self::generate_css_vars( $form_id, $styling );
		wp_add_inline_style( 'formulate-pricing-form', $css_vars );

		ob_start();
		?>
		<div class="bf-form-wrap bf-form-isolated" id="bf-form-<?php echo esc_attr( $form_id ); ?>"
			 data-form-id="<?php echo esc_attr( $form_id ); ?>"
			 data-multi-step="<?php echo $multi_step ? '1' : '0'; ?>"
			 data-steps="<?php echo esc_attr( count( $steps ) ); ?>"
			 <?php echo $is_preview ? 'data-preview="1"' : ''; ?>>

			<?php if ( $multi_step && ! empty( $settings['show_progress'] ) ) : ?>
			<div class="bf-progress-wrap">
				<div class="bf-step-indicators">
					<?php foreach ( $steps as $si => $step ) : ?>
					<div class="bf-step-indicator <?php echo 0 === $si ? 'active' : ''; ?>" data-step="<?php echo esc_attr( $si ); ?>">
						<span class="bf-step-number"><?php echo esc_html( $si + 1 ); ?></span>
						<span class="bf-step-label"><?php echo esc_html( isset( $step['title'] ) ? $step['title'] : '' ); ?></span>
					</div>
					<?php if ( $si < count( $steps ) - 1 ) : ?>
					<div class="bf-step-connector"></div>
					<?php endif; ?>
					<?php endforeach; ?>
				</div>
				<div class="bf-progress-bar">
					<div class="bf-progress-fill" style="width: <?php echo esc_attr( round( 100 / count( $steps ) ) ); ?>%"></div>
				</div>
			</div>
			<?php endif; ?>

			<form class="bf-dynamic-form" method="post" novalidate
				  action=""
				  data-form-id="<?php echo esc_attr( $form_id ); ?>">
				<?php wp_nonce_field( 'formulate_pricing_submit_' . $form_id, 'formulate_pricing_nonce' ); ?>
				<input type="hidden" name="formulate_pricing_form_id" value="<?php echo esc_attr( $form_id ); ?>">

				<?php foreach ( $steps as $si => $step ) : ?>
				<div class="bf-step" data-step-index="<?php echo esc_attr( $si ); ?>"
					 style="<?php echo $si > 0 ? 'display:none;' : ''; ?>">

					<?php if ( $multi_step && ! empty( $step['title'] ) ) : ?>
					<h3 class="bf-step-heading"><?php echo esc_html( $step['title'] ); ?></h3>
					<?php endif; ?>

					<div class="bf-fields-row">
						<?php
						if ( ! empty( $step['fields'] ) ) {
							$fields_html = '';
							foreach ( $step['fields'] as $field ) {
								$fields_html .= self::render_field( $field );
							}
							echo wp_kses( $fields_html, self::get_form_markup_allowed_html() );
						}
						?>
					</div>

					<div class="bf-step-nav">
						<?php if ( $multi_step && $si > 0 ) : ?>
						<button type="button" class="bf-btn bf-btn-secondary bf-btn-prev">
							<?php echo esc_html( isset( $settings['prev_text'] ) ? $settings['prev_text'] : 'Previous' ); ?>
						</button>
						<?php endif; ?>

						<?php if ( $multi_step && $si < count( $steps ) - 1 ) : ?>
						<button type="button" class="bf-btn bf-btn-primary bf-btn-next">
							<?php echo esc_html( isset( $settings['next_text'] ) ? $settings['next_text'] : 'Next' ); ?>
						</button>
						<?php else : ?>
						<button type="submit" class="bf-btn bf-btn-primary bf-btn-submit"
								<?php echo $is_preview ? 'disabled' : ''; ?>>
							<?php echo esc_html( isset( $settings['submit_text'] ) ? $settings['submit_text'] : 'Submit' ); ?>
						</button>
						<?php endif; ?>
					</div>
				</div>
				<?php endforeach; ?>
			</form>

			<div class="bf-success-wrap" style="display:none;">
				<div class="bf-success-icon">
					<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
						<circle cx="24" cy="24" r="24" fill="currentColor" opacity="0.1"/>
						<path d="M14 24l7 7 13-13" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
					</svg>
				</div>
				<p class="bf-success-text"><?php echo wp_kses_post( isset( $settings['success_message'] ) ? $settings['success_message'] : 'Thank you! Your submission has been received.' ); ?></p>
			</div>

			<div class="bf-form-error-msg" style="display:none;"></div>
		</div>
		<?php
		return wp_kses( ob_get_clean(), self::get_form_markup_allowed_html() );
	}

	/**
	 * Allowed HTML for rendered form markup (escape late at output).
	 *
	 * @return array
	 */
	public static function get_form_markup_allowed_html() {
		$attrs = array(
			'class'            => true,
			'id'               => true,
			'for'              => true,
			'style'            => true,
			'role'             => true,
			'aria-hidden'      => true,
			'disabled'         => true,
			'required'         => true,
			'type'             => true,
			'name'             => true,
			'value'            => true,
			'placeholder'      => true,
			'min'              => true,
			'max'              => true,
			'rows'             => true,
			'method'           => true,
			'action'           => true,
			'novalidate'       => true,
			'data-form-id'     => true,
			'data-field-id'    => true,
			'data-field-type'  => true,
			'data-step-index'  => true,
			'data-multi-step'  => true,
			'data-steps'       => true,
			'data-preview'     => true,
			'data-step'        => true,
			'data-price'       => true,
			'data-required'    => true,
			'data-multi'       => true,
			'fill'             => true,
			'stroke'           => true,
			'stroke-width'     => true,
			'stroke-linecap'   => true,
			'stroke-linejoin'  => true,
			'opacity'          => true,
			'viewBox'          => true,
			'width'            => true,
			'height'           => true,
			'cx'               => true,
			'cy'               => true,
			'r'                => true,
			'd'                => true,
		);

		return array(
			'div'      => $attrs,
			'form'     => $attrs,
			'input'    => $attrs,
			'button'   => $attrs,
			'label'    => $attrs,
			'span'     => $attrs,
			'p'        => $attrs,
			'h1'       => $attrs,
			'h2'       => $attrs,
			'h3'       => $attrs,
			'h4'       => $attrs,
			'h5'       => $attrs,
			'h6'       => $attrs,
			'select'   => $attrs,
			'option'   => $attrs,
			'textarea' => $attrs,
			'hr'       => $attrs,
			'svg'      => $attrs,
			'circle'   => $attrs,
			'path'     => $attrs,
			'a'        => array(
				'href'   => true,
				'target' => true,
				'rel'    => true,
				'class'  => true,
			),
			'strong'   => array(),
			'em'       => array(),
			'b'        => array(),
			'i'        => array(),
			'u'        => array(),
			'br'       => array(),
			'small'    => array(),
		);
	}

	/**
	 * Escape rendered form HTML for safe output.
	 *
	 * @param string $html Form markup.
	 * @return string
	 */
	public static function kses_form_markup( $html ) {
		return wp_kses( $html, self::get_form_markup_allowed_html() );
	}

	private static function render_field( $field ) {
		$type        = isset( $field['type'] ) ? $field['type'] : 'text';
		$field_id    = isset( $field['id'] ) ? $field['id'] : '';
		$dom_id      = 'formulate_pricing_field_' . $field_id;
		$name        = 'formulate_pricing_fields[' . $field_id . ']';
		$label       = isset( $field['label'] ) ? $field['label'] : '';
		$required    = ! empty( $field['required'] );
		$placeholder = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
		$width       = isset( $field['width'] ) ? $field['width'] : '100';
		$options     = isset( $field['options'] ) ? $field['options'] : array();
		$help        = isset( $field['help_text'] ) ? $field['help_text'] : '';

		$layout_types = array( 'heading', 'paragraph', 'divider', 'spacer' );
		if ( in_array( $type, $layout_types, true ) ) {
			return self::render_layout_field( $field, $width );
		}

		ob_start();
		?>
		<div class="bf-field-wrap bf-w-<?php echo esc_attr( $width ); ?>"
			 data-field-id="<?php echo esc_attr( $field_id ); ?>"
			 data-field-type="<?php echo esc_attr( $type ); ?>">

			<?php $no_top_label = in_array( $type, array( 'card_choice', 'checkbox', 'radio', 'booking_timeslot' ), true ); ?>
			<?php if ( $label && ! $no_top_label ) : ?>
			<label class="bf-label" for="<?php echo esc_attr( $dom_id ); ?>">
				<?php echo esc_html( $label ); ?>
				<?php if ( $required ) : ?><span class="bf-required" aria-hidden="true">*</span><?php endif; ?>
			</label>
			<?php endif; ?>

			<?php
			$control_markup = '';
			switch ( $type ) {
				case 'text':
				case 'email':
				case 'phone':
				case 'url':
					$control_markup = self::render_input( $type, $dom_id, $name, $placeholder, $required, $field );
					break;
				case 'number':
					$control_markup = self::render_number( $dom_id, $name, $placeholder, $required, $field );
					break;
				case 'textarea':
					$control_markup = self::render_textarea( $dom_id, $name, $placeholder, $required, $field );
					break;
				case 'select':
					$control_markup = self::render_select( $dom_id, $name, $required, $options, $field );
					break;
				case 'radio':
					$control_markup = self::render_radio( $dom_id, $name, $label, $required, $options, $field );
					break;
				case 'checkbox':
					$control_markup = self::render_checkbox( $dom_id, $name, $label, $required, $options, $field );
					break;
				case 'card_choice':
					$control_markup = self::render_card_choice( $dom_id, $name, $label, $required, $options, $field );
					break;
				case 'date':
				case 'booking_date':
					$control_markup = self::render_date( $dom_id, $name, $required, $field );
					break;
				case 'time':
					$control_markup = self::render_time( $dom_id, $name, $required, $field );
					break;
				case 'booking_timeslot':
					$control_markup = self::render_timeslot( $dom_id, $name, $label, $required, $options, $field );
					break;
				case 'booking_postcode':
					$control_markup = self::render_input( 'text', $dom_id, $name, $placeholder ?: 'e.g. 1234 AB', $required, $field );
					break;
				case 'hidden':
					$control_markup = '<input type="hidden" id="' . esc_attr( $dom_id ) . '" name="' . esc_attr( $name ) . '" value="' . esc_attr( isset( $field['default_value'] ) ? $field['default_value'] : '' ) . '">';
					break;
				default:
					$control_markup = self::render_input( 'text', $dom_id, $name, $placeholder, $required, $field );
			}
			echo wp_kses( $control_markup, self::get_form_markup_allowed_html() );
			?>

			<?php if ( $help ) : ?>
			<p class="bf-help-text"><?php echo esc_html( $help ); ?></p>
			<?php endif; ?>

			<span class="bf-field-error" role="alert" style="display:none;"></span>
		</div>
		<?php
		return ob_get_clean();
	}

	private static function render_layout_field( $field, $width ) {
		$type = isset( $field['type'] ) ? $field['type'] : '';
		ob_start();
		?>
		<div class="bf-field-wrap bf-w-<?php echo esc_attr( $width ); ?> bf-layout-field">
			<?php if ( 'heading' === $type ) :
				$tag = isset( $field['heading_level'] ) ? $field['heading_level'] : 'h3';
				$allowed_tags = array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' );
				$tag = in_array( $tag, $allowed_tags, true ) ? $tag : 'h3';
			?>
				<<?php echo esc_attr( $tag ); ?> class="bf-heading"><?php echo esc_html( isset( $field['label'] ) ? $field['label'] : '' ); ?></<?php echo esc_attr( $tag ); ?>>
			<?php elseif ( 'paragraph' === $type ) : ?>
				<p class="bf-paragraph"><?php echo wp_kses_post( isset( $field['content'] ) ? $field['content'] : '' ); ?></p>
			<?php elseif ( 'divider' === $type ) : ?>
				<hr class="bf-divider">
			<?php endif; ?>
		</div>
		<?php
		return ob_get_clean();
	}

	private static function render_input( $type, $id, $name, $placeholder, $required, $field ) {
		$input_type = 'text';
		if ( 'email' === $type ) {
			$input_type = 'email';
		} elseif ( 'phone' === $type ) {
			$input_type = 'tel';
		} elseif ( 'url' === $type ) {
			$input_type = 'url';
		}

		return '<input type="' . esc_attr( $input_type ) . '" id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '" class="bf-input" placeholder="' . esc_attr( $placeholder ) . '"' . ( $required ? ' required' : '' ) . '>';
	}

	private static function render_number( $id, $name, $placeholder, $required, $field ) {
		$min = isset( $field['min'] ) ? ' min="' . esc_attr( $field['min'] ) . '"' : '';
		$max = isset( $field['max'] ) ? ' max="' . esc_attr( $field['max'] ) . '"' : '';
		return '<input type="number" id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '" class="bf-input" placeholder="' . esc_attr( $placeholder ) . '"' . $min . $max . ( $required ? ' required' : '' ) . '>';
	}

	private static function render_textarea( $id, $name, $placeholder, $required, $field ) {
		$rows = intval( isset( $field['rows'] ) ? $field['rows'] : 4 );
		return '<textarea id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '" class="bf-input bf-textarea" placeholder="' . esc_attr( $placeholder ) . '" rows="' . esc_attr( $rows ) . '"' . ( $required ? ' required' : '' ) . '></textarea>';
	}

	private static function render_select( $id, $name, $required, $options, $field ) {
		$out  = '<select id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '" class="bf-input bf-select"' . ( $required ? ' required' : '' ) . '>';
		$out .= '<option value="">' . esc_html( isset( $field['placeholder'] ) ? $field['placeholder'] : 'Select an option' ) . '</option>';
		foreach ( $options as $opt ) {
			$opt_label = is_array( $opt ) ? ( isset( $opt['label'] ) ? $opt['label'] : ( isset( $opt['value'] ) ? $opt['value'] : '' ) ) : $opt;
			$opt_value = is_array( $opt ) ? ( isset( $opt['value'] ) ? $opt['value'] : $opt_label ) : $opt;
			$opt_price = is_array( $opt ) ? ( isset( $opt['price'] ) ? $opt['price'] : '' ) : '';
			$dp        = $opt_price ? ' data-price="' . esc_attr( $opt_price ) . '"' : '';
			$out      .= '<option value="' . esc_attr( $opt_value ) . '"' . $dp . '>' . esc_html( $opt_label ) . '</option>';
		}
		$out .= '</select>';
		return $out;
	}

	private static function render_radio( $id, $name, $label, $required, $options, $field ) {
		ob_start();
		if ( $label ) {
			echo '<div class="bf-choice-label">' . esc_html( $label );
			if ( $required ) echo '<span class="bf-required" aria-hidden="true">*</span>';
			echo '</div>';
		}
		echo '<div class="bf-radio-group" role="radiogroup">';
		foreach ( $options as $i => $opt ) {
			$opt_label = is_array( $opt ) ? ( isset( $opt['label'] ) ? $opt['label'] : '' ) : $opt;
			$opt_value = is_array( $opt ) ? ( isset( $opt['value'] ) ? $opt['value'] : $opt_label ) : $opt;
			$opt_price = is_array( $opt ) && isset( $opt['price'] ) ? $opt['price'] : '';
			$opt_icon  = is_array( $opt ) && isset( $opt['icon'] ) ? $opt['icon'] : '';
			$input_id  = $id . '_' . $i;
		$dp        = $opt_price ? ' data-price="' . esc_attr( $opt_price ) . '"' : '';
		echo '<label class="bf-radio-label" for="' . esc_attr( $input_id ) . '"' . $dp . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $dp built with esc_attr()
			echo '<input type="radio" id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $name ) . '" value="' . esc_attr( $opt_value ) . '"' . ( $required ? ' required' : '' ) . '>';
			echo '<span class="bf-radio-custom"></span>';
			if ( $opt_icon ) echo '<span class="bf-option-icon">' . esc_html( $opt_icon ) . '</span>';
			echo '<span class="bf-option-text">' . esc_html( $opt_label ) . '</span>';
			echo '</label>';
		}
		echo '</div>';
		return ob_get_clean();
	}

	private static function render_checkbox( $id, $name, $label, $required, $options, $field ) {
		ob_start();
		if ( $label ) {
			echo '<div class="bf-choice-label">' . esc_html( $label );
			if ( $required ) echo '<span class="bf-required" aria-hidden="true">*</span>';
			echo '</div>';
		}
		echo '<div class="bf-checkbox-group"' . ( $required ? ' data-required="1"' : '' ) . '>';
		foreach ( $options as $i => $opt ) {
			$opt_label = is_array( $opt ) ? ( isset( $opt['label'] ) ? $opt['label'] : '' ) : $opt;
			$opt_value = is_array( $opt ) ? ( isset( $opt['value'] ) ? $opt['value'] : $opt_label ) : $opt;
			$opt_price = is_array( $opt ) && isset( $opt['price'] ) ? $opt['price'] : '';
			$input_id  = $id . '_' . $i;
		$dp        = $opt_price ? ' data-price="' . esc_attr( $opt_price ) . '"' : '';
		echo '<label class="bf-checkbox-label" for="' . esc_attr( $input_id ) . '"' . $dp . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $dp built with esc_attr()
			echo '<input type="checkbox" id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $name ) . '[]" value="' . esc_attr( $opt_value ) . '"' . ( $required && 0 === $i ? ' required' : '' ) . '>';
			echo '<span class="bf-checkbox-custom"></span>';
			echo '<span class="bf-option-text">' . esc_html( $opt_label ) . '</span>';
			echo '</label>';
		}
		echo '</div>';
		return ob_get_clean();
	}

	private static function render_card_choice( $id, $name, $label, $required, $options, $field ) {
		$multi      = ! empty( $field['multi_select'] );
		$input_type = $multi ? 'checkbox' : 'radio';
		$input_name = $multi ? $name . '[]' : $name;
		ob_start();
		if ( $label ) {
			echo '<div class="bf-choice-label">' . esc_html( $label );
			if ( $required ) echo '<span class="bf-required" aria-hidden="true">*</span>';
			echo '</div>';
		}
		echo '<div class="bf-card-group" data-multi="' . ( $multi ? '1' : '0' ) . '"' . ( $required ? ' data-required="1"' : '' ) . '>';
		foreach ( $options as $i => $opt ) {
			$opt_label = is_array( $opt ) ? ( isset( $opt['label'] ) ? $opt['label'] : '' ) : $opt;
			$opt_value = is_array( $opt ) ? ( isset( $opt['value'] ) ? $opt['value'] : $opt_label ) : $opt;
			$opt_price = is_array( $opt ) && isset( $opt['price'] ) ? $opt['price'] : '';
			$opt_icon  = is_array( $opt ) && isset( $opt['icon'] ) ? $opt['icon'] : '';
			$opt_desc  = is_array( $opt ) && isset( $opt['description'] ) ? $opt['description'] : '';
			$input_id  = $id . '_' . $i;
		$dp        = $opt_price ? ' data-price="' . esc_attr( $opt_price ) . '"' : '';
		echo '<label class="bf-card" for="' . esc_attr( $input_id ) . '"' . $dp . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- $dp built with esc_attr()
			echo '<input type="' . esc_attr( $input_type ) . '" id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $input_name ) . '" value="' . esc_attr( $opt_value ) . '"' . ( $required && 0 === $i ? ' required' : '' ) . '>';
		if ( $opt_icon ) echo '<div class="bf-card-icon">' . esc_html( $opt_icon ) . '</div>';
		echo '<div class="bf-card-title">' . esc_html( $opt_label ) . '</div>';
		if ( $opt_desc ) {
			$allowed_html = array(
				'b'      => array(),
				'strong' => array(),
				'em'     => array(),
				'i'      => array(),
				'u'      => array(),
				'br'     => array(),
				'span'   => array( 'style' => array(), 'class' => array() ),
				'a'      => array( 'href' => array(), 'target' => array(), 'rel' => array() ),
				'small'  => array(),
			);
			echo '<div class="bf-card-desc">' . wp_kses( $opt_desc, $allowed_html ) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		}
			echo '</label>';
		}
		echo '</div>';
		return ob_get_clean();
	}

	private static function render_date( $id, $name, $required, $field ) {
		$min     = isset( $field['min_date'] ) ? $field['min_date'] : gmdate( 'Y-m-d' );
		$max     = isset( $field['max_date'] ) ? ' max="' . esc_attr( $field['max_date'] ) . '"' : '';
		return '<input type="date" id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '" class="bf-input bf-date" min="' . esc_attr( $min ) . '"' . $max . ( $required ? ' required' : '' ) . '>';
	}

	private static function render_time( $id, $name, $required, $field ) {
		return '<input type="time" id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '" class="bf-input"' . ( $required ? ' required' : '' ) . '>';
	}

	private static function render_timeslot( $id, $name, $label, $required, $options, $field ) {
		if ( empty( $options ) ) {
			$options = array(
				array( 'label' => '08:00 – 10:00', 'value' => '08:00-10:00', 'price' => '' ),
				array( 'label' => '10:00 – 12:00', 'value' => '10:00-12:00', 'price' => '' ),
				array( 'label' => '12:00 – 14:00', 'value' => '12:00-14:00', 'price' => '' ),
				array( 'label' => '14:00 – 16:00', 'value' => '14:00-16:00', 'price' => '' ),
			);
		}
		return self::render_radio( $id, $name, $label, $required, $options, $field );
	}

	private static function generate_css_vars( $form_id, $styling ) {
		$s = $styling;

		// Border width: outline preset uses 2px; others 1px; soft 0
		$border_w = isset( $s['input_border_width'] ) ? intval( $s['input_border_width'] ) : 1;
		if ( $border_w < 0 ) { $border_w = 0; }

		$vars = array(
			'--bf-primary'              => isset( $s['primary_color'] ) ? $s['primary_color'] : '#2563eb',
			'--bf-bg'                   => isset( $s['bg_color'] ) ? $s['bg_color'] : '#ffffff',
			'--bf-text'                 => isset( $s['text_color'] ) ? $s['text_color'] : '#1f2937',
			'--bf-label'                => isset( $s['label_color'] ) ? $s['label_color'] : '#374151',
			'--bf-radius'               => ( isset( $s['border_radius'] ) ? intval( $s['border_radius'] ) : 8 ) . 'px',
			'--bf-font-size'            => ( isset( $s['font_size'] ) ? intval( $s['font_size'] ) : 15 ) . 'px',
			'--bf-input-bg'             => isset( $s['input_bg'] ) ? $s['input_bg'] : '#f9fafb',
			'--bf-input-border'         => isset( $s['input_border'] ) ? $s['input_border'] : '#d1d5db',
			'--bf-input-border-width'   => $border_w . 'px',
			'--bf-btn-bg'               => isset( $s['button_bg'] ) ? $s['button_bg'] : '#2563eb',
			'--bf-btn-text'             => isset( $s['button_text_color'] ) ? $s['button_text_color'] : '#ffffff',
			'--bf-btn-radius'           => ( isset( $s['button_border_radius'] ) ? intval( $s['button_border_radius'] ) : 8 ) . 'px',
			'--bf-padding'              => ( isset( $s['form_padding'] ) ? intval( $s['form_padding'] ) : 24 ) . 'px',
			'--bf-gap'                  => ( isset( $s['field_gap'] ) ? intval( $s['field_gap'] ) : 16 ) . 'px',
			'--bf-card-bg'              => isset( $s['card_bg'] ) ? $s['card_bg'] : '#f9fafb',
			'--bf-card-border'          => isset( $s['card_border'] ) ? $s['card_border'] : '#d1d5db',
			'--bf-card-sel-bg'          => isset( $s['card_selected_bg'] ) ? $s['card_selected_bg'] : '#eff6ff',
			'--bf-card-sel-border'      => isset( $s['card_selected_border'] ) ? $s['card_selected_border'] : '#2563eb',
			// Step / progress indicator colours — auto-derived from bg_context on save
			'--bf-step-track'           => isset( $s['step_track'] ) ? $s['step_track'] : '#e5e7eb',
			'--bf-step-text'            => isset( $s['step_text'] ) ? $s['step_text'] : '#6b7280',
		);

		$width = isset( $s['form_width'] ) ? intval( $s['form_width'] ) : 100;
		// Target the wrapper by id + class so CSS variables always apply.
		$selector = "#bf-form-{$form_id}.bf-form-wrap.bf-form-isolated";
		$out      = "{$selector}{--bf-form-width:{$width}%;";
		foreach ( $vars as $var => $val ) {
			$out .= $var . ':' . $val . ';';
		}
		$out .= '}';
		return $out;
	}

	public static function enqueue_assets( $is_preview = false ) {
		self::$form_on_page = true;

		$style_deps = array();
		if ( wp_style_is( 'elementor-frontend', 'registered' ) || wp_style_is( 'elementor-frontend', 'enqueued' ) ) {
			$style_deps[] = 'elementor-frontend';
		}

		// Enqueue immediately when the shortcode renders (footer queue if past wp_head).
		wp_enqueue_style(
			'formulate-pricing-form',
			FORMULATE_PRICING_URL . 'assets/css/dynamic-form.css',
			$style_deps,
			FORMULATE_PRICING_VERSION
		);

		// Print again at end of footer so styles load after theme/Elementor in the cascade.
		if ( ! $is_preview && ! has_action( 'wp_footer', array( __CLASS__, 'print_footer_styles' ) ) ) {
			add_action( 'wp_footer', array( __CLASS__, 'print_footer_styles' ), 9999 );
		}

		wp_enqueue_script(
			'formulate-pricing-form',
			FORMULATE_PRICING_URL . 'assets/js/dynamic-form.js',
			array( 'jquery' ),
			FORMULATE_PRICING_VERSION,
			true
		);

		wp_localize_script(
			'formulate-pricing-form',
			'formulatePricingFormData',
			array(
				'ajaxUrl'   => admin_url( 'admin-ajax.php' ),
				'isPreview' => $is_preview ? 1 : 0,
				'priceFormat' => function_exists( 'formulate_pricing_price_format_for_js' ) ? formulate_pricing_price_format_for_js() : null,
				'strings'     => array(
					'checkoutLoading'  => __( 'Preparing your order…', 'formulate-pricing' ),
					'redirectCheckout' => __( 'Redirecting to checkout…', 'formulate-pricing' ),
				),
			)
		);
	}

	/**
	 * Output form CSS at the very end of the page (after theme & Elementor).
	 */
	public static function print_footer_styles() {
		if ( ! self::$form_on_page ) {
			return;
		}

		if ( wp_style_is( 'formulate-pricing-form', 'enqueued' ) ) {
			wp_print_styles( 'formulate-pricing-form' );
		}
	}
}
