<?php
/**
 * Workflow Processor Service
 *
 * Handles form workflow processing for native integrations.
 *
 * @package SureForms
 * @since 1.13.0
 */

namespace SRFM_Pro\Inc\Pro\Native_Integrations\Services;

use SRFM\Inc\Database\Tables\Entries;
use SRFM\Inc\Helper;
use SRFM_Pro\Inc\Helper as Pro_Helper;
use SRFM_Pro\Inc\Pro\Database\Tables\Integrations;
use SRFM_Pro\Inc\Pro\Native_Integrations\Factories\Transformer_Factory;
use SRFM_Pro\Inc\Pro\Native_Integrations\OAuth_Handler;
use SRFM_Pro\Inc\Pro\Native_Integrations\Provider_Factory;
use SRFM_Pro\Inc\Pro\Native_Integrations\Utils\Integration_Utils;

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

/**
 * Workflow Processor Service class.
 *
 * @since 1.13.0
 */
class Workflow_Processor {
	/**
	 * Original form data context for enhanced transformations
	 *
	 * @var array
	 * @since 1.13.0
	 */
	private $original_form_data_context = [];
	/**
	 * Config Manager instance
	 *
	 * @var Config_Manager
	 */
	private $config_manager;

	/**
	 * OAuth Handler instance
	 *
	 * @var OAuth_Handler
	 * @since 2.1.0
	 */
	private $oauth_handler;

	/**
	 * Constructor
	 *
	 * @param Config_Manager $config_manager Config manager instance.
	 * @param OAuth_Handler  $oauth_handler OAuth handler instance.
	 * @since 1.13.0
	 */
	public function __construct( Config_Manager $config_manager, OAuth_Handler $oauth_handler ) {
		$this->config_manager = $config_manager;
		$this->oauth_handler  = $oauth_handler;
		add_action( 'srfm_after_submission_process', [ $this, 'process_form_workflows' ] );
	}

	/**
	 * Process form workflows
	 *
	 * @param array $form_data Form submission data.
	 * @return void
	 * @since 1.13.0
	 */
	public function process_form_workflows( $form_data ) {
		$form_id = $form_data['form_id'] ?? 0;

		// Get native integrations meta for this form.
		$workflows_json = get_post_meta( $form_id, '_srfm_native_integrations', true );
		$workflows      = [];

		if ( ! empty( $workflows_json ) && is_string( $workflows_json ) ) {
			$decoded   = json_decode( $workflows_json, true );
			$workflows = is_array( $decoded ) ? $decoded : [];
		}

		if ( empty( $workflows ) ) {
			return; // No workflows configured.
		}

		// Prepare submission data for processing.
		$filtered_form_data = array_filter(
			$form_data,
			static function ( $key ) {
				return ! in_array( $key, [ 'form_id', 'submission_id' ], true );
			},
			ARRAY_FILTER_USE_KEY
		);

		$workflow_logs = [];

		// Process each workflow.
		foreach ( $workflows as $workflow ) {
			if ( ! $this->is_workflow_enabled( $workflow ) ) {
				continue; // Skip disabled workflows.
			}

			$result          = $this->process_single_workflow( $workflow, $filtered_form_data, $form_id );
			$workflow_logs[] = $result;
		}

		// Save workflow results to database or log.
		if ( ! empty( $workflow_logs ) ) {
			$this->save_workflow_results( $form_data['submission_id'] ?? 0, $workflow_logs );
		}
	}

	/**
	 * Test workflow configuration
	 *
	 * @param \WP_REST_Request $request The REST request.
	 * @return \WP_REST_Response
	 * @since 1.13.0
	 */
	public function test_workflow( $request ) {
		$integration = $request->get_param( 'integration' );
		$workflow    = $request->get_param( 'workflow' );

		if ( empty( $integration ) || empty( $workflow ) ) {
			return new \WP_REST_Response(
				[
					'success' => false,
					'message' => __( 'Integration and workflow configuration are required.', 'sureforms-pro' ),
				],
				400
			);
		}

		try {
			// Load integration configuration.
			$integration_config = $this->config_manager->load_integration_config( Helper::get_string_value( $integration ) );
			if ( ! $integration_config ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => __( 'Integration configuration not found.', 'sureforms-pro' ),
					],
					404
				);
			}

