<?php
/**
 * Repeater field class for SureForms.
 *
 * Handles initialization and registration of the repeater field functionality,
 * including scripts, styles, and block integration.
 *
 * @package sureforms-pro
 * @since 1.11.0
 */

namespace SRFM_Pro\Inc\Business\Repeater;

use SRFM\Inc\Helper;
use SRFM_Pro\Inc\Helper as Pro_Helper;
use SRFM_Pro\Inc\Traits\Get_Instance;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Init class for repeater field functionality.
 *
 * Initializes and sets up the repeater field, including registering blocks,
 * enqueueing assets, and handling render functions.
 *
 * @since 1.11.0
 */
class Init {
	use Get_Instance;

	/**
	 * Flag to track if block scripts are already loaded.
	 *
	 * @var bool
	 * @since 1.11.0
	 */
	public static $script_dep_blocks = false;

	/**
	 * Constructor.
	 *
	 * Initializes hooks and filters for repeater field functionality.
	 *
	 * @since 1.11.0
	 * @return void
	 */
	public function __construct() {
		add_filter( 'srfm_process_field_value', [ $this, 'process_repeater_field' ], 10, 2 );
		add_action( 'srfm_entry_render_field', [ $this, 'render_repeater_field' ], 10, 1 );
		add_filter( 'srfm_register_additional_blocks', [ $this, 'register_field' ] );
		add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_admin_scripts' ] );
		add_filter( 'srfm_allowed_block_types', [ $this, 'allow_repeater_block_in_sureforms' ], 10, 1 );
		add_filter( 'srfm_blocks', [ $this, 'allow_repeater_block_in_sureforms' ] );
		add_filter( 'render_block', [ $this, 'generate_render_script' ], 10, 2 );
		add_filter( 'srfm_should_skip_block_from_edit_entry', [ $this, 'should_skip_block_from_edit_entry' ], 10, 2 );
		add_action( 'srfm_process_block_on_edit_entry', [ $this, 'process_block_on_edit_entry' ], 10, 1 );
		add_filter( 'srfm_smart_tags_is_block_processed_externally', [ $this, 'process_repeater_field_smart_tags' ], 10, 1 );
		add_action( 'srfm_before_processing_all_data_field', [ $this, 'render_repeater_field_in_email' ], 10, 1 );
		add_filter( 'srfm_all_data_field_row', [ $this, 'should_add_field_row_in_email' ], 10, 2 );
		add_filter( 'srfm_is_smart_tag_value_verified_as_array', [ $this, 'is_smart_tag_value_verified_as_array' ], 10, 2 );
		add_filter( 'srfm_map_slug_to_submission_data_array', [ $this, 'webhook_map_slug_to_submission_data' ], 10, 1 );
		add_action( 'srfm_pro_before_update_entry_data', [ $this, 'process_repeater_field_on_update_entry_data' ], 10, 1 );
		add_filter( 'srfm_sample_data_filter', [ $this, 'add_sample_data_for_repeater' ], 10, 1 );
		add_filter( 'srfm_prepare_submission_data', [ $this, 'prepare_submission_data_for_repeater' ], 10, 1 );
		add_action( 'srfm_enqueue_common_field_assets', [ $this, 'get_repeater_assets' ] );
		add_filter( 'srfm_handle_special_block', [ $this, 'handle_repeater_for_edit_entry' ], 10, 5 );
		add_filter( 'srfm_is_special_block', [ $this, 'mark_repeater_as_special_block' ], 10, 2 );
		add_filter( 'srfm_normalize_csv_field_value', [ $this, 'format_repeater_for_csv_export' ], 10, 2 );
	}

