<?php
/**
 * Save and Resume - Init Class
 *
 * @since 2.2.0
 * @package sureforms-pro
 */

namespace SRFM_Pro\Inc\Pro\Save_Resume;

use SRFM\Inc\Helper;
use SRFM\Inc\Smart_Tags;
use SRFM_Pro\Inc\Helper as Pro_Helper;
use SRFM_Pro\Inc\Pro\Database\Tables\Save_Resume as Save_Resume_Table;
use SRFM_Pro\Inc\Traits\Get_Instance;
use WP_Error;
use WP_REST_Server;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
/**
 * Save and Resume - Init Class
 *
 * Handles the Save and Resume functionality.
 *
 * @since  2.2.0
 * @package sureforms-pro
 */
class Init {
	use Get_Instance;

	/**
	 * Save and Resume constructor.
	 *
	 * @since 2.2.0
	 */
	public function __construct() {
		add_action( 'srfm_register_additional_post_meta', [ $this, 'register_save_resume_meta' ] );
		add_action( 'srfm_enqueue_block_scripts', [ $this, 'enqueue_frontend_assets' ] );
		add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_admin_assets' ] );
		add_filter( 'srfm_rest_api_endpoints', [ $this, 'register_save_resume_endpoint' ] );
		add_filter( 'srfm_after_submit_button_content', [ $this, 'render_in_submit_container' ], 10, 2 );
		add_filter( 'srfm_page_break_buttons_html', [ $this, 'render_in_page_break' ], 10, 2 );
		add_filter( 'srfm_single_conversational_block', [ $this, 'render_in_conversational_form' ], 10, 2 );
		add_action( 'wp', [ $this, 'handle_resume_request' ] );
		add_action( 'srfm_form_submit', [ $this, 'delete_saved_form_on_submit' ] );
		add_action( 'srfm_daily_scheduled_action', [ $this, 'cleanup_old_draft_submissions' ] );
	}

	/**
	 * Register post meta for Save and Resume.
	 * This function registers a custom meta key  `_srfm_save_resume` for storing raw Save and Resume meta data.
	 * The meta key will store data as a JSON string.
	 *
	 * @hooked srfm_register_additional_post_meta
	 * @since 2.2.0
	 * @return void
	 */
	public function register_save_resume_meta() {
		register_post_meta(
			SRFM_FORMS_POST_TYPE,
			'_srfm_save_resume',
			[
				'type'              => 'string',
				'single'            => true,
				'show_in_rest'      => [
					'schema' => [
						'type'    => 'string',
						'context' => [ 'edit' ],
					],
				],
				'sanitize_callback' => [ Utils::class, 'save_resume_meta_sanitizer' ],
				'auth_callback'     => static function () {
					return Helper::current_user_can();
				},
				'default'           => wp_json_encode(
					[
						'status'                => false,
						'linkText'              => __( 'Save Progress', 'sureforms-pro' ),
						'confirmationMessage'   => Utils::get_default_confirmation_message(),
						'afterEmailSendMessage' => Utils::get_default_after_email_send_message(),
						'emailBody'             => Utils::get_default_email_body(),
					]
				),
			]
		);
	}