			// Find action configuration.
			$action        = is_array( $workflow ) && isset( $workflow['action'] ) ? Helper::get_string_value( $workflow['action'] ) : '';
			$action_config = $this->config_manager->find_action_config( $integration_config, $action );
			if ( ! $action_config ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => __( 'Action configuration not found.', 'sureforms-pro' ),
					],
					404
				);
			}

			// Get integration credentials from database.
			$integration_data = Integrations::get_by_type( sanitize_key( Helper::get_string_value( $integration ) ) );

			if ( empty( $integration_data ) || empty( $integration_data['status'] ) ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => __( 'Integration is not configured or enabled.', 'sureforms-pro' ),
					],
					400
				);
			}

			// Get decrypted credentials with OAuth validation.
			$integration_type = sanitize_key( Helper::get_string_value( $integration ) );
			$auth_config      = $integration_config['auth'] ?? [];
			$credentials      = Integration_Utils::get_stored_credentials( $integration_type, $auth_config, $this->oauth_handler );
			if ( is_wp_error( $credentials ) ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => sprintf(
							/* translators: %s: Error message */
							__( 'Failed to get credentials: %s', 'sureforms-pro' ),
							$credentials->get_error_message()
						),
					],
					400
				);
			}

			// Check if this is a WordPress plugin integration.
			$provider = $integration_config['provider'] ?? 'generic';

			// phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled -- Configuration uses lowercase.
			if ( 'wordpress' === strtolower( $provider ) ) {
				return $this->test_wordpress_plugin_workflow( $integration_config, $action_config, Helper::get_array_value( $workflow ) );
			}

			// Prepare the test payload for API integrations.
			$endpoint_config = $action_config['endpoint'] ?? [];

			if ( empty( $endpoint_config['url'] ) ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => __( 'No endpoint URL configured for this action.', 'sureforms-pro' ),
					],
					400
				);
			}

			$workflow_fields = is_array( $workflow ) && isset( $workflow['fields'] ) && is_array( $workflow['fields'] ) ? $workflow['fields'] : [];
			$action_fields   = isset( $action_config['fields'] ) && is_array( $action_config['fields'] ) ? $action_config['fields'] : [];

			// Prepare test data for all fields (both for payload and URL processing).
			$all_test_fields = $this->prepare_all_test_data( $workflow_fields, $action_fields, false );

			// Use generic test payload preparation.
			$payload = $this->prepare_test_payload( $all_test_fields, $action_fields, false );

			// Apply optional specific transformer enhancement for test payload (if available).
			$integration_name = $this->get_integration_name_from_action( $action_config );
			if ( $integration_name ) {
				$transformer = Transformer_Factory::create( $integration_name );
				if ( $transformer ) {
					$payload = $transformer->prepare_test_payload( $all_test_fields, $action_fields );
				}
			}

			// Prepare request args.
			$args = [
				'method'  => $endpoint_config['method'] ?? 'POST',
				'headers' => Integration_Utils::build_auth_headers(
					$credentials,
					$integration_config,
					[
						'default_headers' => [
							'User-Agent' => 'SureForms Pro/' . SRFM_PRO_VER,
						],
					]
				),
				'timeout' => 30,
			];

			// Add payload based on method and payload type.
			$payload_type = $endpoint_config['payload_type'] ?? 'json';
			if ( in_array( $args['method'], [ 'POST', 'PUT', 'PATCH' ], true ) ) {
				if ( 'json' === $payload_type ) {
					// Apply payload transformations if configured.
					if ( ! empty( $action_config['payload_transformation'] ) ) {
						$payload = $this->apply_payload_transformations( $payload, $action_config['payload_transformation'] );
					}
					$json_body                       = wp_json_encode( $payload );
					$args['body']                    = false !== $json_body ? $json_body : '{}';
					$args['headers']['Content-Type'] = 'application/json';
				} else {
					$args['body'] = is_array( $payload ) ? http_build_query( $payload ) : Helper::get_string_value( $payload );
				}
			}

			// Process URL template variables with enhanced processing using test data.
			$all_test_data = array_merge( $all_test_fields, $credentials );
			$url           = $this->process_url_with_enhanced_config(
				$endpoint_config['url'],
				$all_test_data,
				$endpoint_config['url_processing'] ?? []
			);

			// Make the test request.
			$response = wp_remote_request( $url, $args );

			if ( is_wp_error( $response ) ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => sprintf(
							/* translators: %s: Error message from the API response */
							__( 'Test failed: %s', 'sureforms-pro' ),
							$response->get_error_message()
						),
					],
					500
				);
			}

			$status_code   = wp_remote_retrieve_response_code( $response );
			$response_body = wp_remote_retrieve_body( $response );

			// Consider 2xx status codes as success.
			if ( $status_code >= 200 && $status_code < 300 ) {
				return new \WP_REST_Response(
					[
						'success' => true,
						'message' => sprintf(
							/* translators: %s: Workflow name */
							__( 'Workflow "%s" tested successfully! The integration is working correctly.', 'sureforms-pro' ),
							is_array( $workflow ) && isset( $workflow['name'] ) ? Helper::get_string_value( $workflow['name'] ) : __( 'Unnamed workflow', 'sureforms-pro' )
						),
						'details' => [
							'status_code' => $status_code,
							'response'    => $response_body,
						],
					],
					200
				);
			}

			// Parse error message from response.
			$error_data    = json_decode( $response_body, true );
			$error_message = '';
			if ( ! is_array( $error_data ) && ! empty( $response_body ) && is_string( $response_body ) ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => $response_body,
					],
					500
				);
			}

			if ( is_array( $error_data ) ) {
				$error_message = $error_data['message'] ?? $error_data['error'] ?? $error_data['detail'] ?? '';
			}

			if ( empty( $error_message ) ) {
				/* translators: %d: HTTP status code */
				$error_message = sprintf( __( 'HTTP %d error', 'sureforms-pro' ), $status_code );
			}

				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => sprintf(
							/* translators: %1$s: Error message, %2$d: HTTP status code */
							__( 'Test failed: %1$s (HTTP %2$d)', 'sureforms-pro' ),
							$error_message,
							$status_code
						),
						'details' => [
							'status_code' => $status_code,
							'response'    => $response_body,
						],
					],
					400
				);

		} catch ( \Exception $e ) {
			return new \WP_REST_Response(
				[
					'success' => false,
					'message' => sprintf(
						/* translators: %s: Exception error message */
						__( 'Test failed: %s', 'sureforms-pro' ),
						$e->getMessage()
					),
				],
				500
			);
		}
	}

	/**
	 * Get workflow logs for a specific entry
	 *
	 * @param int $entry_id Entry ID.
	 * @return array Array of workflow-related log entries.
	 * @since 1.13.0
	 */
	public static function get_entry_workflow_logs( $entry_id ) {
		if ( empty( $entry_id ) ) {
			return [];
		}

		$entry_data = Entries::get( $entry_id );
		if ( empty( $entry_data['logs'] ) ) {
			return [];
		}

		$logs          = is_array( $entry_data['logs'] ) ? $entry_data['logs'] : [];
		$workflow_logs = [];

		// Filter logs that are related to native integrations/workflows.
		foreach ( $logs as $log ) {
			if ( is_array( $log ) && isset( $log['title'] ) && is_string( $log['title'] ) && false !== strpos( $log['title'], 'Native Integrations' ) ) {
				$workflow_logs[] = $log;
			}
		}

		return $workflow_logs;
	}

	/**
	 * Get workflow execution summary for a specific entry
	 *
	 * @param int $entry_id Entry ID.
	 * @return array Summary of workflow execution status.
	 * @since 1.13.0
	 */
	public static function get_entry_workflow_summary( $entry_id ) {
		$workflow_logs = self::get_entry_workflow_logs( $entry_id );

		if ( empty( $workflow_logs ) ) {
			return [
				'has_workflows'         => false,
				'total_executions'      => 0,
				'successful_executions' => 0,
				'failed_executions'     => 0,
				'last_execution'        => null,
				'overall_status'        => 'none',
			];
		}

		$total_executions      = count( $workflow_logs );
		$successful_executions = 0;
		$failed_executions     = 0;
		$last_execution        = null;

		// Analyze the logs.
		foreach ( $workflow_logs as $log ) {
			// Check if this was a successful execution based on the title.
			if ( isset( $log['title'] ) && false !== strpos( $log['title'], 'successfully' ) ) {
				$successful_executions++;
			} else {
				$failed_executions++;
			}

			// Get the most recent execution (logs are ordered with newest first).
			if ( is_null( $last_execution ) && isset( $log['timestamp'] ) ) {
				$last_execution = $log['timestamp'];
			}
		}

		// Determine overall status.
		$overall_status = 'mixed';
		if ( 0 === $failed_executions ) {
			$overall_status = 'success';
		} elseif ( 0 === $successful_executions ) {
			$overall_status = 'failed';
		}

		return [
			'has_workflows'         => true,
			'total_executions'      => $total_executions,
			'successful_executions' => $successful_executions,
			'failed_executions'     => $failed_executions,
			'last_execution'        => $last_execution,
			'overall_status'        => $overall_status,
		];
	}

	/**
	 * Process template variables in a string
	 *
	 * @param string $template Template string with variables like {{variable}}.
	 * @param array  $fields Field values.
	 * @param array  $credentials Credential values.
	 * @return string Processed string.
	 * @since 1.13.0
	 */
	public static function process_template_variables( $template, $fields, $credentials = [] ) {
		// Ensure template is a string.
		$template = Helper::get_string_value( $template );

		// Merge fields and credentials for variable replacement.
		$variables = array_merge( $fields, $credentials );

		// Replace {{variable}} patterns.
		$result = preg_replace_callback(
			'/\{\{([^}]+)\}\}/',
			static function ( $matches ) use ( $variables ) {
				$key = trim( $matches[1] );

				// Handle special case for base64 encoding.
				if ( 0 === strpos( $key, 'base64(' ) && ')' === substr( $key, -1 ) ) {
					// Extract the expression inside base64().
					$expression = substr( $key, 7, -1 );

					// Handle username:password pattern.
					if ( false !== strpos( $expression, ':' ) ) {
						[ $user_key, $pass_key ] = explode( ':', $expression, 2 );
						$username                = $variables[ trim( $user_key ) ] ?? '';
						$password                = $variables[ trim( $pass_key ) ] ?? '';
						// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Used for HTTP Basic Auth encoding, not obfuscation.
						return base64_encode( $username . ':' . $password );
					}
				}

				// Default: return the variable value or keep the placeholder.
				return $variables[ $key ] ?? $matches[0];
			},
			$template
		);

		// Ensure we always return a string.
		return is_string( $result ) ? $result : $template;
	}

	/**
	 * Get nested value from array using dot notation path
	 *
	 * @param array  $data Array to search.
	 * @param string $path Dot notation path (e.g., 'merge_fields.BIRTHDAY').
	 * @return mixed Value at path or null if not found.
	 * @since 1.13.0
	 */
	public static function get_nested_value( $data, $path ) {
		$keys  = explode( '.', $path );
		$value = $data;

		foreach ( $keys as $key ) {
			// Handle array indices (e.g., "0", "1", etc.).
			if ( is_numeric( $key ) ) {
				$key = (int) $key;
			}

			if ( is_array( $value ) && isset( $value[ $key ] ) ) {
				$value = $value[ $key ];
			} else {
				return null;
			}
		}

		return $value;
	}

	/**
	 * Generate test value based on field definition
	 *
	 * @param array $field Field definition.
	 * @return mixed Test value.
	 * @since 1.13.0
	 */
	public static function generate_test_value( $field ) {
		$field_type = $field['type'] ?? 'text';
		$field_key  = $field['key'] ?? '';

		// Skip dropdown/select fields since they should only contain valid API options.
		if ( in_array( $field_type, [ 'select', 'dropdown', 'select-async' ], true ) ) {
			return null;
		}

		// Special handling for email fields.
		if ( false !== strpos( $field_key, 'email' ) || false !== strpos( $field_key, 'mail' ) ) {
			return 'noreply@sureforms.com';
		}

		// Special handling for name fields.
		if ( false !== strpos( $field_key, 'name' ) && 'name' === $field_key ) {
			return 'Test User';
		}
		if ( false !== strpos( $field_key, 'ipaddress' ) ) {
			return '192.168.1.1';
		}

		switch ( $field_type ) {
			case 'email':
				return 'noreply@sureforms.com';

			case 'number':
				return 123;

			case 'textarea':
				return 'This is a test workflow submission from SureForms.';

			case 'checkbox':
				return true;
			case 'date':
				return '1970-01-01';

			case 'text':
			default:
				// Return a contextual test value based on field key.
				if ( false !== strpos( $field_key, 'phone' ) ) {
					return '+1234567890';
				}
				if ( false !== strpos( $field_key, 'url' ) || false !== strpos( strtolower( $field_key ), 'website' ) ) {
					return 'https://sureforms.com';
				}
				if ( false !== strpos( $field_key, 'address' ) ) {
					return '123 Test Street';
				}
				if ( false !== strpos( $field_key, 'city' ) ) {
					return 'Test City';
				}
				if ( false !== strpos( $field_key, 'state' ) ) {
					return 'CA';
				}
				if ( false !== strpos( $field_key, 'zip' ) || false !== strpos( $field_key, 'postal' ) ) {
					return '12345';
				}
				if ( false !== strpos( $field_key, 'country' ) ) {
					return 'US';
				}

				return 'Test Value';
		}
	}

	/**
	 * Prepare test payload with sample data
	 *
	 * @param array $workflow_fields Fields from the workflow configuration.
	 * @param array $field_definitions Field definitions from the action configuration.
	 * @param bool  $is_wordpress_action Whether this is a WordPress plugin action.
	 * @return array Test payload.
	 * @since 1.13.0
	 */
	public static function prepare_test_payload( $workflow_fields, $field_definitions, $is_wordpress_action = false ) {
		$payload          = [];
		$processed_fields = [];
		// First, process fields defined in the action configuration.
		foreach ( $field_definitions as $field_def ) {
			$field_key = $field_def['key'] ?? '';

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

			// Check if field should be excluded from payload.
			$exclude_from_payload = $field_def['exclude_from_payload'] ?? false;

			if ( $exclude_from_payload ) {
				$processed_fields[ $field_key ] = true; // Mark as processed but don't include in payload.
				continue; // Skip excluded fields in generic test payload.
			}

			// Get field value (from workflow or generate test data).
			$field_value = null;
			if ( isset( $workflow_fields[ $field_key ] ) && ! empty( $workflow_fields[ $field_key ] ) ) {
				$field_value = $workflow_fields[ $field_key ];
			} else {
				// For WordPress actions, only generate test values for required fields.
				$is_required = ! empty( $field_def['required'] );
				if ( $is_wordpress_action && ! $is_required ) {
					continue; // Skip unconfigured optional fields for WordPress actions.
				}
				$field_value = self::generate_test_value( $field_def );
			}

			// Add to payload if not null.
			if ( null !== $field_value ) {
				$payload[ $field_key ] = $field_value;
			}

			$processed_fields[ $field_key ] = true; // Mark as processed.
		}

		// Second, process any additional workflow fields not defined in action config (e.g., dynamic fields).
		foreach ( $workflow_fields as $field_key => $field_value ) {
			// Skip if already processed or if value is empty.
			if ( isset( $processed_fields[ $field_key ] ) || empty( $field_value ) ) {
				continue;
			}

			// Add dynamic field to payload.
			$payload[ $field_key ] = $field_value;
		}

		return $payload;
	}

	/**
	 * Test WordPress plugin workflow configuration
	 *
	 * @param array $integration_config Integration configuration.
	 * @param array $action_config Action configuration.
	 * @param array $workflow Workflow configuration.
	 * @return \WP_REST_Response
	 * @since 1.13.0
	 */
	private function test_wordpress_plugin_workflow( $integration_config, $action_config, $workflow ) {
		try {
			// Create provider instance.
			$provider = Provider_Factory::create( $integration_config['integration']['name'] ?? '', $integration_config );

			if ( ! $provider ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => __( 'Failed to create provider instance for testing.', 'sureforms-pro' ),
					],
					500
				);
			}

			// Check if plugin is active first.
			if ( ! method_exists( $provider, 'is_plugin_active' ) || ! $provider->is_plugin_active() ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => sprintf(
							/* translators: %s: Plugin name */
							__( '%s plugin is not installed or not active.', 'sureforms-pro' ),
							$integration_config['integration']['name'] ?? 'Plugin'
						),
					],
					400
				);
			}

			// Prepare test data.
			$workflow_fields = is_array( $workflow ) && isset( $workflow['fields'] ) && is_array( $workflow['fields'] ) ? $workflow['fields'] : [];
			$action_fields   = isset( $action_config['fields'] ) && is_array( $action_config['fields'] ) ? $action_config['fields'] : [];

			// Generate comprehensive test data.
			$is_wordpress_action = ( $integration_config['provider'] ?? '' ) === 'wordpress'; // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled -- Configuration uses lowercase.
			$test_data           = $this->prepare_all_test_data( $workflow_fields, $action_fields, $is_wordpress_action );

			// Execute the action in test mode.
			$action_name = $action_config['name'] ?? '';

			// Add test mode flag to prevent real data creation.
			$test_data['_test_mode'] = true;

			if ( ! method_exists( $provider, 'execute_action' ) ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => __( 'Provider does not support action execution.', 'sureforms-pro' ),
					],
					500
				);
			}

			// Execute the action.
			$result = $provider->execute_action( $action_name, $test_data );

			if ( ! is_array( $result ) ) {
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => __( 'Invalid response from action execution.', 'sureforms-pro' ),
					],
					500
				);
			}

			$success = $result['success'] ?? false;
			$message = $result['message'] ?? __( 'No response message available.', 'sureforms-pro' );

			if ( $success ) {
				return new \WP_REST_Response(
					[
						'success' => true,
						'message' => sprintf(
							/* translators: %s: Workflow name */
							__( 'WordPress plugin workflow "%s" tested successfully! The integration is working correctly.', 'sureforms-pro' ),
							is_array( $workflow ) && isset( $workflow['name'] ) ? Helper::get_string_value( $workflow['name'] ) : __( 'Unnamed workflow', 'sureforms-pro' )
						),
						'details' => [
							'test_data' => $test_data,
							'result'    => $result,
						],
					],
					200
				);
			}
				return new \WP_REST_Response(
					[
						'success' => false,
						'message' => sprintf(
							/* translators: %s: Error message */
							__( 'WordPress plugin workflow test failed: %s', 'sureforms-pro' ),
							$message
						),
						'details' => [
							'test_data' => $test_data,
							'result'    => $result,
						],
					],
					400
				);

		} catch ( \Exception $e ) {
			return new \WP_REST_Response(
				[
					'success' => false,
					'message' => sprintf(
						/* translators: %s: Exception error message */
						__( 'WordPress plugin workflow test failed: %s', 'sureforms-pro' ),
						$e->getMessage()
					),
				],
				500
			);
		}
	}

	/**
	 * Check if workflow is enabled
	 *
	 * @param array $workflow Workflow configuration.
	 * @return bool True if enabled, false otherwise.
	 * @since 1.13.0
	 */
	private function is_workflow_enabled( $workflow ) {
		return isset( $workflow['status'] ) &&
			( 'enabled' === $workflow['status'] || true === $workflow['status'] );
	}

	/**
	 * Check if integration is enabled
	 *
	 * @param string $integration_type Integration type.
	 * @return bool True if enabled, false otherwise.
	 * @since 1.13.0
	 */
	private function is_integration_enabled( $integration_type ) {
		$integration = Integrations::get_by_type( $integration_type );

		if ( empty( $integration ) ) {
			return false;
		}

		return isset( $integration['status'] ) && '1' === $integration['status'];
	}

	/**
	 * Process a single workflow
	 *
	 * @param array $workflow Workflow configuration.
	 * @param array $submission_data Form submission data.
	 * @param int   $form_id Form ID.
	 * @return array Processing result.
	 * @throws \Exception When integration or action configuration is not found.
	 * @since 1.13.0
	 */
	private function process_single_workflow( $workflow, $submission_data, $form_id ) {
		$integration_type = $workflow['integration'];

		// Check if integration is enabled.
		if ( ! $this->is_integration_enabled( $integration_type ) ) {
			return [
				'workflow_name' => $workflow['name'] ?? 'Unnamed',
				'integration'   => $integration_type,
				'success'       => false,
				'message'       => __( 'Integration is disabled or not configured.', 'sureforms-pro' ),
			];
		}

		// Check conditional logic before processing workflow.
		if ( ! empty( $workflow['conditionalLogic'] ) && ! empty( $workflow['conditionalLogic']['status'] ) ) {
			// Map submission data to the format expected by conditional logic checker.
			$submission_mapped = Helper::map_slug_to_submission_data( $submission_data );

			// Evaluate conditional logic.
			if ( ! Pro_Helper::check_trigger_conditions( $workflow, $submission_mapped ) ) {
				return [
					'workflow_name' => $workflow['name'] ?? __( 'Unnamed', 'sureforms-pro' ),
					'integration'   => $integration_type,
					'success'       => false,
					'message'       => __( 'Workflow was not triggered - conditions not met.', 'sureforms-pro' ),
					'skipped'       => true,
				];
			}
		}

		try {
			// Load integration configuration.
			$integration_config = $this->config_manager->load_integration_config( $integration_type );
			if ( ! $integration_config ) {
				throw new \Exception( __( 'Integration configuration not found.', 'sureforms-pro' ) );
			}

			// Get action configuration.
			$action_config = $this->get_action_config( $integration_config, $workflow['action'] );
			if ( ! $action_config ) {
				throw new \Exception( __( 'Action configuration not found.', 'sureforms-pro' ) );
			}

			// Get integration credentials with OAuth validation.
			$auth_config = $integration_config['auth'] ?? [];
			$credentials = Integration_Utils::get_stored_credentials( $integration_type, $auth_config, $this->oauth_handler );
			if ( is_wp_error( $credentials ) ) {
				return [
					'success' => false,
					'message' => sprintf(
						/* translators: %s: Error message */
						__( 'Failed to get credentials: %s', 'sureforms-pro' ),
						$credentials->get_error_message()
					),
				];
			}

			// Process workflow fields (replace smart tags, etc.).
			$processed_fields = $this->process_workflow_fields( $workflow['fields'], $submission_data, $form_id );

			// Execute the workflow.
			$result = $this->execute_workflow( $integration_config, $action_config, $credentials, $processed_fields, $submission_data, $workflow );

			// Update run count if workflow executed successfully.
			if ( $result['success'] ) {
				$this->increment_workflow_run_count( $form_id, $workflow['id'] );
			}

			return [
				'workflow_name' => $workflow['name'] ?? 'Unnamed',
				'integration'   => $integration_type,
				'success'       => $result['success'],
				'message'       => $result['message'],
				'response'      => $result['response'] ?? null,
			];

		} catch ( \Exception $e ) {
			return [
				'workflow_name' => $workflow['name'] ?? 'Unnamed',
				'integration'   => $integration_type,
				'success'       => false,
				'message'       => $e->getMessage(),
			];
		}
	}

	/**
	 * Get action configuration from integration config
	 *
	 * @param array  $integration_config Integration configuration.
	 * @param string $action_value Action value to find.
	 * @return array|null Action configuration or null if not found.
	 * @since 1.13.0
	 */
	private function get_action_config( $integration_config, $action_value ) {
		if ( ! isset( $integration_config['actions'] ) || ! is_array( $integration_config['actions'] ) ) {
			return null;
		}

		foreach ( $integration_config['actions'] as $action ) {
			// Generate action value from name (same logic as API callback).
			$generated_action_value = sanitize_key( str_replace( ' ', '_', strtolower( $action['name'] ?? '' ) ) );

			if ( $action_value === $generated_action_value ) {
				return $action;
			}
		}

		return null;
	}

	/**
	 * Process workflow fields (replace smart tags, etc.)
	 *
	 * @param array $fields Workflow field mappings.
	 * @param array $submission_data Form submission data.
	 * @param int   $form_id Form ID.
	 * @return array Processed fields.
	 * @since 1.13.0
	 */
	private function process_workflow_fields( $fields, $submission_data, $form_id ) {
		$processed = [];

		foreach ( $fields as $key => $value ) {
			if ( ! is_string( $value ) ) {
				$processed[ $key ] = $value;
				continue;
			}

			// Process smart tags using SureForms Smart_Tags class.
			if ( class_exists( 'SRFM\Inc\Smart_Tags' ) && method_exists( 'SRFM\Inc\Smart_Tags', 'get_instance' ) ) {
				$smart_tags = \SRFM\Inc\Smart_Tags::get_instance();

				// Only provide form_data if the value contains form-specific tags.
				$form_data = null;
				if ( false !== strpos( $value, '{form_title}' ) ) {
					$form_data = [ 'form-id' => $form_id ];
				}

				// Use the process_smart_tags method which handles all smart tag types.
				$processed[ $key ] = $smart_tags->process_smart_tags( $value, $submission_data, $form_data );
			} else {
				// Fallback: return the value as-is if Smart_Tags is not available.
				$processed[ $key ] = $value;
			}
		}

		return $processed;
	}

	/**
	 * Execute a workflow (API call or WordPress plugin action)
	 *
	 * @param array $integration_config Integration configuration.
	 * @param array $action_config Action configuration.
	 * @param array $credentials Integration credentials.
	 * @param array $processed_fields Processed field data.
	 * @param array $original_submission_data Original form submission data.
	 * @param array $workflow Workflow configuration data.
	 * @return array Execution result.
	 * @since 1.13.0
	 */
	private function execute_workflow( $integration_config, $action_config, $credentials, $processed_fields, $original_submission_data = [], $workflow = [] ) {

		// Check if this is a WordPress plugin integration.
		$provider = $integration_config['provider'] ?? 'generic';

		// phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled -- Configuration uses lowercase.
		if ( 'wordpress' === strtolower( $provider ) ) {
			return Integration_Utils::execute_wordpress_plugin_action( $integration_config, $action_config, $processed_fields, $original_submission_data );
		}

		// For API integrations, require endpoint configuration.
		if ( empty( $action_config['endpoint'] ) ) {
			return [
				'success' => false,
				'message' => 'No endpoint configured for action',
			];
		}

		$endpoint_config = $action_config['endpoint'];

		// Extract endpoint properties.
		$url              = $endpoint_config['url'] ?? '';
		$method           = $endpoint_config['method'] ?? 'POST';
		$payload_type     = $endpoint_config['payload_type'] ?? 'json';
		$endpoint_headers = $endpoint_config['headers'] ?? [];

		// Process URL template variables with enhanced processing.
		$all_data = array_merge( $processed_fields, $credentials );
		$url      = $this->process_url_with_enhanced_config(
			$url,
			$all_data,
			$endpoint_config['url_processing'] ?? []
		);

		// Prepare request data.
		$request_data = $this->prepare_request_data( $action_config, $processed_fields, $original_submission_data, $credentials );

		// Apply payload transformations if configured.
		if ( ! empty( $action_config['payload_transformation'] ) ) {
			$request_data = $this->apply_payload_transformations( $request_data, $action_config['payload_transformation'] );
		}

		// Apply optional specific transformer enhancement (if available).
		$integration_name = $this->get_integration_name_from_action( $action_config );
		if ( $integration_name ) {
			$transformer = Transformer_Factory::create( $integration_name );
			if ( $transformer ) {
				$context      = [
					'original_form_data' => $original_submission_data,
					'action_name'        => $action_config['name'] ?? '',
					'workflow'           => $workflow,
					'integration_config' => $integration_config,
					'action_config'      => $action_config,
					'credentials'        => $credentials,
				];
				$request_data = $transformer->transform( $request_data, [], $context );
			}
		}

		// Prepare headers (merge auth headers with endpoint-specific headers).
		$headers = Integration_Utils::build_auth_headers(
			$credentials,
			$integration_config,
			[
				'default_headers'  => [
					'User-Agent' => 'SureForms Pro/' . SRFM_PRO_VER,
				],
				'endpoint_headers' => $endpoint_headers,
			]
		);

		// Make the API request.
		return $this->make_api_request( $url, $method, $request_data, $headers, $payload_type );
	}

	/**
	 * Prepare request data based on action configuration
	 *
	 * @param array $action_config Action configuration.
	 * @param array $processed_fields Processed field data.
	 * @param array $original_submission_data Original form submission data.
	 * @param array $credentials User credentials.
	 * @return array Request data.
	 * @since 1.13.0
	 */
	private function prepare_request_data( $action_config, $processed_fields, $original_submission_data = [], $credentials = [] ) {
		// Set original form data context for enhanced transformations.
		$this->set_original_form_data_context( $original_submission_data );

		// Use the processed fields as the base data.
		$request_data = $processed_fields;

		// Add any additional data from action config.
		if ( isset( $action_config['additional_data'] ) && is_array( $action_config['additional_data'] ) ) {
			// Process placeholders in additional_data using credentials.
			$processed_additional_data = [];
			foreach ( $action_config['additional_data'] as $key => $value ) {
				if ( is_string( $value ) ) {
					$processed_additional_data[ $key ] = Integration_Utils::replace_placeholders( $value, $credentials );
				} else {
					$processed_additional_data[ $key ] = $value;
				}
			}
			$request_data = array_merge( $request_data, $processed_additional_data );
		}

		// Handle field exclusion.
		$fields_config = $action_config['fields'] ?? [];
		foreach ( $fields_config as $field_config ) {
			$field_key = $field_config['key'] ?? '';
			$exclude   = $field_config['exclude_from_payload'] ?? false;

			if ( $exclude && isset( $request_data[ $field_key ] ) ) {
				unset( $request_data[ $field_key ] );
			}
		}

		return $request_data;
	}

	/**
	 * Get integration name from action config for optional transformer enhancement
	 *
	 * @param array $action_config Action configuration.
	 * @return string|null Integration name or null if not found.
	 * @since 1.13.0
	 */
	private function get_integration_name_from_action( $action_config ) {
		$provider = $action_config['provider'] ?? 'generic';

		// Return specific provider name if not generic, null for generic.
		return 'generic' !== $provider ? $provider : null;
	}

	/**
	 * Make API request
	 *
	 * @param string $endpoint API endpoint URL.
	 * @param string $method HTTP method.
	 * @param array  $data Request data.
	 * @param array  $headers Request headers.
	 * @param string $payload_type Payload type (json, form).
	 * @return array Response array with success, message, and response data.
	 * @since 1.13.0
	 */
	private function make_api_request( $endpoint, $method, $data, $headers, $payload_type = 'json' ) {
		// Prepare request arguments.
		$args = [
			'method'    => strtoupper( $method ),
			'headers'   => $headers,
			'timeout'   => 30,
			'sslverify' => true,
		];

		// Add body for POST/PUT/PATCH requests.
		if ( in_array( $args['method'], [ 'POST', 'PUT', 'PATCH' ], true ) ) {
			if ( 'json' === $payload_type ) {
				$json_body    = wp_json_encode( $data );
				$args['body'] = false !== $json_body ? $json_body : '{}';
			} else {
				$args['body'] = is_array( $data ) ? http_build_query( $data ) : Helper::get_string_value( $data );
			}
		}

		// Make the request.
		$response = wp_remote_request( $endpoint, $args );

		// Handle WordPress HTTP errors.
		if ( is_wp_error( $response ) ) {
			return [
				'success'  => false,
				'message'  => sprintf(
					/* translators: %s: HTTP error message */
					__( 'HTTP request failed: %s', 'sureforms-pro' ),
					$response->get_error_message()
				),
				'response' => null,
			];
		}

		$status_code   = wp_remote_retrieve_response_code( $response );
		$response_body = wp_remote_retrieve_body( $response );

		// Consider 2xx status codes as success.
		if ( $status_code >= 200 && $status_code < 300 ) {
			return [
				'success'  => true,
				'message'  => __( 'Request completed successfully.', 'sureforms-pro' ),
				'response' => $response_body,
			];
		}
			// Parse error message from response.
			$error_data    = json_decode( $response_body, true );
			$error_message = '';

		if ( is_array( $error_data ) ) {
			$error_message = $error_data['message'] ?? $error_data['error'] ?? $error_data['detail'] ?? '';
		}

		if ( empty( $error_message ) ) {
			/* translators: %d: HTTP status code */
			$error_message = sprintf( __( 'HTTP %d error', 'sureforms-pro' ), $status_code );
		}

			return [
				'success'  => false,
				'message'  => $error_message,
				'response' => $response_body,
			];
	}

	/**
	 * Prepare all test data by converting smart tags to test values
	 *
	 * @param array $workflow_fields Fields from workflow configuration.
	 * @param array $field_definitions Field definitions from action config.
	 * @param bool  $is_wordpress_action Whether this is a WordPress plugin action.
	 * @return array Test data with smart tags replaced.
	 * @since 1.13.0
	 */
	private function prepare_all_test_data( $workflow_fields, $field_definitions, $is_wordpress_action = false ) {
		$test_data = [];

		// First, convert any smart tags in workflow fields to test data.
		foreach ( $workflow_fields as $key => $value ) {
			if ( is_string( $value ) && $this->is_smart_tag( $value ) ) {
				// Find matching field definition to get proper test data.
				$field_def = null;
				foreach ( $field_definitions as $def ) {
					if ( isset( $def['key'] ) && $def['key'] === $key ) {
						$field_def = $def;
						break;
					}
				}

				if ( $field_def ) {
					$test_data[ $key ] = self::generate_test_value( $field_def );
				} else {
					// Generate contextual test data based on field key.
					$test_data[ $key ] = $this->generate_test_value_by_key( $key );
				}
			} else {
				$test_data[ $key ] = $value;
			}
		}

		// Also generate test data for any fields that might be referenced in URLs but not in workflow.
		foreach ( $field_definitions as $field_def ) {
			$field_key   = $field_def['key'] ?? '';
			$field_type  = $field_def['type'] ?? 'text';
			$is_required = ! empty( $field_def['required'] );

			// Skip dropdown/select fields since they should only contain valid API options.
			if ( in_array( $field_type, [ 'select', 'dropdown', 'select-async', 'multi-select-async' ], true ) ) {
				continue;
			}

			// For WordPress actions, only generate test data for required fields that aren't already set.
			if ( $is_wordpress_action && ! $is_required && ! isset( $test_data[ $field_key ] ) ) {
				continue;
			}

			if ( ! empty( $field_key ) && ! isset( $test_data[ $field_key ] ) ) {
				$test_data[ $field_key ] = self::generate_test_value( $field_def );
			}
		}

		return $test_data;
	}

	/**
	 * Check if a value is a smart tag
	 *
	 * @param string $value Value to check.
	 * @return bool True if it's a smart tag.
	 * @since 1.13.0
	 */
	private function is_smart_tag( $value ) {
		return is_string( $value ) && (
			preg_match( '/^\{[^}]+\}$/', $value ) || // field_id.
			preg_match( '/^\{[^}]+_[^}]+\}$/', $value ) || // field_id_value.
			strpos( $value, '{' ) === 0 // Any other smart tag format.
		);
	}

	/**
	 * Generate test value based on field key when no field definition is available
	 *
	 * @param string $field_key Field key.
	 * @return mixed Test value.
	 * @since 1.13.0
	 */
	private function generate_test_value_by_key( $field_key ) {
		$field_key = strtolower( $field_key );
		// Special handling for common field keys.
		if ( false !== strpos( $field_key, 'email' ) || false !== strpos( $field_key, 'mail' ) ) {
			return 'test@example.com';
		}
		if ( false !== strpos( $field_key, 'name' ) ) {
			return 'Test User';
		}
		if ( false !== strpos( $field_key, 'phone' ) ) {
			return '+1234567890';
		}
		if ( false !== strpos( $field_key, 'address' ) ) {
			return '123 Test Street';
		}
		if ( false !== strpos( $field_key, 'city' ) ) {
			return 'Test City';
		}
		if ( false !== strpos( $field_key, 'zip' ) || false !== strpos( $field_key, 'postal' ) ) {
			return '12345';
		}
		if ( false !== strpos( $field_key, 'date' ) ) {
			return gmdate( 'Y-m-d' );
		}
		if ( false !== strpos( $field_key, 'ipaddress' ) ) {
			return '192.168.1.1';
		}

		// Default test value.
		return 'Test Value';
	}

	/**
	 * Save workflow execution results to entry logs
	 *
	 * This method saves the results of workflow executions to the entry's logs table.
	 * Each workflow execution creates a log entry with:
	 * - Title: Overall success/failure status
	 * - Messages: Individual workflow results with status icons
	 * - Timestamp: When the workflows were executed
	 *
	 * The logs can be viewed in the admin interface and provide a complete audit trail
	 * of all integration attempts for each form submission.
	 *
	 * @param int   $submission_id Submission ID.
	 * @param array $workflow_logs Workflow execution logs.
	 * @return void
	 * @since 1.13.0
	 */
	private function save_workflow_results( $submission_id, $workflow_logs ) {
		if ( empty( $submission_id ) || empty( $workflow_logs ) ) {
			return;
		}

		$success = ! in_array(
			false,
			array_column( $workflow_logs, 'success' ),
			true
		);

		$entries_table = Entries::get_instance();

		// Prepare log messages for each workflow.
		$log_messages  = [];
		$success_count = 0;
		$total_count   = count( $workflow_logs );

		foreach ( $workflow_logs as $workflow_log ) {
			$workflow_name = $workflow_log['workflow_name'] ?? __( 'Unknown Workflow', 'sureforms-pro' );
			$is_success    = $workflow_log['success'] ?? false;
			$message       = $workflow_log['message'] ?? __( 'No details available', 'sureforms-pro' );
			$is_skipped    = $workflow_log['skipped'] ?? false;

			if ( $is_success ) {
				$success_count++;
			}

			// For skipped workflows, show only the message without status icon/text.
			if ( $is_skipped ) {
				$log_messages[] = sprintf(
					'%s: %s',
					$workflow_name,
					$message
				);
			} else {
				$status_icon = $is_success ? '✓' : '✗';
				$status_text = $is_success ? __( 'Success', 'sureforms-pro' ) : __( 'Failed', 'sureforms-pro' );

				$log_messages[] = sprintf(
					'%s %s: %s - %s',
					$status_icon,
					$workflow_name,
					$status_text,
					$message
				);
			}
		}

		// Add summary message.
		$summary_message = sprintf(
			/* translators: %1$d: Number of successful workflows, %2$d: Total number of workflows */
			__( 'Summary: %1$d of %2$d workflows executed successfully', 'sureforms-pro' ),
			$success_count,
			$total_count
		);
		array_unshift( $log_messages, $summary_message );

		// Create the main log title.
		$log_title = $success
			? __( 'Native Integrations - All workflows executed successfully', 'sureforms-pro' )
			: __( 'Native Integrations - Some workflows failed', 'sureforms-pro' );

		// Add the log entry to the submission.
		$entries_table->add_log( $log_title, $log_messages );

		// Get current entry data.
		$entry_data = Entries::get( $submission_id );
		if ( empty( $entry_data ) ) {
			return;
		}

		// Update the entry with the new logs.
		$updated_logs = $entries_table->get_logs();
		Entries::update( $submission_id, [ 'logs' => $updated_logs ] );

		// Reset the logs cache after saving.
		$entries_table->reset_logs();
	}

	/**
	 * Enhanced URL processing with configuration support
	 *
	 * @param string $url URL template.
	 * @param array  $data Data for replacements.
	 * @param array  $url_processing URL processing configuration.
	 * @return string Processed URL.
	 * @since 1.13.0
	 */
	private function process_url_with_enhanced_config( $url, $data, $url_processing ) {
		// Start with enhanced template variable processing.
		$processed_url = Integration_Utils::replace_placeholders( $url, $data );

		// Apply datacenter extraction if configured.
		if ( ! empty( $url_processing['datacenter_extraction']['enabled'] ) ) {
			$processed_url = $this->apply_datacenter_extraction( $processed_url, $data, $url_processing['datacenter_extraction'] );
		}

		// Apply hash generation if configured.
		if ( ! empty( $url_processing['hash_generation']['enabled'] ) ) {
			$processed_url = $this->apply_hash_generation( $processed_url, $data, $url_processing['hash_generation'] );
		}

		// Apply field lookup if configured.
		if ( ! empty( $url_processing['field_lookup']['enabled'] ) ) {
			$processed_url = Compatibility_Utils::apply_field_lookup( $processed_url, $data, $url_processing['field_lookup'] );
		}

		return $processed_url;
	}

	/**
	 * Apply datacenter extraction from API key
	 *
	 * @param string $url URL template.
	 * @param array  $data Data containing source field.
	 * @param array  $config Datacenter extraction configuration.
	 * @return string URL with datacenter replaced.
	 * @since 1.13.0
	 */
	private function apply_datacenter_extraction( $url, $data, $config ) {
		$source_field      = $config['source_field'] ?? '';
		$pattern           = $config['pattern'] ?? '';
		$replacement_token = $config['replacement_token'] ?? '';

		if ( empty( $source_field ) || empty( $pattern ) || empty( $replacement_token ) ) {
			return $url;
		}

		$source_value = $data[ $source_field ] ?? '';
		if ( empty( $source_value ) ) {
			return $url;
		}

		// Extract datacenter using regex pattern.
		if ( preg_match( '/' . $pattern . '/', $source_value, $matches ) ) {
			$datacenter = $matches[1] ?? '';
			if ( ! empty( $datacenter ) ) {
				$url = str_replace( $replacement_token, $datacenter, $url );
			}
		}

		return $url;
	}

	/**
	 * Apply hash generation for URLs
	 *
	 * @param string $url URL template.
	 * @param array  $data Data containing source field.
	 * @param array  $config Hash generation configuration.
	 * @return string URL with hash replaced.
	 * @since 1.13.0
	 */
	private function apply_hash_generation( $url, $data, $config ) {
		$source_field      = $config['source_field'] ?? '';
		$hash_type         = $config['hash_type'] ?? 'md5';
		$replacement_token = $config['replacement_token'] ?? '';

		if ( empty( $source_field ) || empty( $replacement_token ) ) {
			return $url;
		}

		$source_value = $data[ $source_field ] ?? '';
		if ( empty( $source_value ) ) {
			return $url;
		}

		// Generate hash.
		$hash = '';
		switch ( $hash_type ) {
			case 'md5':
				$hash = md5( strtolower( trim( $source_value ) ) );
				break;
			case 'sha1':
				$hash = sha1( strtolower( trim( $source_value ) ) );
				break;
			case 'sha256':
				$hash = hash( 'sha256', strtolower( trim( $source_value ) ) );
				break;
		}

		if ( ! empty( $hash ) ) {
			$url = str_replace( $replacement_token, $hash, $url );
		}

		return $url;
	}

	/**
	 * Apply payload transformations based on JSON configuration
	 *
	 * @param array $payload Original payload data.
	 * @param array $transformation_config Transformation configuration.
	 * @return array Transformed payload.
	 * @since 1.13.0
	 */
	private function apply_payload_transformations( $payload, $transformation_config ) {
		if ( empty( $transformation_config['enabled'] ) || empty( $transformation_config['transformations'] ) ) {
			return $payload;
		}

		foreach ( $transformation_config['transformations'] as $transformation ) {
			$type = $transformation['type'] ?? '';

			switch ( $type ) {
				case 'array_conversion':
					$payload = $this->transform_array_conversion( $payload, $transformation );
					break;

				case 'nested_object':
					$payload = $this->transform_nested_object( $payload, $transformation );
					break;

				case 'tag_array_conversion':
					$payload = $this->transform_tag_array_conversion( $payload, $transformation );
					break;

				case 'conditional_field':
					$payload = $this->transform_conditional_field( $payload, $transformation );
					break;

				case 'field_format':
					$payload = $this->transform_field_format( $payload, $transformation );
					break;

				case 'structure_conversion':
					$payload = $this->transform_structure_conversion( $payload, $transformation );
					break;
				case 'extract_url':
					$payload = $this->transform_extract_url( $payload, $transformation );
					break;
				case 'dynamic_field_conversion':
					$payload = Compatibility_Utils::transform_dynamic_field_conversion( $payload, $transformation );
					break;

				case 'wrap_in_array':
					$payload = $this->transform_wrap_in_array( $payload, $transformation );
					break;
			}
		}

		return $payload;
	}

	/**
	 * Transform comma-separated values to arrays
	 *
	 * @param array $payload Payload data.
	 * @param array $config Transformation configuration.
	 * @return array Transformed payload.
	 * @since 1.13.0
	 */
	private function transform_array_conversion( $payload, $config ) {
		$field     = $config['field'] ?? '';
		$target    = $config['target'] ?? $field; // Use target if specified, otherwise use source field.
		$delimiter = $config['delimiter'] ?? ',';

		if ( empty( $field ) || ! isset( $payload[ $field ] ) ) {
			return $payload;
		}

		$value           = $payload[ $field ];
		$converted_array = [];

		if ( is_string( $value ) && ! empty( $value ) ) {
			// Handle string values that need to be split by delimiter.
			$converted_array = array_map( 'trim', explode( $delimiter, $value ) );
		} elseif ( ! is_array( $value ) && ! empty( $value ) ) {
			// Handle single values (integer/string) that need to be wrapped in an array.
			$converted_array = [ is_numeric( $value ) ? (int) $value : $value ];
		}

		if ( ! empty( $converted_array ) ) {
			// If target is different from source field, create new field and remove old one.
			if ( $target !== $field ) {
				$payload[ $target ] = $converted_array;
				unset( $payload[ $field ] );
			} else {
				$payload[ $field ] = $converted_array;
			}
		}

		return $payload;
	}

	/**
	 * Transform fields into nested objects
	 *
	 * @param array $payload Payload data.
	 * @param array $config Transformation configuration.
	 * @return array Transformed payload.
	 * @since 1.13.0
	 */
	private function transform_nested_object( $payload, $config ) {
		$target         = $config['target'] ?? '';
		$source_pattern = $config['source_pattern'] ?? '';
		$exclude_fields = $config['exclude_fields'] ?? [];
		$exclude_empty  = $config['exclude_empty'] ?? false; // Default to false for backward compatibility.
		$field_mapping  = $config['field_mapping'] ?? [];

		if ( empty( $target ) || empty( $source_pattern ) ) {
			return $payload;
		}

		$nested_data = [];

		// Handle field_mapping if provided - this creates a specific nested structure.
		if ( ! empty( $field_mapping ) ) {
			foreach ( $field_mapping as $nested_key => $template ) {
				// Replace template variables with actual payload values.
				$processed_value            = Integration_Utils::replace_placeholders( $template, $payload );
				$nested_data[ $nested_key ] = $processed_value;
			}

			// Remove the source fields that were used in the mapping.
			foreach ( $payload as $key => $value ) {
				if ( fnmatch( $source_pattern, $key ) ) {
					unset( $payload[ $key ] );
				}
			}
		} else {
			// Original behavior - find fields matching the pattern and move them.
			foreach ( $payload as $key => $value ) {
				// Skip excluded fields.
				if ( in_array( $key, $exclude_fields, true ) ) {
					continue;
				}

				// Skip empty fields if exclude_empty is enabled.
				if ( $exclude_empty && Integration_Utils::is_empty_field_value( $value ) ) {
					unset( $payload[ $key ] );
					continue;
				}

				if ( fnmatch( $source_pattern, $key ) ) {
					if ( '*' === $source_pattern ) {
						// Move all non-excluded fields as-is.
						$nested_data[ $key ] = $value;
					} else {
						// Remove pattern prefix for specific patterns like MERGE*.
						$nested_key                 = str_replace( rtrim( $source_pattern, '*' ), '', $key );
						$nested_data[ $nested_key ] = $value;
					}
					unset( $payload[ $key ] );
				}
			}
		}

		if ( ! empty( $nested_data ) ) {
			$payload[ $target ] = $nested_data;
		}

		return $payload;
	}

	/**
	 * Transform tags to integration-specific tag array format
	 *
	 * @param array $payload Payload data.
	 * @param array $config Transformation configuration.
	 * @return array Transformed payload.
	 * @since 1.13.0
	 */
	private function transform_tag_array_conversion( $payload, $config ) {
		$source_field     = $config['source_field'] ?? '';
		$target_structure = $config['target_structure'] ?? '';
		$tag_format       = $config['tag_format'] ?? [];

		if ( empty( $source_field ) || empty( $target_structure ) || ! isset( $payload[ $source_field ] ) ) {
			return $payload;
		}

		$tags_value = $payload[ $source_field ];
		$tags_array = [];

		if ( is_string( $tags_value ) && ! empty( $tags_value ) ) {
			$tag_names = array_map( 'trim', explode( ',', $tags_value ) );

			foreach ( $tag_names as $tag_name ) {
				if ( ! empty( $tag_name ) ) {
					$tag_object = [];
					foreach ( $tag_format as $key => $value ) {
						$tag_object[ $key ] = str_replace( '{{tag_name}}', $tag_name, $value );
					}
					$tags_array[] = $tag_object;
				}
			}
		}

		if ( ! empty( $tags_array ) ) {
			$payload[ $target_structure ] = $tags_array;
		}

		// Remove the original field.
		unset( $payload[ $source_field ] );

		return $payload;
	}

	/**
	 * Add conditional fields based on other field values
	 *
	 * @param array $payload Payload data.
	 * @param array $config Transformation configuration.
	 * @return array Transformed payload.
	 * @since 1.13.0
	 */
	private function transform_conditional_field( $payload, $config ) {
		$field     = $config['field'] ?? '';
		$condition = $config['condition'] ?? '';

		if ( empty( $field ) || empty( $condition ) ) {
			return $payload;
		}

		// Only add the field if the condition field has a value.
		if ( isset( $payload[ $condition ] ) && ! empty( $payload[ $condition ] ) ) {
			// For now, just add an empty object. This can be enhanced based on specific needs.
			$payload[ $field ] = [];
		}

		return $payload;
	}

	/**
	 * Wrap a field's value or the entire payload in an array
	 *
	 * @param array $payload Payload data.
	 * @param array $config Transformation configuration.
	 * @return array Transformed payload.
	 * @since 1.13.0
	 */
	private function transform_wrap_in_array( $payload, $config ) {
		$target_field = $config['target_field'] ?? '';
		$source_field = $config['source_field'] ?? '';

		// If target_field is specified, wrap that specific field's value in an array.
		if ( ! empty( $target_field ) ) {
			if ( isset( $payload[ $target_field ] ) && ! is_array( $payload[ $target_field ] ) ) {
				$payload[ $target_field ] = [ $payload[ $target_field ] ];
			} elseif ( isset( $payload[ $target_field ] ) && is_array( $payload[ $target_field ] ) ) {
				// Check if it's already an indexed array, if not wrap it.
				if ( ! isset( $payload[ $target_field ][0] ) ) {
					$payload[ $target_field ] = [ $payload[ $target_field ] ];
				}
			}
		} elseif ( ! empty( $source_field ) && ! empty( $config['target_field_name'] ) ) {
			// If source_field is specified, take that field's value and wrap it in target_field array.
			$target_field_name = $config['target_field_name'];
			if ( isset( $payload[ $source_field ] ) ) {
				$value = $payload[ $source_field ];
				// If value is an array and it's associative (not indexed), wrap it in an array.
				if ( is_array( $value ) ) {
					$payload[ $target_field_name ] = isset( $value[0] ) ? $value : [ $value ];
				} else {
					$payload[ $target_field_name ] = [ $value ];
				}
				unset( $payload[ $source_field ] );
			}
		} else {
			// Otherwise, wrap the entire payload in an array (for cases where the API expects an array of objects).
			$payload = [ $payload ];
		}

		return $payload;
	}

	/**
	 * Generic field format transformation based on JSON rules
	 *
	 * @param array $payload Payload data.
	 * @param array $config Transformation configuration.
	 * @return array Transformed payload.
	 * @since 1.13.0
	 */
	private function transform_field_format( $payload, $config ) {
		$field_rules = $config['field_rules'] ?? [];

		foreach ( $field_rules as $rule ) {
			$fields      = $rule['fields'] ?? [];
			$format_type = $rule['format_type'] ?? '';

			foreach ( $fields as $field_pattern ) {
				$matching_fields = $this->find_fields_by_pattern( $payload, $field_pattern );

				foreach ( $matching_fields as $field_path ) {
					$value = Integration_Utils::get_nested_value( $payload, $field_path );

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

					if ( 'date' === $format_type ) {
						$processed_value = $this->apply_date_format_rule( $value, $rule );
					} elseif ( 'string' === $format_type ) {
						$processed_value = $this->apply_string_format_rule( $value, $rule );
					} else {
						$processed_value = $value;
					}

					$payload = Integration_Utils::set_nested_value( $payload, $field_path, $processed_value );
				}
			}
		}

		return $payload;
	}

	/**
	 * Generic structure conversion transformation based on JSON rules
	 *
	 * @param array $payload Payload data.
	 * @param array $config Transformation configuration.
	 * @return array Transformed payload.
	 * @since 1.13.0
	 */
	private function transform_structure_conversion( $payload, $config ) {
		$conversions = $config['conversions'] ?? [];

		foreach ( $conversions as $conversion ) {
			$field_patterns          = $conversion['fields'] ?? [];
			$target_structure        = $conversion['target_structure'] ?? [];
			$auto_detect             = $conversion['auto_detect'] ?? [];
			$use_original_form_data  = $conversion['use_original_form_data'] ?? false;
			$fallback_to_field_value = $conversion['fallback_to_field_value'] ?? false;

			foreach ( $field_patterns as $field_pattern ) {
				$matching_fields = $this->find_fields_by_pattern( $payload, $field_pattern );

				foreach ( $matching_fields as $field_path ) {
					$value = Integration_Utils::get_nested_value( $payload, $field_path );

					if ( null === $value ) {
						continue;
					}

					$converted_value = $this->apply_enhanced_structure_conversion_rule(
						$value,
						$target_structure,
						$auto_detect,
						$payload,
						$use_original_form_data,
						$fallback_to_field_value
					);
					$payload         = Integration_Utils::set_nested_value( $payload, $field_path, $converted_value );
				}
			}
		}

		return $payload;
	}

	/**
	 * Extract URL from processed smart tag containing anchor tag
	 *
	 * @param array $payload Payload data.
	 * @param array $config Transformation configuration.
	 * @return array Transformed payload.
	 * @since 1.13.0
	 */
	private function transform_extract_url( $payload, $config ) {
		$field  = $config['field'] ?? '';
		$target = $config['target'] ?? $field; // Use target if specified, otherwise use source field.

		if ( empty( $field ) || ! isset( $payload[ $field ] ) ) {
			return $payload;
		}

		$value = $payload[ $field ];

		// Check if value is a string and contains an anchor tag.
		if ( is_string( $value ) && ! empty( $value ) ) {
			// Extract URL from anchor tag using regex.
			if ( preg_match( '/<a[^>]+href=[\'"](.*?)[\'"][^>]*>/', $value, $matches ) ) {
				$extracted_url = $matches[1];

				// If target is different from source field, create new field and optionally remove old one.
				if ( $target !== $field ) {
					$payload[ $target ] = $extracted_url;
					// Don't remove the original field in case it's needed elsewhere.
				} else {
					$payload[ $field ] = $extracted_url;
				}
			}
			// If no anchor tag found, keep the original value.
		}

		return $payload;
	}

	/**
	 * Find fields in payload matching a pattern (supports nested objects)
	 *
	 * @param array  $payload Payload data.
	 * @param string $pattern Field pattern (supports wildcards).
	 * @return array Array of matching field paths (e.g., ['field1', 'nested.field2']).
	 * @since 1.13.0
	 */
	private function find_fields_by_pattern( $payload, $pattern ) {
		return $this->find_fields_recursive( $payload, $pattern, '' );
	}

	/**
	 * Recursively find fields matching a pattern in nested structures
	 *
	 * @param array  $data Data to search.
	 * @param string $pattern Field pattern (supports wildcards).
	 * @param string $prefix Current nesting prefix.
	 * @return array Array of matching field paths.
	 * @since 1.13.0
	 */
	private function find_fields_recursive( $data, $pattern, $prefix = '' ) {
		$matching_fields = [];

		if ( ! is_array( $data ) ) {
			return $matching_fields;
		}

		// Convert pattern to regex if it contains wildcards.
		$use_regex     = false !== strpos( $pattern, '*' );
		$regex_pattern = '';
		if ( $use_regex ) {
			$regex_pattern = '/^' . str_replace( '*', '.*', preg_quote( $pattern, '/' ) ) . '$/i';
		}

		foreach ( $data as $key => $value ) {
			$field_path = empty( $prefix ) ? $key : $prefix . '.' . $key;

			// Check if current field matches the pattern.
			$matches = false;
			if ( $use_regex ) {
				$matches = preg_match( $regex_pattern, $key );
			} else {
				$matches = $key === $pattern;
			}

			if ( $matches ) {
				$matching_fields[] = $field_path;
			}

			// Recursively search nested arrays/objects.
			if ( is_array( $value ) ) {
				$nested_matches  = $this->find_fields_recursive( $value, $pattern, $field_path );
				$matching_fields = array_merge( $matching_fields, $nested_matches );
			}
		}

		return $matching_fields;
	}

	/**
	 * Apply date formatting rule from JSON configuration
	 *
	 * @param mixed $value Input value.
	 * @param array $rule Formatting rule.
	 * @return string Formatted value.
	 * @since 1.13.0
	 */
	private function apply_date_format_rule( $value, $rule ) {
		if ( empty( $value ) ) {
			return '';
		}

		$value_str        = Helper::get_string_value( $value );
		$input_formats    = $rule['input_formats'] ?? [];
		$output_format    = $rule['output_format'] ?? 'Y-m-d';
		$validation_regex = $rule['validation_regex'] ?? '';

		// Check if already in correct format.
		if ( ! empty( $validation_regex ) && preg_match( '/' . $validation_regex . '/', $value_str ) ) {
			return $value_str;
		}

		// Try to parse using defined input formats.
		foreach ( $input_formats as $format ) {
			$date = \DateTime::createFromFormat( $format, $value_str );
			if ( false !== $date ) {
				return $date->format( $output_format );
			}
		}

		// Try fallback regex if provided.
		$fallback_regex = $rule['fallback_regex'] ?? '';
		if ( ! empty( $fallback_regex ) && preg_match( '/' . $fallback_regex . '/', $value_str, $matches ) ) {
			$processed_value = $rule['fallback_replacement'] ?? '';
			foreach ( $matches as $i => $match ) {
				$processed_value = str_replace( "{{$i}}", $match, $processed_value );
			}
			return $processed_value;
		}

		// Return original value if no transformation possible.
		return $value_str;
	}

	/**
	 * Apply string formatting rule from JSON configuration
	 *
	 * @param mixed $value Input value.
	 * @param array $rule Formatting rule.
	 * @return string Formatted value.
	 * @since 1.13.0
	 */
	private function apply_string_format_rule( $value, $rule ) {
		$value_str       = Helper::get_string_value( $value );
		$transformations = $rule['transformations'] ?? [];

		foreach ( $transformations as $transformation ) {
			$type = $transformation['type'] ?? '';

			switch ( $type ) {
				case 'uppercase':
					$value_str = strtoupper( Helper::get_string_value( $value_str ) );
					break;
				case 'lowercase':
					$value_str = strtolower( Helper::get_string_value( $value_str ) );
					break;
				case 'trim':
					$value_str = trim( Helper::get_string_value( $value_str ) );
					break;
				case 'regex_replace':
					$pattern     = $transformation['pattern'] ?? '';
					$replacement = $transformation['replacement'] ?? '';
					if ( ! empty( $pattern ) ) {
						$value_str = preg_replace( '/' . $pattern . '/', $replacement, Helper::get_string_value( $value_str ) );
					}
					break;
			}
		}

		return Helper::get_string_value( $value_str );
	}

	/**
	 * Apply enhanced structure conversion rule from JSON configuration
	 *
	 * @param mixed $value Input value.
	 * @param array $target_structure Target structure template.
	 * @param array $auto_detect Auto-detection rules.
	 * @param array $payload Full payload for context.
	 * @param bool  $use_original_form_data Whether to use original form data for detection.
	 * @param bool  $fallback_to_field_value Whether to fallback to field value if detection fails.
	 * @return array Converted structure.
	 * @since 1.13.0
	 */
	private function apply_enhanced_structure_conversion_rule( $value, $target_structure, $auto_detect, $payload, $use_original_form_data = false, $fallback_to_field_value = false ) {
		// If already an array, return as-is.
		if ( is_array( $value ) ) {
			return $value;
		}

		$result    = [];
		$value_str = Helper::get_string_value( $value );

		// Get original form data if available and requested.
		$original_form_data = [];
		if ( $use_original_form_data ) {
			$original_form_data = $this->get_original_form_data_context();
		}

		// Process target structure template.
		foreach ( $target_structure as $key => $template ) {
			if ( '{{field_value}}' === $template ) {
				$result[ $key ] = $value_str;
			} elseif ( 0 === strpos( $template, '{{auto_detect_original:' ) ) {
				// Extract detection key for original form data.
				preg_match( '/\{\{auto_detect_original:([^}]+)\}\}/', $template, $matches );
				$detect_key = $matches[1] ?? '';

				if ( isset( $auto_detect[ $detect_key ] ) ) {
					$detected_value = $this->auto_detect_field_value( $original_form_data, $auto_detect[ $detect_key ] );

					// Fallback to field value if detection fails and fallback is enabled.
					if ( empty( $detected_value ) && $fallback_to_field_value ) {
						$detected_value = $value_str;
					}

					$result[ $key ] = $detected_value;
				} else {
					$result[ $key ] = $fallback_to_field_value ? $value_str : '';
				}
			} elseif ( 0 === strpos( $template, '{{auto_detect:' ) ) {
				// Extract detection key for payload data (original behavior).
				preg_match( '/\{\{auto_detect:([^}]+)\}\}/', $template, $matches );
				$detect_key = $matches[1] ?? '';

				if ( isset( $auto_detect[ $detect_key ] ) ) {
					$result[ $key ] = $this->auto_detect_field_value( $payload, $auto_detect[ $detect_key ] );
				} else {
					$result[ $key ] = '';
				}
			} else {
				$result[ $key ] = $template;
			}
		}

		return $result;
	}

	/**
	 * Auto-detect field value from payload using detection keywords
	 *
	 * @param array $payload Full payload.
	 * @param array $keywords Detection keywords.
	 * @return string Detected value or empty string.
	 * @since 1.13.0
	 */
	private function auto_detect_field_value( $payload, $keywords ) {
		foreach ( $payload as $field => $value ) {
			$field_lower = strtolower( $field );

			foreach ( $keywords as $keyword ) {
				if ( false !== strpos( $field_lower, strtolower( $keyword ) ) ) {
					return Helper::get_string_value( $value );
				}
			}
		}

		return '';
	}

	/**
	 * Get original form data context for enhanced transformations
	 *
	 * @return array Original form data.
	 * @since 1.13.0
	 */
	private function get_original_form_data_context() {
		return $this->original_form_data_context;
	}

	/**
	 * Set original form data context for enhanced transformations
	 *
	 * @param array $original_form_data Original form data.
	 * @return void
	 * @since 1.13.0
	 */
	private function set_original_form_data_context( $original_form_data ) {
		$this->original_form_data_context = $original_form_data;
	}

	/**
	 * Increment workflow run count
	 *
	 * @param int    $form_id Form ID.
	 * @param string $workflow_id Workflow ID.
	 * @return void
	 * @since 1.13.0
	 */
	private function increment_workflow_run_count( $form_id, $workflow_id ) {
		if ( empty( $form_id ) || empty( $workflow_id ) ) {
			return;
		}

		// Get current workflows from meta.
		$workflows_json = get_post_meta( $form_id, '_srfm_native_integrations', true );
		$workflows      = [];

		if ( ! empty( $workflows_json ) && is_string( $workflows_json ) ) {
			$decoded   = json_decode( $workflows_json, true );
			$workflows = is_array( $decoded ) ? $decoded : [];
		}

		// Find and update the specific workflow.
		$updated = false;
		foreach ( $workflows as &$workflow ) {
			if ( isset( $workflow['id'] ) && $workflow['id'] === $workflow_id ) {
				$workflow['run_count'] = ( $workflow['run_count'] ?? 0 ) + 1;
				$workflow['last_run']  = time() * 1000; // Store in milliseconds like other timestamps.
				$updated               = true;
				break;
			}
		}

		// Save updated workflows back to meta.
		if ( $updated ) {
			update_post_meta( $form_id, '_srfm_native_integrations', wp_json_encode( $workflows ) );
		}
	}

}