	/**
	 * Process repeater block when editing an entry.
	 *
	 * Handles the rendering of repeater blocks in the entry edit view by:
	 * 1. Checking if this is a repeater block
	 * 2. Finding the corresponding entry data for this block
	 * 3. Attaching the entry data to the block attributes
	 * 4. Rendering the block with the entry data
	 *
	 * @since 1.11.0
	 * @param array<string,mixed> $args {
	 *     Arguments for processing the block.
	 *     @type string $block_name    The name of the block being processed
	 *     @type array  $current_block The block data and attributes
	 *     @type array  $entry         The entry data containing form submissions
	 * }
	 * @return void
	 */
	public function process_block_on_edit_entry( $args ) {
		// Bail if not a repeater block.
		if ( ! isset( $args['block_name'] ) || 'srfm/repeater' !== $args['block_name'] ) {
			return;
		}

		// Bail if no block data available.
		if ( ! isset( $args['current_block'] ) || ! is_array( $args['current_block'] ) || ! isset( $args['current_block']['attrs'] ) ) {
			return;
		}

		// Get the block ID and construct the entry key prefix.
		$get_current_block_id              = $args['current_block']['attrs']['block_id'];
		$prepare_entry_key_starting_string = 'srfm-repeater-' . $get_current_block_id;

		// Search entry data for matching repeater field.
		$entry_fields = is_array( $args['entry'] ) && isset( $args['entry']['form_data'] ) ? $args['entry']['form_data'] : null;

		if ( ! is_array( $entry_fields ) ) {
			return;
		}

		$entry_to_set = null;
		foreach ( $entry_fields as $field_key => $field_value ) {
			if ( is_string( $field_key ) && strpos( $field_key, $prepare_entry_key_starting_string ) === 0 ) {
				$entry_to_set = $field_value;
				break;
			}
		}

		// Add entry data to block attributes if found.
		if ( $entry_to_set ) {
			$args['current_block']['attrs']['entry_data'] = $entry_to_set;
		}

		echo render_block( $args['current_block'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- We need to print the form blocks markup.
	}

	/**
	 * Determines if a block should be skipped from edit entry functionality.
	 *
	 * @param bool                 $should_skip_block_on_entry Whether the block should be skipped.
	 * @param array<string, mixed> $args Arguments containing field information.
	 *
	 * @since 1.11.0
	 * @return bool Modified decision on whether to skip block from edit entry.
	 */
	public function should_skip_block_from_edit_entry( $should_skip_block_on_entry, $args ) {
		if ( isset( $args['block_name'] ) && 'srfm/repeater' === $args['block_name'] ) {
			return true;
		}
		return $should_skip_block_on_entry;
	}

	/**
	 * Register repeater field block.
	 *
	 * Adds repeater block to the list of registered blocks.
	 *
	 * @param array $blocks Array of registered blocks.
	 *
	 * @since 1.11.0
	 * @return array Modified array of blocks.
	 */
	public function register_field( $blocks ) {
		$blocks[] = [
			'dir'       => SRFM_PRO_DIR . 'inc/business/repeater/block.php',
			'namespace' => 'SRFM_PRO\\Inc\\Blocks',
		];

		return $blocks;
	}

	/**
	 * Enqueue admin scripts for repeater field.
	 *
	 * Loads necessary JavaScript files for the block editor.
	 *
	 * @since 1.11.0
	 * @return void
	 */
	public function enqueue_admin_scripts() {
		$scripts = [
			[
				'unique_file'        => 'repeaterAdmin',
				'unique_handle'      => 'repeater-admin',
				'extra_dependencies' => [],
			],
		];
		$this->enqueue_scripts( $scripts );
		$this->enqueue_styles();
	}

	/**
	 * Allow repeater block in sureforms.
	 *
	 * Adds repeater block to the list of allowed blocks.
	 *
	 * @param array<string> $blocks Array of blocks.
	 *
	 * @since 1.11.0
	 * @return array<string>
	 */
	public function allow_repeater_block_in_sureforms( $blocks ) {
		return array_merge(
			$blocks,
			[
				'srfm/repeater',
			]
		);
	}

	/**
	 * Generate render script for repeater block.
	 *
	 * Loads necessary assets when repeater block is rendered.
	 *
	 * @param string $block_content Entire Block Content.
	 * @param array  $block Block Properties As An Array.
	 *
	 * @since 1.11.0
	 * @return string
	 */
	public function generate_render_script( $block_content, $block ) {
		if ( isset( $block['blockName'] ) && 'srfm/repeater' === $block['blockName'] ) {
			if ( ! self::$script_dep_blocks ) {
				self::$script_dep_blocks = true;
				$this->get_repeater_assets();
			}
		}
		return $block_content;
	}

	/**
	 * Load repeater assets.
	 *
	 * Enqueues necessary styles and scripts for repeater functionality.
	 *
	 * @since 1.11.0
	 * @return void
	 */
	public function get_repeater_assets() {
		$this->enqueue_styles();
		$scripts = [
			[
				'unique_file'        => 'repeaterFrontend',
				'unique_handle'      => 'repeater-frontend',
				'extra_dependencies' => [ 'wp-a11y' ],
			],
		];
		$this->enqueue_scripts( $scripts );
	}

	/**
	 * Enqueue required styles for the repeater field.
	 *
	 * Loads necessary CSS files for both editor and frontend display.
	 *
	 * @since 1.11.0
	 * @return void
	 */
	public function enqueue_styles() {
		$file_prefix = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? '' : '.min';
		$dir_name    = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? 'unminified' : 'minified';
		$css_uri     = SRFM_PRO_URL . 'assets/css/' . $dir_name . '/package/business/';
		// This checks the text direction of the site, if it is RTL then it will load the RTL version of the CSS file.
		$rtl = is_rtl() ? '-rtl' : '';

		// Load the repeater styles.
		wp_enqueue_style( SRFM_PRO_SLUG . '-repeater', $css_uri . 'repeater' . $file_prefix . $rtl . '.css', [], SRFM_PRO_VER );
	}

	/**
	 * Process repeater field values.
	 *
	 * Sanitizes and processes values from repeater fields for storage.
	 *
	 * @param mixed $value Field value to process.
	 * @param array $args Arguments containing field information.
	 *
	 * @since 1.11.0
	 * @return array|mixed Processed field value.
	 */
	public function process_repeater_field( $value, $args ) {
		// Check if this is a repeater block.
		if ( 'srfm-repeater' === $args['field_block_name'] && is_array( $value ) ) {
			$processed_value = [];
			// Iterate through each repeater item and sanitize values.
			foreach ( $value as $repeater_item ) {
				$processed_item = [];
				foreach ( $repeater_item as $key => $item_value ) {
					$processed_item[ $key ] = is_string( $item_value ) ? htmlspecialchars( $item_value ) : $item_value;
				}
				$processed_value[] = $processed_item;
			}

			return [
				'value'        => $processed_value,
				'is_processed' => true,
			];
		}

		return $value;
	}

	/**
	 * Renders repeater field in the entry view.
	 *
	 * @param array $args Arguments containing field information.
	 *
	 * @since 1.11.0
	 * @return void
	 */
	public function render_repeater_field( $args ) {
		if ( 'srfm-repeater' === $args['block_name'] ) {
			$this->render_single_page_entry_rows( $args );
		}
	}

	/**
	 * Renders individual repeater entries in a table format.
	 *
	 * Creates a table view showing all repeater field entries with their
	 * respective labels and values.
	 *
	 * @param array $args Arguments containing field information and values.
	 *
	 * @since 1.11.0
	 * @return void
	 */
	public function render_single_page_entry_rows( $args ) {
		$base_label = $args['processed_label'];

		// Loop through each field item (0, 1, 2, etc.).
		foreach ( $args['value'] as $index => $field_item ) {
			// Create incremental label (Repeater 1, Repeater 2, etc.).
			$repeater_number = $index + 1;
			$current_label   = sprintf( '%s #%d', $base_label, $repeater_number );

			// Create a header row for this repeater instance.
			?>
			<tr>
				<td style="font-weight: bold;" colspan="2"><?php echo esc_html( $current_label ); ?></td>
			</tr>
			<?php

			// Loop through each key-value pair in this field item.
			foreach ( $field_item as $field_key => $field_value ) {
				// Skip empty values.
				if ( empty( $field_value ) ) {
					continue;
				}

				// Extract and decrypt the field label.
				$label = explode( '-lbl-', $field_key )[1];
				$label = explode( '-', $label )[0];
				$label = $label ? Helper::decrypt( $label ) : '';

				?>
				<tr>
					<td><?php echo esc_html( $label ); ?></td>
					<td><?php echo esc_html( $field_value ); ?></td>
				</tr>
				<?php
			}
		}
	}

	/**
	 * Process repeater field for smart tags.
	 *
	 * Handles the processing of repeater fields in smart tags by generating
	 * HTML table output for email notifications and other smart tag contexts.
	 * For webhook processing, returns form-encoded data array instead of HTML.
	 *
	 * @param array $args Arguments containing block processing information.
	 *
	 * @since 1.11.0
	 * @return void|array Modified args with processed value for repeater blocks.
	 */
	public function process_repeater_field_smart_tags( $args ) {
		// Check if this is a repeater block.
		if ( ! isset( $args['block_type'] ) || 0 !== strpos( $args['block_type'], 'srfm-repeater' ) ) {
			return;
		}

		// Check if we have submission data.
		if ( ! isset( $args['submission_item_value'] ) || ! is_array( $args['submission_item_value'] ) ) {
			return;
		}

		$submission_data = $args['submission_item_value'];
		$base_label      = $args['submission_item_key'] ?? '';
		$base_label      = $this->process_the_label( $base_label );
		$base_label      = empty( $base_label ) ? __( 'Repeater', 'sureforms-pro' ) : $base_label;

		// Check if this is webhook processing.
		$is_webhook_processing = isset( $args['submission_data']['_is_webhook_processing'] ) && $args['submission_data']['_is_webhook_processing'];

		if ( $is_webhook_processing ) {
			// For webhook processing, return form-encoded data array.
			$processed_data          = $this->prepare_repeater_data_for_webhook( $submission_data, $base_label );
			$args['processed_value'] = $processed_data;
		} else {
			// Generate HTML table for repeater data.
			$html_output             = $this->generate_repeater_table_html( $submission_data, $base_label );
			$args['processed_value'] = $html_output;
		}

		return $args;
	}

	/**
	 * Process and extract field label from field key.
	 *
	 * Extracts the encrypted field label from a field key string and decrypts it
	 * to get the human-readable label.
	 *
	 * @param string $field_key The field key containing the encrypted label.
	 *
	 * @since 1.11.0
	 * @return string The decrypted field label or empty string if not found.
	 */
	public function process_the_label( $field_key ) {
		// Extract and decrypt the field label.
		$label_parts = explode( '-lbl-', (string) $field_key );
		if ( isset( $label_parts[1] ) ) {
			$parts = explode( '-', $label_parts[1] );
			if ( isset( $parts[0] ) ) {
				$label_part = $parts[0];
				$label      = ! empty( $label_part ) ? Helper::decrypt( $label_part ) : '';
			} else {
				$label = '';
			}
		} else {
			$label = '';
		}

		return $label;
	}

	/**
	 * Renders repeater field in email template.
	 *
	 * Handles the rendering of repeater fields in the all data section of emails
	 * by generating HTML table output with proper styling.
	 *
	 * @param array $args Arguments containing field information.
	 *
	 * @since 1.11.0
	 * @return void
	 */
	public function render_repeater_field_in_email( $args ) {
		// Check if this is a repeater block.
		if ( ! isset( $args['block_name'] ) || 'srfm-repeater' !== $args['block_name'] ) {
			return;
		}

		// Check if we have field data.
		if ( ! isset( $args['value'] ) || ! is_array( $args['value'] ) ) {
			return;
		}

		$base_label = $args['processed_label'] ?? __( 'Repeater', 'sureforms-pro' );

		echo $this->generate_field_row( $args['value'], $base_label, true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Ignored to render html
	}

	/**
	 * Determines if a field row should be added in email template.
	 *
	 * Prevents repeater fields from being processed by the core email template
	 * since they are handled separately by the render_repeater_field_in_email method.
	 *
	 * @param bool  $should_add_field_row Whether the field row should be added.
	 * @param array $args Arguments containing field information.
	 *
	 * @since 1.11.0
	 * @return bool Modified decision on whether to add field row.
	 */
	public function should_add_field_row_in_email( $should_add_field_row, $args ) {
		if ( isset( $args['block_name'] ) && 'srfm-repeater' === $args['block_name'] ) {
			return false;
		}

		return $should_add_field_row;
	}

	/**
	 * Verifies if a smart tag value should be treated as an array.
	 *
	 * This function checks if a smart tag value from a repeater field should be returned as an array
	 * rather than being converted to a string. It analyzes the submission data to find repeater fields
	 * and compares their slugs with the requested smart tag to determine if array format should be preserved.
	 *
	 * @param bool  $is_verified_value The default verification status (false).
	 * @param array $args {
	 *     Arguments passed to the filter.
	 *
	 *     @type string     $tag             The smart tag being processed.
	 *     @type array|null $submission_data The form submission data.
	 *     @type array|null $form_data       The form configuration data.
	 *     @type mixed      $value           The smart tag value.
	 * }
	 * @since 1.11.0
	 * @return bool True if value should be treated as array, false otherwise.
	 */
	public function is_smart_tag_value_verified_as_array( $is_verified_value, $args ) {
		// Return default if required args are missing.
		if ( empty( $args ) || empty( $args['tag'] ) ) {
			return $is_verified_value;
		}

		// Find all repeater fields in submission data.
		$find_repeater_data = array_filter(
			$args['submission_data'],
			static function( $key ) {
				return strpos( $key, 'srfm-repeater-' ) === 0;
			},
			ARRAY_FILTER_USE_KEY
		);

		// Return default if no repeater data found or it's not an array.
		if ( ! is_array( $find_repeater_data ) ) {
			return $is_verified_value;
		}

		// Extract the field slug from the smart tag (format: {form:field-slug}).
		$get_slug = preg_match( '/\{form:(.*?)}/', $args['tag'], $matches ) ? $matches[1] : '';

		if ( empty( $get_slug ) ) {
			return $is_verified_value;
		}

		// Loop through repeater fields to find matching slug.
		foreach ( array_keys( $find_repeater_data ) as $key ) {
			// Split key into parts using "-lbl-" delimiter.
			$parts = explode( '-lbl-', $key );
			if ( count( $parts ) < 2 ) {
				continue;
			}

			// Get the label part after "-lbl-".
			$label_part = $parts[1];

			// Split label into segments.
			$label_segments = explode( '-', $label_part );
			if ( count( $label_segments ) < 2 ) {
				continue;
			}

			// Remove encoded string and reconstruct field slug.
			array_shift( $label_segments );
			$field_slug = implode( '-', $label_segments );

			// Return true if slugs match.
			if ( $field_slug === $get_slug ) {
				return true;
			}
		}

		return $is_verified_value;
	}

	/**
	 * Maps repeater field data for webhook submissions.
	 *
	 * Processes repeater field data into a format suitable for webhook submissions by:
	 * 1. Identifying repeater fields by their key prefix
	 * 2. Converting nested repeater items into indexed key-value pairs
	 * 3. Mapping field slugs to their corresponding submission data
	 *
	 * @since 1.11.0
	 * @param array<string,mixed> $args {
	 *     Arguments for mapping submission data.
	 *     @type string       $key   The field key to check for repeater prefix
	 *     @type string       $slug  The field slug to use in mapped data
	 *     @type array<mixed> $value The repeater field values to process
	 * }
	 * @return array<string,mixed> Modified args with processed repeater data
	 */
	public function webhook_map_slug_to_submission_data( $args ) {
		// Validate that this is a repeater field by checking the key prefix.
		if ( isset( $args['key'] ) && ! empty( $args['value'] ) && is_array( $args['value'] ) && is_string( $args['key'] ) && strpos( $args['key'], 'srfm-repeater-' ) === 0 ) {
			// Validate required args exist.
			$slug          = ! empty( $args['slug'] ) && is_string( $args['slug'] ) ? $args['slug'] : __( 'Repeater', 'sureforms-pro' );
			$repeater_data = [];

			// Process each repeater item.
			foreach ( $args['value'] as $index => $field_item ) {
				// Skip non-array items as they are invalid.
				if ( ! is_array( $field_item ) ) {
					continue;
				}

				// Map the nested field items to their submission data.
				$get_repeater_items = Helper::map_slug_to_submission_data( $field_item );

				// Create unique key for this repeater item using index.
				$slug_with_index                   = $slug . '-' . ( $index + 1 );
				$repeater_data[ $slug_with_index ] = $get_repeater_items;
			}

			// Only update args if we have processed data.
			if ( ! empty( $repeater_data ) ) {
				$args['value']        = $repeater_data;
				$args['is_processed'] = true;
			}
		}

		return $args;
	}

	/**
	 * Generates HTML table rows for repeater field data.
	 *
	 * @param array  $repeater_data Array of repeater field data where each element represents a repeater instance.
	 * @param string $base_label    Base label for repeater instances (e.g., "Repeater" becomes "Repeater 1", "Repeater 2").
	 * @param bool   $for_all_data  Whether to format the output for all data display (affects column layout).
	 *
	 * @return string HTML string containing table rows for the repeater data.
	 */
	public function generate_field_row( $repeater_data, $base_label = '', $for_all_data = false ) {
		$html = '';

		$td_style = 'font-weight: 500;font-size: 14px;line-height: 20px;padding: 12px;text-align:left;border-right: none;word-break: break-word;border-bottom: 1px solid #E5E7EB;';

		// Loop through each repeater item.
		foreach ( $repeater_data as $index => $field_item ) {
			if ( ! is_array( $field_item ) ) {
				continue;
			}

				// Create incremental label (Repeater 1, Repeater 2, etc.).
				$repeater_number = $index + 1;
				$current_label   = sprintf( '%s #%d', $base_label, $repeater_number );

				// Create header row for this repeater instance using output buffering.
				ob_start();
			?>
				<tr>
					<th colspan="2" style="<?php echo esc_attr( $td_style ); ?>color: #1e293b;background-color: #f1f5f9;"><?php echo esc_html( $current_label ); ?></th>
				</tr>
				<?php
				$html .= ob_get_clean();

				// Loop through each field in this repeater instance.
				foreach ( $field_item as $field_key => $field_value ) {
					// Skip empty values.
					if ( empty( $field_value ) ) {
						$field_value = '';
					}

					$label = $this->process_the_label( $field_key );

					// Create data row using output buffering.
					ob_start();
					?>
				<tr>
					<?php if ( $for_all_data ) { ?>
						<td style="<?php echo esc_attr( $td_style ); ?>color: #111827;">
							<div style="display:flex;align-items:center;gap:10px;">
								<div style="width:163px;max-width:163px;min-width:163px;color: #111827;"><?php echo esc_html( $label ); ?>:</div>
								<div style="color: #4B5563;"><?php echo esc_html( $field_value ); ?></div>
							</div>
						</td>
					<?php } else { ?>
						<td style="<?php echo esc_attr( $td_style ); ?>color: #111827;width:163px;max-width:163px;min-width:163px;"><?php echo esc_html( $label ); ?>:</td>
						<td style="<?php echo esc_attr( $td_style ); ?>color: #4B5563;"><?php echo esc_html( $field_value ); ?></td>
					<?php } ?>
				</tr>
					<?php
					$html .= ob_get_clean();
				}
		}

		return $html;
	}

	/**
	 * Process repeater field data when updating an entry.
	 *
	 * Compares saved repeater field values with updated values and logs any changes.
	 * Updates the change counter when differences are found.
	 *
	 * @since 1.11.0
	 * @param array<string,mixed> $args {
	 *     Arguments for processing the repeater field.
	 *     @type string $field_name      Field name/key being processed
	 *     @type array  $field_value     New field value being saved
	 *     @type array  $saved_form_data Previously saved form data
	 *     @type object $entry_instance  Instance of entry manager class
	 *     @type int    $changed         Reference to counter tracking number of changes
	 * }
	 * @return void
	 */
	public function process_repeater_field_on_update_entry_data( $args ) {
		// Validate required arguments.
		if ( ! is_array( $args ) || empty( $args['field_name'] ) || ! is_string( $args['field_name'] ) || empty( $args['saved_form_data'] ) || ! is_array( $args['saved_form_data'] ) ) {
			return;
		}

		// Check if this is a repeater field.
		if ( false === strpos( $args['field_name'], 'srfm-repeater' ) ) {
			return;
		}

		// Validate saved form data exists for this field.
		if ( empty( $args['saved_form_data'][ $args['field_name'] ] ) ) {
			return;
		}

		// Validate field value is an array.
		if ( ! isset( $args['field_value'] ) || ! is_array( $args['field_value'] ) ) {
			return;
		}

		// Validate entry instance.
		if ( ! isset( $args['entry_instance'] ) || ! is_object( $args['entry_instance'] ) || ! method_exists( $args['entry_instance'], 'update_log' ) ) {
			return;
		}

		$saved_repeater_value  = $args['saved_form_data'][ $args['field_name'] ];
		$repeater_parent_label = Helper::get_field_label_from_key( $args['field_name'] );

		foreach ( $args['field_value'] as $repeater_parent_key => $repeater_item ) {
			if ( ! is_array( $repeater_item ) ) {
				continue;
			}

			foreach ( $repeater_item as $key => $item_value ) {
				// Validate saved value exists.
				if ( ! isset( $saved_repeater_value[ $repeater_parent_key ][ $key ] ) ) {
					continue;
				}

				$saved_value = $saved_repeater_value[ $repeater_parent_key ][ $key ];

				// Skip if no change.
				if ( $saved_value === $item_value ) {
					continue;
				}

				$current_item_label        = Helper::get_field_label_from_key( $key );
				$prepare_label_with_parent = sprintf(
					'%1$s #%2$d: %3$s',
					$repeater_parent_label,
					$repeater_parent_key + 1,
					$current_item_label
				);

				// Update entry log with change.
				// Validate log methods exist.
				if ( ! method_exists( $args['entry_instance'], 'get_last_log_key' ) || ! method_exists( $args['entry_instance'], 'update_log' ) ) {
					continue;
				}

				// Get last log key.
				$last_log_key = $args['entry_instance']->get_last_log_key();
				if ( empty( $last_log_key ) && 0 !== $last_log_key ) {
					continue;
				}

				$args['entry_instance']->update_log(
					$last_log_key,
					null,
					[
						sprintf(
							'%1$s %2$s &#8594; %3$s',
							'<strong>' . $prepare_label_with_parent . ': </strong>',
							"<del>{$saved_value}</del>",
							$item_value
						),
					]
				);

				// Increment change counter.
				if ( isset( $args['changed'] ) ) {
					$args['changed']++;
				}
			}
		}
	}

	/**
	 * Prepare submission data for repeater fields.
	 *
	 * This method processes repeater field data from form submissions and formats it
	 * for storage and email notifications. It extracts the repeater items and creates
	 * a structured array with proper labels and values.
	 *
	 * @param array $submission_args {
	 *     Arguments containing field information from the submission.
	 *
	 *     @type array  $block_parts  Array of block parts extracted from field key.
	 *     @type string $field_key    The field key from submission data.
	 *     @type mixed  $field_value  The field value from submission data.
	 * }
	 *
	 * @since 1.11.0
	 * @return array {
	 *     Formatted repeater data for submission.
	 *
	 *     @type string $label The formatted label for the repeater field.
	 *     @type array  $value Array of repeater items with their field values.
	 * }
	 */
	public function prepare_submission_data_for_repeater( $submission_args ) {
		// Validate that block_parts exists and is an array.
		if ( ! isset( $submission_args['block_parts'] ) || ! is_array( $submission_args['block_parts'] ) ) {
			return [];
		}

		$block_parts = $submission_args['block_parts'];

		// Verify that this is a repeater field by checking the first part starts with 'srfm-repeater'.
		if ( ! isset( $block_parts[0] ) || ! str_starts_with( $block_parts[0], 'srfm-repeater' ) ) {
			return [];
		}

		// Extract the repeater block key slug from the second part.
		if ( ! isset( $block_parts[1] ) || ! is_string( $block_parts[1] ) ) {
			return [];
		}

		$repeater_block_key_slug = $block_parts[1];

		// Get the repeater items from the field value.
		$repeater_field_items = $submission_args['field_value'];

		// Ensure the field value is an array of repeater items.
		if ( ! is_array( $repeater_field_items ) ) {
			return [];
		}

		$formatted_repeater_items = [];

		// Process each repeater item.
		foreach ( $repeater_field_items as $single_repeater_item ) {

			$processed_item_values = [];

			// Process each field within the repeater item.
			foreach ( $single_repeater_item as $field_key => $field_value ) {
				// Split the key to extract the label part.
				$key_with_label_slug = explode( '-lbl-', $field_key );
				$label_key_parts     = explode( '-', $key_with_label_slug[1] );

				// Remove the first item (block type) and join the rest to form the clean field label.
				$clean_label_parts = array_slice( $label_key_parts, 1 );
				$clean_field_label = implode( '-', $clean_label_parts );

				// Only add fields with valid labels.
				if ( ! empty( $clean_field_label ) ) {
					$processed_item_values[ $clean_field_label ] = html_entity_decode( esc_attr( Helper::get_string_value( $field_value ) ) );
				}
			}

			$formatted_repeater_items[] = $processed_item_values;
		}

		// Create a clean label for the repeater field by removing the block type prefix.
		$repeater_label_parts = explode( '-', $repeater_block_key_slug );
		$clean_repeater_label = implode( '-', array_slice( $repeater_label_parts, 1 ) );

		return [
			'label' => $clean_repeater_label,
			'value' => $formatted_repeater_items,
		];
	}

	/**
	 * Add sample data for repeater fields.
	 *
	 * Adds sample data for the repeater field type to the dummy data array used
	 * for SureTriggers integration and other contexts that need field sample data.
	 *
	 * @param array $dummy_data  The existing dummy data array.
	 *
	 * @since 1.11.0
	 * @return array Modified dummy data array with repeater sample data.
	 */
	public function add_sample_data_for_repeater( $dummy_data ) {
		// Validate input parameters.
		if ( ! is_array( $dummy_data ) ) {
			return [];
		}

		// Add sample data for repeater block type.
		$dummy_data['srfm/repeater'] = [
			[
				'text-field'        => __( 'Sample text entry 1', 'sureforms-pro' ),
				'email-field'       => 'user1@example.com',
				'number-field'      => 100,
				'textarea-field'    => __( 'Sample textarea entry 1', 'sureforms-pro' ),
				'url-field'         => 'https://example1.com',
				'phone-field'       => '+1234567890',
				'date-picker-field' => '2023-10-01',
				'dropdown-field'    => 'Option 1',
				'time-picker-field' => '10:00 AM',
			],
			[
				'text-field'        => __( 'Sample text entry 2', 'sureforms-pro' ),
				'email-field'       => 'user2@example.com',
				'number-field'      => 200,
				'textarea-field'    => __( 'Sample textarea entry 2', 'sureforms-pro' ),
				'url-field'         => 'https://example2.com',
				'phone-field'       => '+1987654321',
				'date-picker-field' => '2023-10-02',
				'dropdown-field'    => 'Option 2',
				'time-picker-field' => '11:00 AM',
			],
			[
				'text-field'        => __( 'Sample text entry 3', 'sureforms-pro' ),
				'email-field'       => 'user3@example.com',
				'number-field'      => 300,
				'textarea-field'    => __( 'Sample textarea entry 3', 'sureforms-pro' ),
				'url-field'         => 'https://example3.com',
				'phone-field'       => '+1555123456',
				'date-picker-field' => '2023-10-03',
				'dropdown-field'    => 'Option 3',
				'time-picker-field' => '12:00 AM',
			],
		];

		return $dummy_data;
	}

	/**
	 * Remove border from the last table row in repeater table HTML.
	 *
	 * This method finds the last <tr> element in the provided HTML content
	 * and removes the border-bottom style from all <td> elements within it.
	 * This is used to clean up the visual appearance of repeater tables.
	 *
	 * @param string $content HTML content containing table structure.
	 *
	 * @since 1.11.0
	 * @return string Modified HTML content with border removed from last row.
	 */
	public function remove_border_from_last_tr_td_table( $content ) {
		// Check if content contains table and tr elements.
		if ( empty( $content ) || ! preg_match( '/<tr[^>]*>/i', $content ) ) {
			return $content;
		}

		// Find and modify the last tr in one go.
		$modified_html = preg_replace_callback(
			'/(.*)(<tr[^>]*>.*?<\/tr>)(?!.*<tr)/is',
			static function( $matches ) {
				$before_last_tr = $matches[1];
				$last_tr        = $matches[2];

				// Remove border-bottom from all td elements in this tr.
				$modified_tr = preg_replace_callback(
					'/(<td[^>]*style\s*=\s*["\'])([^"\']*?)(["\'][^>]*>)/i',
					static function( $td_match ) {
						$start = $td_match[1];
						$style = $td_match[2];

						// Remove ONLY border-bottom (not border-bottom-width, etc.).
						$style = preg_replace( '/\s*border-bottom\s*:[^;]*;?/i', '', $style );
						$style = is_string( $style ) ? $style : '';

						// Clean up multiple semicolons and trim.
						$style = preg_replace( '/;+/', ';', $style );
						$style = is_string( $style ) ? trim( $style, '; ' ) : '';

						$end = $td_match[3];
						return $start . $style . $end;
					},
					$last_tr
				);

				return $before_last_tr . $modified_tr;
			},
			$content
		);

		return is_string( $modified_html ) ? $modified_html : '';
	}

	/**
	 * Handle repeater field like address for REST API.
	 *
	 * @param mixed              $field_value The field value.
	 * @param string             $block_type The block type.
	 * @param array              $block The block data.
	 * @param array              $sureforms_blocks Block attributes.
	 * @param \SRFM\Inc\Rest_Api $rest_api_instance REST API instance.
	 * @return mixed
	 */
	public function handle_repeater_for_edit_entry( $field_value, $block_type, $block, $sureforms_blocks, $rest_api_instance ) {
		if ( 'repeater' === $block_type && isset( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) {
			$repeater_items = [];

			// Get current dropdown counter as base for repeater items.
			$base_counter = $rest_api_instance->get_dropdown_counter();

			// Process each repeater item with same base counter.
			if ( is_array( $field_value ) ) {
				foreach ( $field_value as $item ) {
					$inner_fields = [];
					$rest_api_instance->extract_form_fields( $block['innerBlocks'], $sureforms_blocks, $inner_fields, $item, false, $base_counter );
					$repeater_items[] = $inner_fields;
				}
			}

			return $repeater_items;
		}
		return $field_value;
	}

	/**
	 * Mark repeater as special block that should skip inner fields.
	 *
	 * @param bool   $is_special_block Whether the block is a special block.
	 * @param string $block_type The block type.
	 * @return bool
	 */
	public function mark_repeater_as_special_block( $is_special_block, $block_type ) {
		if ( 'repeater' === $block_type ) {
			return true;
		}
		return $is_special_block;
	}

	/**
	 * Format repeater field for CSV export.
	 *
	 * Hooks into the srfm_normalize_csv_field_value filter to provide proper
	 * CSV formatting for repeater fields. This follows the same pattern as
	 * other repeater field processing methods in this class.
	 *
	 * @param array<int, array<string, mixed>>|mixed $field_value The field value to normalize.
	 * @param string                                 $field_key   The field key for identifying field type.
	 *
	 * @since 2.3.0
	 * @return array<int, array<string, mixed>>|mixed The formatted value for CSV export, or original value if not a repeater field.
	 */
	public function format_repeater_for_csv_export( $field_value, $field_key ) {
		// Check if this is a repeater field.
		if ( ! $this->is_repeater_field( $field_key ) ) {
			return $field_value;
		}

		/**
		 * The repeater field value as an array.
		 *
		 * @var array<int, array<string, mixed>> $field_value_array The repeater field value.
		 */
		$field_value_array = Helper::get_array_value( $field_value );

		// Format the repeater data for CSV.
		return $this->format_repeater_data_for_csv( $field_value_array );
	}

	/**
	 * Prepare repeater data for webhook processing.
	 *
	 * Creates an array representation of repeater field data suitable for
	 * webhook requests.
	 *
	 * @param array  $repeater_data The repeater field data.
	 * @param string $base_label Base label for the repeater field.
	 *
	 * @since 1.11.0
	 * @return array Array representation of repeater data for webhooks.
	 */
	private function prepare_repeater_data_for_webhook( $repeater_data, $base_label = '' ) {
		if ( empty( $repeater_data ) || ! is_array( $repeater_data ) ) {
			return [];
		}

		$webhook_data = [];

		// Loop through each repeater item.
		foreach ( $repeater_data as $index => $field_item ) {
			if ( ! is_array( $field_item ) ) {
				continue;
			}

			// Create incremental label (Repeater 1, Repeater 2, etc.).
			$repeater_number = $index + 1;
			$current_label   = sprintf( '%s %d', esc_html( $base_label ), $repeater_number );

			$repeater_item = [];

			// Loop through each field in this repeater instance.
			foreach ( $field_item as $field_key => $field_value ) {
				// Skip empty values.
				if ( empty( $field_value ) ) {
					continue;
				}

				$label = $this->process_the_label( $field_key );

				// Add field data to repeater item.
				if ( ! empty( $label ) ) {
					$repeater_item[ esc_html( $label ) ] = esc_html( $field_value );
				}
			}

			// Add the repeater item to webhook data if it has content.
			if ( ! empty( $repeater_item ) ) {
				$webhook_data[ $current_label ] = $repeater_item;
			}
		}

		return $webhook_data;
	}

	/**
	 * Generate HTML table for repeater data.
	 *
	 * Creates an HTML table representation of repeater field data for use
	 * in smart tags, emails, and other contexts.
	 *
	 * @param array  $repeater_data The repeater field data.
	 * @param string $base_label Base label for the repeater field.
	 *
	 * @since 1.11.0
	 * @return string HTML table representation of repeater data.
	 */
	private function generate_repeater_table_html( $repeater_data, $base_label = '' ) {
		if ( empty( $repeater_data ) || ! is_array( $repeater_data ) ) {
			return '';
		}

		ob_start();
		?>
		<table class="srfm-repeater-table" cellpadding="0" cellspacing="0" style="border: 1px solid #dce0e6;border-radius: 6px;margin-top: 25px;margin-bottom: 25px;width: 100%;overflow: hidden;">
			<style>
				.srfm-repeater-table tr:last-child td {
					border: none !important;
				}
			</style>
			<?php echo $this->generate_field_row( $repeater_data, $base_label ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Ignored to render html ?>
		</table>
		<?php
		$output = ob_get_clean();
		return is_string( $output ) ? $this->remove_border_from_last_tr_td_table( $output ) : '';
	}

	/**
	 * Helper method to enqueue scripts.
	 *
	 * Handles script dependencies and enqueuing for repeater functionality.
	 *
	 * @param array $scripts Array of script configurations.
	 *
	 * @since 1.11.0
	 * @return void
	 */
	private function enqueue_scripts( $scripts ) {
		foreach ( $scripts as $script ) {
			$script_dep_path = SRFM_PRO_DIR . 'dist/package/business/' . $script['unique_file'] . '.asset.php';
			$script_dep_data = file_exists( $script_dep_path )
				? include $script_dep_path
				: [
					'dependencies' => [],
					'version'      => SRFM_PRO_VER,
				];

			// Ensure dependencies are arrays.
			$script_dep = array_merge(
				is_array( $script_dep_data['dependencies'] ) ? $script_dep_data['dependencies'] : [],
				is_array( $script['extra_dependencies'] ) ? $script['extra_dependencies'] : []
			);

			// Enqueue script.
			wp_enqueue_script(
				SRFM_PRO_SLUG . '-' . $script['unique_handle'], // Handle.
				SRFM_PRO_URL . 'dist/package/business/' . $script['unique_file'] . '.js',
				$script_dep, // Dependencies.
				$script_dep_data['version'], // Version.
				true // Enqueue the script in the footer.
			);

			// Register script translations.
			Pro_Helper::register_script_translations( SRFM_PRO_SLUG . '-' . $script['unique_handle'] );
		}
	}

	/**
	 * Check if field is a repeater field.
	 *
	 * @param string $field_key   Field key.
	 *
	 * @since 2.3.0
	 * @return bool True if repeater field, false otherwise.
	 */
	private function is_repeater_field( $field_key ) {
		// Check if key indicates repeater field.
		if ( ! empty( $field_key ) && is_string( $field_key ) && strpos( $field_key, 'srfm-repeater-' ) === 0 ) {
			return true;
		}

		return false;
	}

	/**
	 * Format repeater field data for CSV export.
	 *
	 * Formats repeater field data into a readable string for CSV export.
	 * Each repeater item is numbered and field values are displayed with their labels.
	 *
	 * Example output: "Item 1: Name: John, Email: john@example.com | Item 2: Name: Jane, Email: jane@example.com"
	 *
	 * @param array<int, array<string, mixed>> $field_value Repeater field value (nested array).
	 *
	 * @since 2.3.0
	 * @return string Formatted repeater data.
	 */
	private function format_repeater_data_for_csv( $field_value ) {
		if ( empty( $field_value ) || ! is_array( $field_value ) ) {
			return '';
		}

		$repeater_items = [];

		// Process each repeater item.
		foreach ( $field_value as $index => $item ) {
			if ( ! is_array( $item ) ) {
				continue;
			}

			$item_fields = [];
			$item_number = $index + 1;

			// Process each field in the repeater item.
			foreach ( $item as $item_key => $item_value ) {
				// Skip empty values.
				if ( empty( $item_value ) && '0' !== $item_value ) {
					continue;
				}

				// Extract field label from the item key.
				$field_label = $this->process_the_label( $item_key );

				// Format the field value.
				$formatted_value = is_array( $item_value )
					? implode( ', ', array_map( 'sanitize_text_field', $item_value ) )
					: sanitize_text_field( Helper::get_string_value( $item_value ) );

				// Add to item fields.
				if ( ! empty( $field_label ) ) {
					$item_fields[] = sprintf( '%s: %s', $field_label, $formatted_value );
				} else {
					$item_fields[] = $formatted_value;
				}
			}

			// Add item to repeater items if it has fields.
			if ( ! empty( $item_fields ) ) {
				$repeater_items[] = sprintf( 'Item %d: %s', $item_number, implode( ', ', $item_fields ) );
			}
		}

		// Join all repeater items with separator.
		return implode( ' | ', $repeater_items );
	}
}