	/**
	 * Enqueue Save and Resume frontend assets.
	 *
	 * @hooked srfm_enqueue_block_scripts
	 * @param array<string,mixed> $args Arguments array.
	 * @since 2.2.0
	 * @return void
	 */
	public function enqueue_frontend_assets( $args ) {

		// Static flag to ensure scripts are only enqueued once.
		static $scripts_enqueued = false;

		// Return early if scripts are already enqueued.
		if ( $scripts_enqueued ) {
			return;
		}

		// Get form ID from args.
		$form_id = is_array( $args ) && isset( $args['attr'] ) && is_array( $args['attr'] ) && isset( $args['attr']['formId'] ) ? absint( $args['attr']['formId'] ) : 0;

		// only enqueue if save and resume is enabled for this form.
		if ( ! $this->is_save_resume_enabled( $form_id ) ) {
			return;
		}

		$file_prefix = Utils::get_file_prefix();
		$dir_name    = Utils::get_dir_name();
		$css_uri     = Utils::get_css_uri( $dir_name );
		$rtl         = Utils::get_rtl_suffix();
		// Load the frontend custom app styles.
		wp_enqueue_style( SRFM_PRO_SLUG . '-save-resume', $css_uri . 'save-resume-frontend' . $file_prefix . $rtl . '.css', [], SRFM_PRO_VER );

		$scripts = [
			[
				'unique_file'        => 'saveResumeFrontend',
				'unique_handle'      => 'save-resume-frontend',
				'extra_dependencies' => [],
			],
		];

		foreach ( $scripts as $script ) {
			$script_dep_path = SRFM_PRO_DIR . 'dist/package/pro/' . $script['unique_file'] . '.asset.php';
			$script_dep_data = file_exists( $script_dep_path )
				? include $script_dep_path
				: [
					'dependencies' => [],
					'version'      => SRFM_PRO_VER,
				];
			$script_dep      = array_merge( $script_dep_data['dependencies'], $script['extra_dependencies'] );

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

			wp_localize_script(
				SRFM_PRO_SLUG . '-' . $script['unique_handle'],
				'srfmSaveFrontendData',
				[
					'nonce' => wp_create_nonce( 'srfm_save_resume_nonce' ),
				]
			);

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

		// Localize resume data if available.
		$this->localize_resume_data();

		// Set flag to true to prevent re-enqueuing.
		$scripts_enqueued = true;
	}

	/**
	 * Enqueue Save and Resume block editor assets.
	 *
	 * @hooked enqueue_block_editor_assets
	 * @since 2.2.0
	 * @return void
	 */
	public function enqueue_admin_assets() {
		$file_prefix = Utils::get_file_prefix();
		$dir_name    = Utils::get_dir_name();
		$css_uri     = Utils::get_css_uri( $dir_name );
		$rtl         = Utils::get_rtl_suffix();
		wp_enqueue_style( SRFM_PRO_SLUG . '-save-resume', $css_uri . 'save-resume-editor' . $file_prefix . $rtl . '.css', [], SRFM_PRO_VER );

		$script = [
			'unique_file'        => 'saveResumeEditor',
			'unique_handle'      => 'save-resume-editor',
			'extra_dependencies' => [],
		];

		$script_dep_path = SRFM_PRO_DIR . 'dist/package/pro/' . $script['unique_file'] . '.asset.php';
		$script_dep_data = file_exists( $script_dep_path )
			? include $script_dep_path
			: [
				'dependencies' => [],
				'version'      => SRFM_PRO_VER,
			];
		$script_dep      = array_merge( $script_dep_data['dependencies'], $script['extra_dependencies'] );

		wp_enqueue_script(
			SRFM_PRO_SLUG . '-' . $script['unique_handle'],
			SRFM_PRO_URL . 'dist/package/pro/' . $script['unique_file'] . '.js',
			$script_dep,
			$script_dep_data['version'],
			true
		);

		wp_localize_script(
			SRFM_PRO_SLUG . '-' . $script['unique_handle'],
			'srfmSaveResumeEditorData',
			[
				'editor_default_styles' => Pro_Helper::editor_default_styles(),
				'is_ssl'                => is_ssl(),
			]
		);

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

	/**
	 * Register Save and Resume REST API endpoint.
	 *
	 * @hooked srfm_rest_api_endpoints
	 * @param array<mixed> $endpoints Existing endpoints.
	 * @return array<mixed>
	 * @since 2.2.0
	 */
	public function register_save_resume_endpoint( $endpoints ) {
		$endpoints['save-form'] = [
			'methods'             => WP_REST_Server::EDITABLE,
			'callback'            => [ $this, 'handle_save_form' ],
			'permission_callback' => [ $this, 'save_form_permissions_check' ],
		];

		$endpoints['send-resume-email'] = [
			'methods'             => WP_REST_Server::EDITABLE,
			'callback'            => [ $this, 'handle_send_resume_email' ],
			'permission_callback' => [ $this, 'send_resume_email_permissions_check' ],
		];
		return $endpoints;
	}

	/**
	 * Check whether a given request has permission access route.
	 *
	 * @param \WP_REST_Request $request Request object or array containing form data.
	 * @since 2.2.0
	 * @return WP_Error|bool
	 */
	public function save_form_permissions_check( $request ) {
		$form_data = Helper::sanitize_by_field_type( $request->get_params() );

		if ( empty( $form_data ) || ! is_array( $form_data ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Form data is not found.', 'sureforms-pro' ),
				]
			);
		}

		// Get nonce from request data.
		$nonce = isset( $form_data['nonce'] ) ? Helper::get_string_value( $form_data['nonce'] ) : '';

		if ( ! wp_verify_nonce( sanitize_text_field( $nonce ), 'srfm_save_resume_nonce' ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Nonce verification failed.', 'sureforms-pro' ),
				]
			);
		}

		if ( ! $form_data['form_id'] ) {
			wp_send_json_error(
				[
					'message'  => __( 'Form Id is missing.', 'sureforms-pro' ),
					'position' => 'header',
				]
			);
		}

		return true;
	}

	/**
	 * Handle the save form request.
	 *
	 * @param \WP_REST_Request $request Request object containing form data.
	 * @since 2.2.0
	 * @return void
	 */
	public function handle_save_form( $request ) {

		$save_data = json_decode( $request->get_body(), true );

		// Check for JSON decode error.
		if ( json_last_error() !== JSON_ERROR_NONE ) {
			wp_send_json_error(
				[
					'srfm_save_form_error' => __( 'Invalid form data.', 'sureforms-pro' ),
				]
			);
		}

		$save_data = is_array( $save_data ) ? $save_data : [];
		$form_id   = isset( $save_data['form_id'] ) ? absint( $save_data['form_id'] ) : 0;

		// go ahead only if save and resume is enabled for this form.
		if ( ! $this->is_save_resume_enabled( $form_id ) ) {
			wp_send_json_error( __( 'Save and Resume is not enabled for this form.', 'sureforms-pro' ) );
		}

		$source_url = isset( $save_data['source_url'] ) && is_string( $save_data['source_url'] ) ? esc_url_raw( $save_data['source_url'] ) : null;

		// Delete existing token if found in source URL.
		$this->delete_existing_token_from_url( $source_url );

		$conversational_current_step = isset( $save_data['conversational_current_step'] ) ? absint( $save_data['conversational_current_step'] ) : null;
		$page_break_current_step     = isset( $save_data['page_break_current_step'] ) ? absint( $save_data['page_break_current_step'] ) : null;
		$form_data                   = isset( $save_data['form_data'] ) && is_array( $save_data['form_data'] ) ? Helper::sanitize_by_field_type( $save_data['form_data'] ) : [];

		// Clean and filter form data for saving.
		$form_data = $this->clean_form_data_for_saving( $form_data );

		// Add current step to form data if available.
		if ( null !== $conversational_current_step ) {
			$form_data['conversational-current-step'] = $conversational_current_step;
		}
		if ( null !== $page_break_current_step ) {
			$form_data['page-break-current-step'] = $page_break_current_step;
		}

		$id = Helper::generate_unique_id( Save_Resume_Table::class );

		if ( ! $id ) {
			wp_send_json_error(
				[
					'srfm_save_form_error' => __( 'Could not generate unique ID for saving form. Please try again.', 'sureforms-pro' ),
				]
			);
		}

		Save_Resume_Table::add(
			[
				'id'         => $id,
				'email'      => isset( $save_data['email'] ) && is_string( $save_data['email'] ) ? sanitize_email( $save_data['email'] ) : '',
				'ip'         => isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '',
				'source_url' => $source_url ?? '',
				'form_id'    => $form_id,
				'form_data'  => wp_json_encode( $form_data ),
			]
		);

		$settings             = $this->get_save_resume_settings( $form_id );
		$confirmation_message = Helper::strip_js_attributes( $settings['confirmationMessage'] ?? '' );
		$resume_url           = $this->build_resume_url( $source_url ?? '', $id );

		// Generate the save resume widgets.
		$widgets                 = $this->generate_save_resume_widgets( $resume_url, $id, $form_id );
		$resume_url_widget       = $widgets['resume_url_widget'];
		$save_email_input_widget = $widgets['email_input_widget'];

		// Process smart tags and replace placeholders in confirmation message.
		$smart_tags           = Smart_Tags::get_instance();
		$confirmation_message = $smart_tags->process_smart_tags( $confirmation_message, $form_data, $form_data );
		// Replace placeholders.
		$confirmation_message = str_replace( '{resume_url}', $resume_url_widget, is_string( $confirmation_message ) ? $confirmation_message : '' );

		// Check if content contains {save_email_input} placeholder.
		if ( strpos( $confirmation_message, '{save_email_input}' ) !== false ) {
			// Replace the placeholder with the widget.
			$pos                  = strpos( $confirmation_message, '{save_email_input}' );
			$confirmation_message = substr_replace( $confirmation_message, $save_email_input_widget, $pos, strlen( '{save_email_input}' ) );
		} else {
			// Only add email input widget if placeholder does not exist.
			$confirmation_message .= $save_email_input_widget;
		}

		wp_send_json_success(
			[
				'token'               => $id,
				'confirmationMessage' => $confirmation_message,
			]
		);
	}

	/**
	 * Check whether a given request has permission to send resume email.
	 *
	 * @param \WP_REST_Request $request Request object or array containing form data.
	 * @since 2.2.0
	 * @return WP_Error|bool
	 */
	public function send_resume_email_permissions_check( $request ) {
		$body_data = json_decode( $request->get_body(), true );

		if ( json_last_error() !== JSON_ERROR_NONE ) {
			wp_send_json_error(
				[
					'message' => __( 'Invalid request data.', 'sureforms-pro' ),
				]
			);
		}

		$body_data = is_array( $body_data ) ? $body_data : [];

		// Get nonce from request data.
		$nonce = isset( $body_data['nonce'] ) && is_string( $body_data['nonce'] ) ? $body_data['nonce'] : '';

		if ( ! wp_verify_nonce( sanitize_text_field( $nonce ), 'srfm_save_resume_nonce' ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Nonce verification failed.', 'sureforms-pro' ),
				]
			);
		}

		return true;
	}

	/**
	 * Handle the send resume email request.
	 *
	 * @param \WP_REST_Request $request Request object containing email and token.
	 * @since 2.2.0
	 * @return void
	 */
	public function handle_send_resume_email( $request ) {
		$body_data = json_decode( $request->get_body(), true );
		$body_data = is_array( $body_data ) ? $body_data : [];

		$email   = isset( $body_data['email'] ) && is_string( $body_data['email'] ) ? sanitize_email( $body_data['email'] ) : '';
		$token   = isset( $body_data['token'] ) && is_string( $body_data['token'] ) ? sanitize_text_field( $body_data['token'] ) : '';
		$form_id = isset( $body_data['form_id'] ) ? absint( $body_data['form_id'] ) : 0;

		if ( empty( $email ) || ! is_email( $email ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Please enter a valid email address.', 'sureforms-pro' ),
				]
			);
		}

		if ( empty( $token ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Invalid token.', 'sureforms-pro' ),
				]
			);
		}

		if ( empty( $form_id ) ) {
			wp_send_json_error(
				[
					'message' => __( 'Form ID is missing.', 'sureforms-pro' ),
				]
			);
		}

		// Get the saved form data to verify token exists.
		$saved_data = $this->get_saved_data_by_token( $token );
		if ( ! $saved_data ) {
			wp_send_json_error(
				[
					'message' => __( 'Invalid or expired token.', 'sureforms-pro' ),
				]
			);
		}

		// Get form settings.
		$settings = $this->get_save_resume_settings( $form_id );
		$form     = get_post( $form_id );

		if ( ! $form ) {
			wp_send_json_error(
				[
					'message' => __( 'Form not found.', 'sureforms-pro' ),
				]
			);
		}

		$form_title = $form->post_title;

		// Build resume URL.
		$source_url = isset( $saved_data['source_url'] ) && is_string( $saved_data['source_url'] ) ? $saved_data['source_url'] : '';
		$resume_url = $this->build_resume_url( $source_url, $token );

		// Generate the save resume widgets for after email message.
		$widgets           = $this->generate_save_resume_widgets( $resume_url, $token, $form_id );
		$resume_url_widget = $widgets['resume_url_widget'];

		// Get email body from settings and process smart tags.
		$email_body_content = isset( $settings['emailBody'] ) && is_string( $settings['emailBody'] ) ? $settings['emailBody'] : Utils::get_default_email_body();
		$smart_tags         = Smart_Tags::get_instance();
		$form_data          = isset( $saved_data['form_data'] ) && is_array( $saved_data['form_data'] ) ? $saved_data['form_data'] : [];
		$email_body_content = $smart_tags->process_smart_tags( $email_body_content, $form_data, $form_data );

		// Build the resume button HTML.
		ob_start();
		?>
		<a href="<?php echo esc_url( $resume_url ); ?>" target="_blank" style="display: inline-block; background-color: #C74B27; border: 1px solid #C74B27; border-radius: 6px; color: #ffffff; font-size: 14px; font-weight: 500; line-height: 20px; padding: 12px 24px; text-decoration: none; -webkit-text-size-adjust: none; mso-hide: all; cursor: pointer; width: fit-content;">
			<?php esc_html_e( 'Resume Your Form', 'sureforms-pro' ); ?>
		</a>
		<?php
		$resume_button_output = ob_get_clean();
		$resume_button        = trim( false !== $resume_button_output ? $resume_button_output : '' );

		// Replace placeholders.
		$email_body_content = str_replace( '{resume_url}', $resume_button, $email_body_content );

		// Build the email with custom Save & Resume template that includes logo.
		$email_body = $this->build_email_template( $email_body_content );

		// Send email.
		$subject = sprintf(
			/* translators: %s: Form title */
			__( 'Resume Your Form: %s', 'sureforms-pro' ),
			$form_title
		);

		$headers  = 'X-Mailer: PHP/' . phpversion() . "\r\n";
		$headers .= "Content-Type: text/html; charset=utf-8\r\n";

		// Store mail error for debugging.
		$mail_error = '';
		add_action(
			'wp_mail_failed',
			static function ( $error ) use ( &$mail_error ) {
				if ( is_wp_error( $error ) ) {
					$mail_error = $error->get_error_message();
				}
			}
		);

		/**
		 * Temporary override the content type for wp_mail.
		 * This helps us from breaking of content type from other plugins.
		 */
		add_filter(
			'wp_mail_content_type',
			static function() {
				return 'text/html'; // We need "text/html" content type to render our emails.
			},
			99
		);

		/**
		 * Start sending email.
		 * Wrapping it in the buffer because when some plugin such as zoho mail, overrides the wp_mail
		 * function and any exception is thrown ( Or printed ) from that plugin side, it affects the JSON response.
		 * So, to make sure such exceptions doesn't affect our JSON response, we are wrapping it inside buffer.
		 *
		 * Try-Catch does not work because the notice or errors might be echoed by other plugins rather than thrown as an exception.
		 *
		 * @since 2.2.0
		 */
		$sent = false;
		ob_start();
		$sent = wp_mail( $email, $subject, $email_body, $headers );
		if ( ! $sent ) {
			// Fallback to default PHP mail if for some reasons wp_mail fails.
			$sent = mail( $email, $subject, $email_body, $headers );
		}
		$email_report = ob_get_clean(); // Catch any printed notice/errors/message for reports.

		if ( $sent ) {
			// Update the email in the database for this token.
			Save_Resume_Table::update(
				$token,
				[
					'email' => $email,
				]
			);

			// Get after email send message and process smart tags.
			$after_email_message = isset( $settings['afterEmailSendMessage'] ) && is_string( $settings['afterEmailSendMessage'] ) ? $settings['afterEmailSendMessage'] : '';
			$form_data           = isset( $saved_data['form_data'] ) && is_array( $saved_data['form_data'] ) ? $saved_data['form_data'] : [];
			$after_email_message = $smart_tags->process_smart_tags( $after_email_message, $form_data, $form_data );
			$after_email_message = str_replace( '{save_email}', esc_html( $email ), $after_email_message );
			$after_email_message = str_replace( '{resume_url}', $resume_url_widget, $after_email_message );

			wp_send_json_success(
				[
					'message'               => __( 'Email sent successfully!', 'sureforms-pro' ),
					'afterEmailSendMessage' => $after_email_message,
				]
			);
		} else {
			$error_message = __( 'Email could not be sent. This may be due to email server configuration.', 'sureforms-pro' );

			// Determine the reason for failure.
			$reason = '';
			if ( ! empty( $email_report ) ) {
				$reason = $email_report;
			} elseif ( ! empty( $mail_error ) ) {
				$reason = $mail_error;
			}

			// Append debug info if available and in debug mode.
			if ( ! empty( $reason ) && ( defined( 'WP_DEBUG' ) && WP_DEBUG ) ) {
				$error_message .= ' Error: ' . $reason;
			}

			// Still return the after email message so user can see the resume URL.
			$after_email_message = isset( $settings['afterEmailSendMessage'] ) && is_string( $settings['afterEmailSendMessage'] ) ? $settings['afterEmailSendMessage'] : '';
			$form_data           = isset( $saved_data['form_data'] ) && is_array( $saved_data['form_data'] ) ? $saved_data['form_data'] : [];
			$after_email_message = $smart_tags->process_smart_tags( $after_email_message, $form_data, $form_data );
			$after_email_message = str_replace( '{save_email}', esc_html( $email ), $after_email_message );
			$after_email_message = str_replace( '{resume_url}', $resume_url_widget, $after_email_message );

			// Provide helpful fallback message.
			ob_start();
			?>
			<p><?php echo esc_html( $error_message ); ?></p>
			<p><?php esc_html_e( 'However, you can still copy the resume link above.', 'sureforms-pro' ); ?></p>
			<?php if ( ! empty( $after_email_message ) ) { ?>
				<?php echo $after_email_message; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Already escaped via str_replace. ?>
			<?php } ?>
			<?php
			$fallback_message_output = ob_get_clean();
			$fallback_message        = trim( false !== $fallback_message_output ? $fallback_message_output : '' );

			wp_send_json_error(
				[
					'message'               => $error_message,
					'afterEmailSendMessage' => $fallback_message,
				]
			);
		}
	}

	/**
	 * Add save and resume link in submit container.
	 *
	 * @hooked srfm_submit_container
	 * @param string              $html Original HTML.
	 * @param array<string,mixed> $args Arguments array.
	 * @since 2.2.0
	 * @return string
	 */
	public function render_in_submit_container( $html, $args ) {
		$id = isset( $args['id'] ) && is_numeric( $args['id'] ) ? Helper::get_integer_value( $args['id'] ) : 0;

		// also match button alignment classes.
		if ( ! $this->is_save_resume_enabled( $id ) ) {
			return $html;
		}

		$submit_button_alignment = isset( $args['submit_button_alignment'] ) && is_string( $args['submit_button_alignment'] ) ? sanitize_text_field( $args['submit_button_alignment'] ) : 'left';
		$save_resume_text        = $this->get_save_resume_text( $id );

		$save_resume_link = $this->get_save_resume_link( $save_resume_text, 'srfm-save-resume-submit-link-ctn', '', $submit_button_alignment );

		ob_start();
		?>
			<?php echo wp_kses_post( $save_resume_link ); ?>
		<?php
		$output = ob_get_clean();
		return $html . trim( false !== $output ? $output : '' );
	}

	/**
	 * Add save and resume link in page break buttons.
	 *
	 * @hooked srfm_page_break_buttons
	 * @param string              $html Original HTML.
	 * @param array<string,mixed> $args Arguments array.
	 * @return string
	 */
	public function render_in_page_break( $html, $args ) {
		$id                = isset( $args['id'] ) && is_numeric( $args['id'] ) ? Helper::get_integer_value( $args['id'] ) : 0;
		$previous_btn_text = isset( $args['previous_btn_text'] ) && is_string( $args['previous_btn_text'] ) ? sanitize_text_field( $args['previous_btn_text'] ) : esc_html__( 'Previous', 'sureforms-pro' );
		$next_btn_text     = isset( $args['next_btn_text'] ) && is_string( $args['next_btn_text'] ) ? sanitize_text_field( $args['next_btn_text'] ) : esc_html__( 'Next', 'sureforms-pro' );
		$is_welcome_screen = isset( $args['is_welcome_screen'] ) ? boolval( $args['is_welcome_screen'] ) : false;

		// Only show the link if Save and Resume is enabled.
		if ( ! $this->is_save_resume_enabled( $id ) ) {
			return $html;
		}

		$save_resume_text = $this->get_save_resume_text( $id );

		// Determine container classes.
		$container_classes = method_exists( '\SRFM\Inc\Helper', 'join_strings' )
			? esc_attr( Helper::join_strings( [ 'srfm-page-break-buttons', 'wp-block-button', $is_welcome_screen ? 'srfm-welcome-screen-active' : '' ] ) )
			: 'srfm-page-break-buttons wp-block-button';

		$save_resume_link = $this->get_save_resume_link( $save_resume_text, 'srfm-save-resume-link--margin-right' );

		ob_start();
		?>
		<div class="<?php echo esc_attr( $container_classes ); ?>">
			<button aria-label="<?php esc_attr_e( 'Go to Previous Page', 'sureforms-pro' ); ?>" class="srfm-pre-btn"><?php echo esc_attr( $previous_btn_text ); ?></button>
			<div class="srfm-save-resume-page-break-ctn">
				<?php
				// Use custom kses to allow additional attributes.
				$allowed_html                          = wp_kses_allowed_html( 'post' );
				$allowed_html['a']['tabindex']         = true;
				$allowed_html['a']['role']             = true;
				$allowed_html['a']['aria-label']       = true;
				$allowed_html['a']['aria-describedby'] = true;
				$allowed_html['a']['data-*']           = true;
				$allowed_html['span']['tabindex']      = true;
				$allowed_html['span']['role']          = true;
				$allowed_html['span']['aria-label']    = true;
				$allowed_html['span']['aria-hidden']   = true;
				$allowed_html['span']['data-*']        = true;

				echo wp_kses( $save_resume_link, $allowed_html );
				?>
				<button aria-label="<?php esc_attr_e( 'Go to Next Page', 'sureforms-pro' ); ?>" class="srfm-nxt-btn"><?php echo esc_attr( $next_btn_text ); ?></button>
			</div>
		</div>
		<?php
		$output = ob_get_clean();
		return trim( false !== $output ? $output : '' );
	}

	/**
	 * Add save and resume link in conversational form.
	 *
	 * @hooked srfm_page_break_buttons
	 * @param string              $output   Original block markup.
	 * @param array<string,mixed> $args Arguments array.
	 * @return string Modified block markup.
	 */
	public function render_in_conversational_form( $output, $args ) {
		$index        = isset( $args['index'] ) && is_numeric( $args['index'] ) ? Helper::get_integer_value( $args['index'] ) : 0;
		$total_blocks = isset( $args['total_blocks'] ) && is_numeric( $args['total_blocks'] ) ? Helper::get_integer_value( $args['total_blocks'] ) : 0;
		$is_block     = isset( $args['is_block'] ) ? boolval( $args['is_block'] ) : false;

		$form_id = get_the_ID();

		if ( ! is_int( $form_id ) ) {
			return $output;
		}

		// Only show the link if Save and Resume is enabled.
		if ( ! $this->is_save_resume_enabled( $form_id ) ) {
			return $output;
		}

		$save_resume_text = $this->get_save_resume_text( $form_id );

		// Only add link on "Next" button screens (not final block).
		if ( $is_block && $index < $total_blocks - 1 ) {
			// Get the save & resume link with accessibility attributes and href for conversational forms.
			$save_continue_link = $this->get_save_resume_link( $save_resume_text, '', '#' );

			// Wrap button+helper and link inside flex container using CSS classes.
			$replacement_wrapper_start = '<div class="srfm-cf-save-resume-wrapper">';
			$replacement_wrapper_end   = '</div>';

			if ( strpos( $output, 'srfm-cf-nav-helper-text' ) !== false ) {
				// Replace just the button + helper wrapper.
				$output = str_replace(
					'<button',
					$replacement_wrapper_start . '<div class="srfm-cf-button-helper-wrapper"><button',
					$output
				);
				$output = str_replace(
					'</span></div>',
					'</span></div></div><div>' . $save_continue_link . '</div>' . $replacement_wrapper_end,
					$output
				);
			} else {
				// Fallback: append before closing container.
				$output = str_replace(
					'</div></div>',
					$replacement_wrapper_start . '<div>' . $save_continue_link . '</div>' . $replacement_wrapper_end . '</div></div>',
					$output
				);
			}
		}

		return $output;
	}

	/**
	 * Handle resume request when user visits page with token.
	 *
	 * @return void
	 * @since 2.2.0
	 */
	public function handle_resume_request() {
		// Check if resume token exists in URL.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public resume link, validated via token lookup.
		if ( ! isset( $_GET['srfm_continue_token'] ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public resume link, validated via token lookup.
		$token = sanitize_text_field( wp_unslash( $_GET['srfm_continue_token'] ) );

		// Validate and get saved data.
		$saved_data = $this->get_saved_data_by_token( $token );

		if ( ! $saved_data ) {
			return;
		}

		// Store resume data in global variable to pass to JavaScript.
		$GLOBALS['srfm_resume_data'] = $saved_data;
	}

	/**
	 * Show error message for invalid/expired token.
	 *
	 * @return void
	 * @since 2.2.0
	 */
	public function show_token_error() {
		?>
		<script type="text/javascript">
			(function() {
				'use strict';
				document.addEventListener('DOMContentLoaded', function() {
					var message = document.createElement('div');
					message.className = 'srfm-resume-error';
					message.innerHTML = '<div class="srfm-notice srfm-notice-error">' +
						'<p><strong><?php echo esc_js( __( 'Invalid or Expired Link', 'sureforms-pro' ) ); ?></strong></p>' +
						'<p><?php echo esc_js( __( 'The resume link you used is invalid or has expired. Please start a new form submission.', 'sureforms-pro' ) ); ?></p>' +
						'</div>';

					var form = document.querySelector('.srfm-form-container');
					if (form) {
						form.parentNode.insertBefore(message, form);
					}
				});
			})();
		</script>
		<?php
	}

	/**
	 * Delete saved form after successful submission.
	 *
	 * @return void
	 * @since 2.2.0
	 */
	public function delete_saved_form_on_submit() {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing -- Token validated during form submission.
		if ( ! isset( $_GET['srfm_continue_token'] ) && ! isset( $_POST['srfm_continue_token'] ) ) {
			return;
		}

		$token = '';
		if ( isset( $_GET['srfm_continue_token'] ) ) {
			$token = sanitize_text_field( wp_unslash( $_GET['srfm_continue_token'] ) );
		} elseif ( isset( $_POST['srfm_continue_token'] ) ) {
			$token = sanitize_text_field( wp_unslash( $_POST['srfm_continue_token'] ) );
		}
		// phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing

		if ( empty( $token ) ) {
			return;
		}

		// Delete the saved form data.
		Save_Resume_Table::get_instance()->delete( $token );
	}

	/**
	 * Cleanup old draft submissions that are older than 30 days.
	 *
	 * @hooked srfm_daily_cleanup_event
	 * @since 2.2.0
	 * @return void
	 */
	public function cleanup_old_draft_submissions() {
		Save_Resume_Table::delete_old_drafts();
	}

	/**
	 * Localize resume data for JavaScript.
	 *
	 * @return void
	 * @since 2.2.0
	 */
	private function localize_resume_data() {
		// Check if we have resume data stored in global.
		if ( ! isset( $GLOBALS['srfm_resume_data'] ) ) {
			return;
		}

		$saved_data = $GLOBALS['srfm_resume_data'];

		if ( ! $saved_data || ! is_array( $saved_data ) ) {
			return;
		}

		// Localize the script with resume data.
		wp_localize_script(
			SRFM_PRO_SLUG . '-save-resume-frontend',
			'srfmResumeData',
			[
				'formId'   => absint( $saved_data['form_id'] ),
				'formData' => $saved_data['form_data'],
				'token'    => sanitize_text_field( Helper::get_string_value( $saved_data['token'] ) ),
			]
		);
	}

	/**
	 * Delete existing token found in URL query parameters.
	 *
	 * @param string|null $source_url The source URL to check for existing tokens.
	 * @return void
	 * @since 2.2.0
	 */
	private function delete_existing_token_from_url( $source_url ) {
		if ( ! $source_url ) {
			return;
		}

		$parsed_url = wp_parse_url( $source_url );
		if ( ! isset( $parsed_url['query'] ) ) {
			return;
		}

		parse_str( $parsed_url['query'], $query_params );
		if ( ! isset( $query_params['srfm_continue_token'] ) ) {
			return;
		}

		$existing_token = sanitize_text_field( Helper::get_string_value( $query_params['srfm_continue_token'] ) );
		Save_Resume_Table::delete( $existing_token );
	}

	/**
	 * Clean and filter form data for saving.
	 *
	 * @param array<mixed> $form_data The form data to clean and filter.
	 * @return array<mixed> The cleaned form data.
	 * @since 2.2.0
	 */
	private function clean_form_data_for_saving( $form_data ) {
		if ( ! is_array( $form_data ) ) {
			return [];
		}

		// Remove fields that are not part of the actual form data.
		unset( $form_data['sureforms_form_submit'], $form_data['_wp_http_referer'], $form_data['srfm-sender-email-field'] );

		// Filter out unwanted field keys (upload, repeater, hidden fields).
		$form_data = array_filter(
			$form_data,
			static function ( $key ) {
				if ( ! is_string( $key ) ) {
					return true;
				}

				$excluded_prefixes = [ 'srfm-upload', 'srfm-repeater', 'srfm-hidden' ];
				foreach ( $excluded_prefixes as $prefix ) {
					if ( strpos( $key, $prefix ) !== false ) {
						return false;
					}
				}

				return true;
			},
			ARRAY_FILTER_USE_KEY
		);

		// Skip empty fields in form data.
		return array_filter(
			$form_data,
			static function ( $value ) {
				return '' !== $value && null !== $value;
			}
		);
	}

	/**
	 * Generate save resume widgets HTML.
	 *
	 * @param string $resume_url The resume URL.
	 * @param string $id The form save token ID.
	 * @param int    $form_id The form ID.
	 * @return array{resume_url_widget: string, email_input_widget: string}
	 * @since 2.2.0
	 */
	private function generate_save_resume_widgets( $resume_url, $id, $form_id ) {
		// Build the resume URL widget HTML.
		ob_start();
		?>
		<div class="srfm-resume-url-container">
		<div style="width: 100%; display: flex; align-items: center; overflow: hidden; box-sizing: border-box; margin: 0; padding: 0; background: none; border: none; font-family: inherit; position: relative;">
			<input
				type="text"
				value="<?php echo esc_attr( $resume_url ); ?>"
				id="srfm-resume-link"
				readonly
				disabled
				class="srfm-resume-url-input"
				style="height: 40px; flex: 1; padding: 10px; outline: none; font-size: 16px; color: #8d8d8d; background: #fbfbfb; border: 1px solid #c4c4c4; border-top-left-radius: 6px; border-bottom-left-radius: 6px; border-top-right-radius: 0; border-bottom-right-radius: 0; box-sizing: border-box; margin: 0; width: auto; min-width: 0; font-family: inherit; line-height: normal; text-align: left; vertical-align: baseline; appearance: none; -webkit-appearance: none; -moz-appearance: none;"
			/>
			<button
				title="<?php esc_attr_e( 'Copy to clipboard', 'sureforms-pro' ); ?>"
				class="srfm-save-resume-copy-btn"
				style="background: #f9fafb; cursor: pointer; padding: 9px 12px; border: 1px solid #c4c4c4; border-left: 0; border-top-right-radius: 6px; border-bottom-right-radius: 6px; border-top-left-radius: 0; border-bottom-left-radius: 0; box-sizing: border-box; margin: 0; height: 40px; width: auto; min-width: 44px; display: flex; align-items: center; justify-content: center; font-family: inherit; font-size: inherit; line-height: normal; text-align: center; vertical-align: baseline; appearance: none; -webkit-appearance: none; -moz-appearance: none; color: inherit; text-decoration: none; outline: none;"
			>
				<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#111827" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block; margin: 0; padding: 0; border: none; background: none; pointer-events: none; flex-shrink: 0;">
					<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>
					<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>
				</svg>
			</button>
		</div>
		</div>
		<?php
		$resume_url_widget_output = ob_get_clean();
		$resume_url_widget        = trim( false !== $resume_url_widget_output ? $resume_url_widget_output : '' );

		// Build the email input widget HTML.
		ob_start();
		?>
		<div class="srfm-save-resume-email-container">
			<div style="display: flex; flex-direction: column; gap: 10px;">
				<input
					type="email"
					placeholder="<?php esc_attr_e( 'Enter your email address', 'sureforms-pro' ); ?>"
					id="srfm_email_input"
					class="srfm-save-resume-email-input email-markup"
					style="width: 100%; height: 40px; padding: 10px; border: 1px solid #d1d5db; border-radius: 6px; outline: none; font-size: 16px; color: #111827; background: #ffffff;"
					onfocus="this.style.borderColor='var(--srfm-color-scheme-primary, #c74b27)'; this.style.boxShadow='0 0 0 .25px var(--srfm-color-scheme-primary, #c74b27)';"
					onblur="this.style.borderColor='#d1d5db'; this.style.boxShadow='none';"
				/>
				<button
					type="button"
					class="srfm-button srfm-submit-button srfm-save-resume-send-email-btn"
					data-token="<?php echo esc_attr( $id ); ?>"
					data-form-id="<?php echo esc_attr( Helper::get_string_value( $form_id ) ); ?>"
					style="width: fit-content;"
				>
					<?php esc_html_e( 'Send Link', 'sureforms-pro' ); ?>
				</button>
			</div>
		<?php
		$icon    = Helper::fetch_svg( 'info_circle', '', 'aria-hidden="true" style="line-height: 1; display: flex; color: #dc2626;"' );
		$classes = 'srfm-common-error-message srfm-error-message srfm-footer-error';
		?>
		<p id="srfm-error-message" class="<?php echo esc_attr( $classes ); ?>" style="margin-top: 10px; display: none; line-height: 20px; color: #111827; font-size: 14px; font-weight: var( --srfm-error-font-weight );align-items: center; border: 1px solid #fecaca; width: 100%; padding: 12px; border-radius: 8px; background-color: #fef2f2; gap: 8px;"><?php echo wp_kses( $icon, Helper::$allowed_tags_svg ); ?><span class="srfm-error-content srfm-save-resume-email-message"></span></p>
		</div>
		<?php
		$save_email_input_output = ob_get_clean();
		$save_email_input_widget = trim( false !== $save_email_input_output ? $save_email_input_output : '' );

		return [
			'resume_url_widget'  => $resume_url_widget,
			'email_input_widget' => $save_email_input_widget,
		];
	}

	/**
	 * Get save resume settings for a form.
	 *
	 * @param int $form_id The form ID.
	 * @return array{status: bool, linkText: string, confirmationMessage: string, afterEmailSendMessage: string, emailBody: string}
	 * @since 2.2.0
	 */
	private function get_save_resume_settings( $form_id ) {
		$settings = get_post_meta( $form_id, '_srfm_save_resume', true );
		$settings = is_string( $settings ) ? json_decode( $settings, true ) : [];

		// Default values to ensure proper array shape.
		$defaults = [
			'status'                => false,
			'linkText'              => '',
			'confirmationMessage'   => '',
			'afterEmailSendMessage' => '',
			'emailBody'             => '',
		];

		// Ensure we return the expected array structure with default values if settings are invalid.
		if ( ! is_array( $settings ) || empty( $settings ) ) {
			return $defaults;
		}

		// Merge with defaults to ensure all required keys exist and cast to expected type.
		$merged = array_merge( $defaults, $settings );

		return [
			'status'                => (bool) ( $merged['status'] ?? false ),
			'linkText'              => (string) ( $merged['linkText'] ?? '' ),
			'confirmationMessage'   => (string) ( $merged['confirmationMessage'] ?? '' ),
			'afterEmailSendMessage' => (string) ( $merged['afterEmailSendMessage'] ?? '' ),
			'emailBody'             => (string) ( $merged['emailBody'] ?? '' ),
		];
	}

	/**
	 * Check if save resume is enabled for a form.
	 *
	 * @param int $form_id The form ID.
	 * @return bool
	 * @since 2.2.0
	 */
	private function is_save_resume_enabled( $form_id ) {
		$settings = $this->get_save_resume_settings( $form_id );
		return isset( $settings['status'] ) ? wp_validate_boolean( $settings['status'] ) : false;
	}

	/**
	 * Get save resume link text.
	 *
	 * @param int $form_id The form ID.
	 * @return string
	 * @since 2.2.0
	 */
	private function get_save_resume_text( $form_id ) {
		$settings  = $this->get_save_resume_settings( $form_id );
		$link_text = isset( $settings['linkText'] ) && ! empty( $settings['linkText'] ) ? $settings['linkText'] : '';

		return $link_text ? $link_text : __( 'Save & Continue', 'sureforms-pro' );
	}

	/**
	 * Build resume URL with token.
	 *
	 * @param string $source_url The source URL.
	 * @param string $token The resume token.
	 * @return string
	 * @since 2.2.0
	 */
	private function build_resume_url( $source_url, $token ) {
		// Ensure source_url is a string.
		$source_url = is_string( $source_url ) && ! empty( $source_url ) ? $source_url : '';

		if ( empty( $source_url ) ) {
			return '';
		}

		// Parse the URL to get all components.
		$parsed_url = wp_parse_url( $source_url );

		if ( false === $parsed_url ) {
			return $source_url . '?srfm_continue_token=' . rawurlencode( $token );
		}

		// Build the base URL (scheme, host, port, path).
		$base_url = '';
		if ( isset( $parsed_url['scheme'] ) ) {
			$base_url .= $parsed_url['scheme'] . '://';
		}
		if ( isset( $parsed_url['host'] ) ) {
			$base_url .= $parsed_url['host'];
		}
		if ( isset( $parsed_url['port'] ) ) {
			$base_url .= ':' . $parsed_url['port'];
		}
		if ( isset( $parsed_url['path'] ) ) {
			$base_url .= $parsed_url['path'];
		}

		// Handle existing query parameters.
		$query_params = [];
		if ( isset( $parsed_url['query'] ) ) {
			parse_str( $parsed_url['query'], $query_params );
		}

		// Remove any existing resume token to avoid duplicates.
		unset( $query_params['srfm_continue_token'] );

		// Add our token at the end.
		$query_params['srfm_continue_token'] = $token;

		// Build the final URL with all parameters.
		$final_url = $base_url;
		if ( ! empty( $query_params ) ) {
			$final_url .= '?' . http_build_query( $query_params, '', '&', PHP_QUERY_RFC3986 );
		}

		// Add fragment if it exists.
		if ( isset( $parsed_url['fragment'] ) ) {
			$final_url .= '#' . $parsed_url['fragment'];
		}

		return $final_url;
	}

	/**
	 * Get saved form data by token.
	 *
	 * @param string $token The resume token.
	 * @return array<mixed>|false
	 * @since 2.2.0
	 */
	private function get_saved_data_by_token( $token ) {
		$saved_form = Save_Resume_Table::get( $token );

		if ( empty( $saved_form ) || ! is_array( $saved_form ) ) {
			return false;
		}

		// Check if token is expired (30 days).
		// The table uses 'date_created' column.
		$created_at = isset( $saved_form['date_created'] ) ? strtotime( $saved_form['date_created'] ) : 0;

		if ( 0 === $created_at ) {
			return false;
		}

		$expires_at   = $created_at + ( 30 * DAY_IN_SECONDS );
		$current_time = time();

		if ( $current_time > $expires_at ) {
			// Token expired, delete it.
			Save_Resume_Table::delete( $token );
			return false;
		}

		// Get form data - it's already decoded by the get() method.
		$form_data = $saved_form['form_data'] ?? [];

		// If form_data is a string, it might be encrypted.
		if ( is_string( $form_data ) ) {
			$decrypted = Helper::decrypt( $form_data );
			$form_data = maybe_unserialize( $decrypted );
		}

		return [
			'form_id'    => absint( $saved_form['form_id'] ?? 0 ),
			'form_data'  => is_array( $form_data ) ? $form_data : [],
			'token'      => $token,
			'source_url' => isset( $saved_form['source_url'] ) && is_string( $saved_form['source_url'] ) ? $saved_form['source_url'] : '',
		];
	}

	/**
	 * Generate Save & Resume link HTML with accessibility attributes.
	 *
	 * @param string $text      The link text.
	 * @param string $css_class Optional CSS class to add.
	 * @param string $href      Optional href attribute for the link.
	 * @param string $alignment Optional text alignment for the link container.
	 *
	 * @since 2.2.0
	 * @return string
	 */
	private function get_save_resume_link( $text, $css_class = '', $href = '', string $alignment = 'left' ) {
		$classes = 'srfm-save-resume-link';
		if ( ! empty( $css_class ) ) {
			$classes .= ' ' . $css_class;
		}

		// Unique ID for ARIA describedby relationship.
		$unique_id = 'srfm-save-resume-desc-' . wp_rand( 1000, 9999 );

		$text = is_string( $text ) ? $text : '';

		// If justify then align center.
		if ( 'justify' === $alignment ) {
			$alignment = 'center';
		}

		ob_start();
		?>
		<div style="width: 100%; text-align: <?php echo esc_attr( $alignment ); ?>;">
			<a tabindex="0" class="<?php echo esc_attr( $classes ); ?>"<?php echo ! empty( $href ) ? ' href="' . esc_url( $href ) . '"' : ''; ?> aria-describedby="<?php echo esc_attr( $unique_id ); ?>"><?php echo esc_html( is_string( $text ) ? $text : '' ); ?></a>
			<span id="<?php echo esc_attr( $unique_id ); ?>" class="screen-reader-text"><?php esc_html_e( 'Saves your current progress and provides a link to resume later.', 'sureforms-pro' ); ?></span>
		</div>
		<?php
		$output = ob_get_clean();
		return trim( false !== $output ? $output : '' );
	}

	/**
	 * Build complete email template with SureForms branding and logo.
	 *
	 * @param string $content Email body content.
	 * @since 2.2.0
	 * @return string Complete HTML email.
	 */
	private function build_email_template( $content ) {

		ob_start();
		?>
		<!DOCTYPE html>
		<html>
		<head>
			<meta charset="utf-8">
			<meta name="viewport" content="width=device-width, initial-scale=1.0">
			<title><?php echo esc_html__( 'Resume Your Form', 'sureforms-pro' ); ?></title>
		</head>
		<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;">
			<div style="background-color: #F1F5F9; padding: 40px 20px;">
				<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 640px; margin: 0 auto;">
					<tbody>
						<!-- Content Container -->
						<tr>
							<td>
								<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #FFFFFF;">
									<tbody>
										<tr>
											<td style="padding: 24px;">
												<?php
												echo Helper::strip_js_attributes( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Content is sanitized before being passed.
												?>
											</td>
										</tr>
									</tbody>
								</table>
							</td>
						</tr>
					</tbody>
				</table>
			</div>
		</body>
		</html>
		<?php
		$output = ob_get_clean();
		return trim( false === $output ? '' : $output );
	}

}
