<?php
/**
 * Custom Post Type - Load Class
 *
 * @since 1.10.0
 * @package sureforms-pro
 */

namespace SRFM_Pro\Inc\Business\Custom_Post_Type;

use SRFM\Inc\Database\Tables\Entries;
use SRFM\Inc\Helper;
use SRFM\Inc\Smart_Tags;
use SRFM_Pro\Inc\Helper as Pro_Helper;
use SRFM_Pro\Inc\Traits\Get_Instance;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
/**
 * Custom Post Type - Load Class
 *
 * Handles the creation of posts from form submissions.
 *
 * @since  1.10.0
 * @package sureforms-pro
 */
class Init {
	use Get_Instance;

	/**
	 * Form Submission Data.
	 *
	 * @var array<mixed>
	 * @since  1.10.0
	 */
	private $submission_data = [];

	/**
	 * CPT constructor.
	 *
	 * @since 1.10.0
	 */
	public function __construct() {
		add_action( 'srfm_register_additional_post_meta', [ $this, 'register_cpt_meta' ] );
		add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_admin_scripts' ] );
		add_action( 'wp_ajax_get_srfm_cpt_module_data', [ $this, 'get_srfm_cpt_module_data' ] );
		add_action( 'wp_ajax_get_taxonomies_as_feed_settings_fields', [ $this, 'get_taxonomies_data' ] );
		add_action( 'srfm_after_submission_process', [ $this, 'handle_post_creation' ] );
	}

	/**
	 * Register post meta for CPT.
	 * This function registers a custom meta key  `_srfm_raw_cpt_meta` for storing raw post meta data.
	 * The meta key will store data as a JSON string.
	 *
	 * @hooked srfm_register_additional_post_meta
	 * @since 1.10.0
	 * @return void
	 */
	public function register_cpt_meta() {
		register_post_meta(
			'sureforms_form',
			'_srfm_raw_cpt_meta',
			[
				'type'              => 'string',  // Will store as JSON string.
				'single'            => true,    // Store as single value.
				'show_in_rest'      => [
					'schema' => [
						'type'    => 'string',
						'context' => [ 'edit' ],
					],
				],
				// Custom callback to sanitize the data.
				'sanitize_callback' => [ Utils::class, 'cpt_feed_data_sanitizer' ],
				'auth_callback'     => static function () {
					return Helper::current_user_can();
				},
			]
		);
	}

	/**
	 * Enqueue CPT block editor script.
	 *
	 * @hooked enqueue_block_editor_assets
	 * @since 1.10.0
	 * @return void
	 */
	public function enqueue_admin_scripts() {
		$script = [
			'unique_file'        => 'srfmCustomPostType',
			'unique_handle'      => 'srfm-custom-post-type',
			'extra_dependencies' => [],
		];

		$script_dep_path = SRFM_PRO_DIR . 'dist/package/business/' . $script['unique_file'] . '.asset.php';
		$script_dep_data = file_exists( $script_dep_path )
			? include $script_dep_path
			: [
				'dependencies' => [],
				'version'      => SRFM_PRO_VER,
			];
		$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/business/' . $script['unique_file'] . '.js',
			$script_dep, // Dependencies, defined above.
			$script_dep_data['version'], // SRFM_VER.
			true // Enqueue the script in the footer.
		);

		// Localize script data.
		wp_localize_script(
			SRFM_PRO_SLUG . '-' . $script['unique_handle'],
			'srfmCptModuleLocalizedData',
			[
				'srfmCptTaxNonce'  => wp_create_nonce( 'srfm_cpt_tax_nonce' ),
				'srfmCptDataNonce' => wp_create_nonce( 'srfm_cpt_data_nonce' ),
			]
		);

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

	/**
	 * AJAX callback for getting CPT data.
	 *
	 * @hooked wp_ajax_get_srfm_cpt_module_data
	 * @since 1.10.0
	 * @return void
	 */
	public function get_srfm_cpt_module_data() {

		Helper::verify_nonce_and_capabilities( 'ajax', 'srfm_cpt_data_nonce', 'srfm_cpt_data_nonce' );

		// Prepare data to send back to the client.
		// This data includes all registered post types, available statuses, authors, and meta field.
		$data = [
			'registeredPostTypes' => self::get_all_post_types(),
			'availableStatuses'   => self::get_post_statuses_as_choices(),
			'availableUsers'      => $this->get_post_authors_as_choices(),
			'metaFieldOptions'    => self::get_meta_field_dropdown_options(),
		];

		wp_send_json_success( $data );
	}

	/**
	 * AJAX callback to return taxonomy fields for a specific post type.
	 *
	 * @hooked wp_ajax_get_taxonomies_as_feed_settings_fields
	 * @since 1.10.0
	 * @return void
	 */
	public function get_taxonomies_data() {

		Helper::verify_nonce_and_capabilities( 'ajax', 'srfm_cpt_tax_nonce', 'srfm_cpt_tax_nonce' );

		// Sanitize and validate post_type input.
		// Nonce and capabilities are verified in verify_nonce_and_capabilities.
		$post_type = isset( $_POST['post_type'] ) ? sanitize_key( $_POST['post_type'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing

		if ( empty( $post_type ) || ! post_type_exists( $post_type ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid or missing post type.', 'sureforms-pro' ) ], 400 );
		}

		// Retrieve taxonomy fields for the specified post type.
		$fields = $this->get_taxonomies_as_feed_settings_fields( $post_type );

		// Send success response.
		wp_send_json_success( $fields );
	}

	/**
	 * Get suggestions for taxonomy terms based on user input.
	 *
	 * @param string $taxonomy The taxonomy to search within.
	 * @since 1.10.0
	 * @return array<mixed> Array of term suggestions with 'id' and 'text' keys.
	 */
	public static function get_taxonomy_wise_results( $taxonomy ) {

		if ( empty( $taxonomy ) || ! is_string( $taxonomy ) ) {
			return []; // Return empty array if taxonomy is not valid.
		}

		// Validate the taxonomy exists.
		$taxonomy_object = get_taxonomy( $taxonomy );
		if ( ! $taxonomy_object ) {
			wp_send_json_error(
				[ 'message' => esc_html__( 'Invalid taxonomy specified.', 'sureforms-pro' ) ],
				400
			);
		}

		// Get terms that match the query.
		$terms = get_terms(
			[
				'taxonomy'   => $taxonomy,
				'name__like' => '',
				'hide_empty' => false,
			]
		);

		$results = [];

		// Format each matching term.
		if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
			foreach ( $terms as $term ) {
				$results[] = [
					'id'   => esc_attr( $term->slug ),
					'text' => wp_strip_all_tags( $term->name ),
				];
			}
		}

		// Option to add a custom term manually.
		$results[] = [
			'id'   => 'srfm_cpt_tax_custom',
			'text' => sprintf(
				/* translators: %s is the singular name of the taxonomy */
				esc_html__( 'Add New %s', 'sureforms-pro' ),
				esc_html( $taxonomy_object->labels->singular_name )
			),
		];

		return $results;
	}

	/**
	 * Handle post creation after form submission.
	 *
	 * @hooked srfm_after_submission_process
	 * @since 1.10.0
	 *
	 * @param array $form_data The data submitted in the form.
	 * @return void
	 */
	public function handle_post_creation( $form_data ) {
		$form_id               = (int) $form_data['form_id'] ? (int) $form_data['form_id'] : 0;
		$this->submission_data = Helper::map_slug_to_submission_data( $form_data );

		if ( empty( $form_id ) || ! is_numeric( $form_id ) ) {
			return; // Exit if form ID is invalid.
		}

		$post_feeds = $this->get_valid_post_feeds( $form_id );

		// If no valid feeds found, return early.
		if ( empty( $post_feeds ) ) {
			return;
		}

		// Initialize Smart Tags processor.
		$smart_tags = new Smart_Tags();

		foreach ( $post_feeds as $feed ) {
			// Skip the feed where status is false.
			if ( empty( $feed['status'] ) ) {
				continue;
			}

			// Check the conditions for triggering the post creation.
			if ( ! Pro_Helper::check_trigger_conditions( $feed, $this->submission_data ) ) {
				continue;
			}

			// Prepare post data from feed and form.
			$post_data = $this->prepare_post_data( $feed, $form_data, $smart_tags );

			// Insert post and get post ID.
			$post_id = wp_insert_post( $post_data );

			if ( ! is_int( $post_id ) ) {
				continue; // Skip if post insertion failed.
			}

			// Handle featured image upload.
			if ( ! empty( $feed['featuredImage']['image'] ) ) {
				$this->maybe_upload_featured_image( $feed['featuredImage'], $form_data, $post_id, $smart_tags );
			}
		}
	}

	/**
	 * Format a date string to 'Y-m-d H:i:s' format.
	 *
	 * @since 1.10.0
	 * @param string $date The date string to format.
	 * @return string Formatted date string in 'Y-m-d H:i:s' format.
	 */
	public static function get_formatted_date( $date ) {
		try {
			// if custom date is empty, use current time.
			if ( empty( trim( $date ) ) ) {
				$date = current_time( 'mysql' );
			}

			// Normalize dot-separated dates to dash-separated for compatibility.
			$normalized_date = Helper::get_string_value( preg_replace( '/(\d{4})\.(\d{2})\.(\d{2})/', '$1-$2-$3', $date ) );

			return ( new \DateTime( $normalized_date, wp_timezone() ) )->format( 'Y-m-d H:i:s' );
		} catch ( \Exception $e ) {
			// If we can't parse the date because it's invalid, set the post date to now.
			return ( new \DateTime( 'now', wp_timezone() ) )->format( 'Y-m-d H:i:s' );
		}
	}

	/**
	 * Set the featured image for a post by sideloading an image from a URL.
	 *
	 * @since 1.10.0
	 * @param string                  $url The URL of the image to sideload.
	 * @param array<string|int|mixed> $post_data Additional post data, including 'post_parent', 'alt_text', and 'caption'.
	 * @return int|false The attachment ID on success, or false on failure.
	 */
	private static function handle_media_upload( $url, $post_data = [] ) {

		if ( empty( $url ) || ! is_string( $url ) ) {
			return false; // Return false if URL is empty or not a string.
		}

		// Load required WordPress files for media handling.
		if ( ! function_exists( 'media_sideload_image' ) ) {
			require_once ABSPATH . 'wp-admin/includes/image.php';
			require_once ABSPATH . 'wp-admin/includes/media.php';
			require_once ABSPATH . 'wp-admin/includes/file.php';
		}

		$url = esc_url_raw( $url );

		// Check if the URL is allowed.
		if ( ! self::check_allowed_media_hosts( $url ) ) {
			return false; // Return false if the URL is not allowed.
		}

		// The post ID to attach the media to (or 0 for unattached).
		$post_id = $post_data['post_parent'] ? Helper::get_integer_value( $post_data['post_parent'] ) : 0;

		// Download and sideload the image.
		$attachment_id = media_sideload_image( $url, $post_id, null, 'id' );

		if ( is_wp_error( $attachment_id ) || ! is_int( $attachment_id ) ) {
			return false;
		}

		// Set alt text.
		$alt_text = Helper::get_string_value( $post_data['alt_text'] );
		if ( ! empty( $alt_text ) ) {
			update_post_meta(
				$attachment_id,
				'_wp_attachment_image_alt',
				sanitize_text_field( $alt_text )
			);
		}

		// Set caption.
		$caption = Helper::get_string_value( $post_data['caption'] );
		if ( ! empty( $caption ) ) {
			wp_update_post(
				[
					'ID'           => $attachment_id,
					'post_excerpt' => sanitize_text_field( $caption ), // Use post_excerpt for caption.
				]
			);
		}

		// Set thr thumbnail for a particular post id.
		set_post_thumbnail( $post_id, $attachment_id );

		return $attachment_id; // Return the attachment ID.
	}

	/**
	 * Check if the media URL is allowed based on the host.
	 * This function checks if the media URL's host is in the list of allowed hosts.
	 * It allows for custom conditions to be applied via filters.
	 *
	 * @param string $url The media URL to check.
	 * @return bool True if the host is allowed, false otherwise.
	 * @since 1.10.0
	 */
	private static function check_allowed_media_hosts( $url ) {
		$file_host = wp_parse_url( $url, PHP_URL_HOST );

		/**
		 * Filter to allow specific media hosts.
		 *
		 * @since 1.10.0
		 */
		$allowed_base_domains = apply_filters(
			'srfm_cpt_allowed_media_hosts',
			[ wp_parse_url( site_url(), PHP_URL_HOST ) ],
			$url,
			$file_host
		);

		// Check if allowed hosts are defined and valid.
		if ( ! is_array( $allowed_base_domains ) || empty( $allowed_base_domains ) ) {
			return false;
		}

		/**
		 * Filter to allow custom host conditions.
		 *
		 * @since 1.10.0
		 */
		$custom_conditions = apply_filters(
			'srfm_cpt_additional_media_host_conditions',
			[],
			$url,
			$file_host
		);

		// Ensure custom conditions is an array.
		if ( ! is_array( $custom_conditions ) ) {
			$custom_conditions = [];
		}

		foreach ( $allowed_base_domains as $allowed ) {
			// If host matches directly or partially.
			if ( $file_host === $allowed ) {
				return true;
			}

			// Check if any custom condition matches the URL.
			foreach ( $custom_conditions as $condition ) {
				if ( strpos( $url, $condition ) !== false ) {
					return true;
				}
			}
		}

		return false; // No match found.
	}

	/**
	 * Get all registered post types grouped into Default and Custom categories.
	 *
	 * @since 1.10.0
	 * @return array<mixed> Array of grouped post types with labels and values.
	 */
	private static function get_all_post_types() {
		$options = [];

		// Get built-in (default) public post types like 'post' and 'page'.
		$default_post_types = get_post_types(
			[
				'public'   => true,     // Only publicly visible post types.
				'_builtin' => true,     // Core WordPress types.
			],
			'objects' // Return as array of post type objects.
		);

		// Initialize array to hold default post type choices.
		$default_choices = [];

		// Loop through each built-in post type.
		if ( ! empty( $default_post_types ) && is_array( $default_post_types ) ) {
			foreach ( $default_post_types as $post_type ) {
				// Skip if not an object or if it's the 'attachment' post type.
				if ( ! is_object( $post_type ) || 'attachment' === $post_type->name ) {
					continue;
				}

				// Add label/value pair for each post type.
				$default_choices[] = [
					'label' => esc_html( $post_type->labels->singular_name ),
					'value' => esc_attr( $post_type->name ),
				];
			}

			// If default choices were found, add them under a grouped label.
			if ( ! empty( $default_choices ) ) {
				$options[] = [
					'label'   => esc_html__( 'WordPress Post Types', 'sureforms-pro' ),
					'choices' => $default_choices,
				];
			}
		}

		// Get custom (non-built-in) public post types.
		$custom_post_types = get_post_types(
			[
				'public'   => true,     // Publicly visible.
				'_builtin' => false,    // Only user-defined/custom types.
			],
			'objects'
		);

		// Initialize array to hold custom post type choices.
		$custom_choices = [];

		// Loop through each custom post type.
		if ( ! empty( $custom_post_types ) && is_array( $custom_post_types ) ) {
			foreach ( $custom_post_types as $post_type ) {
				// Skip if not an object (shouldn't happen, but safe check).
				if ( ! is_object( $post_type ) ) {
					continue;
				}

				// Add label/value pair for each custom post type.
				$custom_choices[] = [
					'label' => esc_html( $post_type->labels->singular_name ),
					'value' => esc_attr( $post_type->name ),
				];
			}

			// If custom choices were found, add them under a grouped label.
			if ( ! empty( $custom_choices ) ) {
				$options[] = [
					'label'   => esc_html__( 'Custom Post Types', 'sureforms-pro' ),
					'choices' => $custom_choices,
				];
			}
		}

		// Return the complete list of grouped post type options.
		return $options;
	}

	/**
	 * Get all available post statuses as choices.
	 *
	 * @since 1.10.0
	 * @return array<mixed> Array of post statuses with labels and values.
	 */
	private static function get_post_statuses_as_choices() {
		$post_statuses = get_post_statuses();

		// convert post statuses to array of choices.
		return array_map(
			static function ( $status, $label ) {
				return [
					'label' => esc_html( $label ),
					'value' => esc_attr( $status ),
				];
			},
			array_keys( $post_statuses ),
			$post_statuses
		);
	}

	/**
	 * Get suggestions for post author field.
	 *
	 * @since 1.10.0
	 * @return array<mixed> Array of post statuses with labels and values.
	 */
	private static function get_post_authors_as_choices() {

		// Initialize choices.
		$choices = [
			[
				'label' => esc_html__( 'Logged In User', 'sureforms-pro' ),
				'value' => 'logged_in_user',
			],
		];

		// Get all users.
		$user_query = get_users(
			[
				'fields'  => [ 'ID', 'display_name' ],
				'orderby' => 'display_name',
				'order'   => 'ASC',
				'number'  => -1, // Get all users.
			]
		);

		// Loop through users and add them as choices.
		foreach ( $user_query as $user ) {

			// Add user to choices.
			$choices[] = [
				'label' => esc_html( $user->display_name ),
				'value' => esc_attr( $user->ID ),
			];

		}

		return $choices;
	}

	/**
	 * Get all custom meta keys registered and return them the label/value pairs
	 * for use in dropdowns.
	 *
	 * @since 1.10.0
	 * @return array<int<0, max>,array<string, string>> Array of custom meta keys.
	 */
	private static function get_meta_field_dropdown_options() {
		$meta_keys = self::get_all_custom_meta_keys();

		$options = [];

		// Add each meta key.
		foreach ( $meta_keys as $key ) {
			$options[] = [
				'label' => esc_html( $key ),
				'value' => esc_attr( $key ),
			];
		}

		return $options;
	}

	/**
	 * Get all meta keys registered in WordPress using the postmeta table.
	 *
	 * @since 1.10.0
	 * @return array<string> Array of all meta keys.
	 */
	private static function get_all_custom_meta_keys() {
		global $wpdb;

		$cache_key   = 'srfm_cpt_meta_keys_list';
		$cache_group = 'srfm_cpt_meta_keys';

		// Try to get the result from cache first.
		$meta_keys = wp_cache_get( $cache_key, $cache_group );
		if ( false === $meta_keys ) {
			/**
			 * Get all distinct meta keys from the postmeta table.
			 * Exclude keys that start with an underscore (private meta keys).
			 * Also exclude keys that are between '_' and '_z' to avoid reserved keys.
			 */

			// Reason: Direct DB query is required to get all distinct meta keys. There is no core API for this query,
			// and the query is safely prepared and cached to mitigate performance and security concerns.
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
			$meta_keys = $wpdb->get_col(
				$wpdb->prepare(
					"SELECT DISTINCT meta_key
					FROM {$wpdb->prefix}postmeta
					WHERE meta_key NOT BETWEEN '_' AND '_z'
					HAVING meta_key NOT LIKE %s
					ORDER BY meta_key",
					$wpdb->esc_like( '_' ) . '%'
				)
			);

			// Store the result in cache.
			wp_cache_set( $cache_key, $meta_keys, $cache_group, HOUR_IN_SECONDS );
		}

		return $meta_keys;
	}

	/**
	 * Get taxonomies for a specific post type and return them as settings fields.
	 *
	 * @since 1.10.0
	 * @param string $post_type The post type to get taxonomies for.
	 * @return array<mixed> Array of taxonomy fields with labels and values.
	 */
	private static function get_taxonomies_as_feed_settings_fields( $post_type ) {

		// Setup fields array.
		$fields = [];

		// Get the taxonomies.
		$taxonomies = get_object_taxonomies( $post_type, 'objects' );

		// If no taxonomies found, return empty array.
		if ( empty( $taxonomies ) || ! is_array( $taxonomies ) ) {
			return $fields; // Return empty array if no taxonomies found.
		}

		// Prepare fields.
		foreach ( $taxonomies as $taxonomy ) {

			if (
			! is_object( $taxonomy ) ||
			! property_exists( $taxonomy, 'name' ) ||
			! property_exists( $taxonomy, 'label' ) ||
			! property_exists( $taxonomy, 'labels' ) ||
			! isset( $taxonomy->labels->singular_name )
			) {
				continue;
			}

			// Ignore post format taxonomy.
			// Post format is a special taxonomy that is not suitable for this.
			if ( 'post_format' === $taxonomy->name ) {
				continue;
			}

			$terms = self::get_taxonomy_wise_results( $taxonomy->name );

			// Add field.
			$fields[] = [
				'singularLabel' => $taxonomy->labels->singular_name,
				'label'         => $taxonomy->label,
				'taxonomy'      => $taxonomy->name,
				'terms'         => [
					$taxonomy->name => ! empty( $terms ) ? $terms : [],
				],
			];
		}

		// Return fields.
		return $fields;
	}

	/**
	 * Retrieve and validate post feeds from post meta.
	 *
	 * @since 1.10.0
	 *
	 * @param int $form_id The form ID.
	 * @return array Valid post feeds.
	 */
	private function get_valid_post_feeds( $form_id ) {
		$post_feeds = get_post_meta( $form_id, '_srfm_raw_cpt_meta', true );

		if ( empty( $post_feeds ) || ! is_string( $post_feeds ) ) {
			return []; // Return empty array if no feeds found or invalid type.
		}

		$post_feeds = json_decode( $post_feeds, true );

		// Return only if feeds are a valid array.
		return ! empty( $post_feeds ) && is_array( $post_feeds ) ? $post_feeds : [];
	}

	/**
	 * Prepare meta input data from user meta settings.
	 *
	 * @since 1.10.0
	 *
	 * @param array  $user_meta  User meta configuration.
	 * @param array  $form_data  Form data.
	 * @param object $smart_tags Smart tags processor.
	 * @return array Meta input array.
	 */
	private function prepare_meta_input( $user_meta, $form_data, $smart_tags ) {
		$meta_input = [];

		if ( ! empty( $user_meta ) && is_array( $user_meta ) ) {
			foreach ( $user_meta as $meta_field ) {
				if ( isset( $meta_field['key'], $meta_field['value'] ) ) {
					$meta_input[ sanitize_key( $meta_field['key'] ) ] = sanitize_text_field(
						method_exists( $smart_tags, 'process_smart_tags' )
						? $smart_tags->process_smart_tags( $meta_field['value'] ?? '', $form_data )
						: $meta_field['value']
					);
				}
			}
		}

		return $meta_input;
	}

	/**
	 * Prepare taxonomy assignment input from settings.
	 *
	 * @since 1.10.0
	 *
	 * @param array  $taxonomies Taxonomy settings.
	 * @param array  $form_data  Form data.
	 * @param object $smart_tags Smart tags processor.
	 * @return array Tax input array.
	 */
	private function prepare_tax_input( $taxonomies, $form_data, $smart_tags ) {
		$tax_input = [];

		if ( ! empty( $taxonomies ) && is_array( $taxonomies ) ) {
			foreach ( $taxonomies as $taxonomy ) {
				if ( isset( $taxonomy['term'], $taxonomy['value'] ) ) {
					$term  = sanitize_key( $taxonomy['term'] );
					$value = method_exists( $smart_tags, 'process_smart_tags' )
						? $smart_tags->process_smart_tags( $taxonomy['value']['id'], $form_data )
						: $taxonomy['value']['id'];

					/**
					 * Split the value by '|' if it contains multiple terms.
					 * This allows for multiple terms to be assigned if dropdown multiple is enabled.
					 * If no '|' is found, treat it as a single term.
					 */
					if ( strpos( $value, '|' ) !== false ) {
						$values = explode( '|', $value );
					} else {
						$values = [ $value ];
					}

					foreach ( $values as $value ) {
						$value = trim( $value ); // Trim whitespace.

						if ( empty( $value ) || ! taxonomy_exists( $term ) ) {
							continue;
						}

						$term_obj = get_term_by( 'name', $value, $term );

						if ( is_wp_error( $term_obj ) ) {
							continue;
						}

						if ( ! $term_obj ) {
							$new_term = wp_insert_term( $value, $term );

							if ( is_wp_error( $new_term ) ) {
								continue; // Skip if term creation failed.
							}

							$term_id = is_array( $new_term ) && isset( $new_term['term_id'] ) ? (int) $new_term['term_id'] : 0;
						} else {

							$term_id = is_a( $term_obj, 'WP_Term' ) ? $term_obj->term_id : (int) $term_obj;
						}

						$tax_input[ $term ][] = $term_id;

					}
				}
			}
		}

		return $tax_input;
	}

	/**
	 * Compile post data from feed settings and form data.
	 *
	 * @since 1.10.0
	 *
	 * @param array  $feed       Single feed config.
	 * @param array  $form_data  Form data.
	 * @param object $smart_tags Smart tags processor.
	 * @return array Post data array for insertion.
	 */
	private function prepare_post_data( $feed, $form_data, $smart_tags ) {
		$post_type  = $feed['postSettings']['postType'] ?? 'post';
		$meta_input = $this->prepare_meta_input( $feed['userMeta'] ?? [], $form_data, $smart_tags );
		$tax_input  = $this->prepare_tax_input( $feed['taxonomies'] ?? [], $form_data, $smart_tags );

		$post_data = [
			'visibility'  => sanitize_text_field( $feed['postSettings']['visibility'] ?? 'public' ),
			'post_type'   => sanitize_text_field( $post_type ),
			'post_status' => sanitize_text_field( $feed['postSettings']['status'] ?? 'draft' ),
			'post_title'  => sanitize_text_field(
				method_exists( $smart_tags, 'process_smart_tags' )
				? $smart_tags->process_smart_tags( $feed['postGeneral']['title'] ?? '', $form_data )
				: $feed['postGeneral']['title']
			),

			'meta_input'  => $meta_input,
			'tax_input'   => $tax_input,
		];

		// if post title is empty, set it to 'Untitled'.
		if ( empty( $post_data['post_title'] ) ) {
			$post_data['post_title'] = esc_html__( 'Untitled', 'sureforms-pro' );
		}

		// Handle visibility protection.
		if ( 'protected' === $feed['postSettings']['visibility'] && ! empty( $feed['postSettings']['password'] ) ) {
			$post_data['post_password'] = method_exists( $smart_tags, 'process_smart_tags' )
				? $smart_tags->process_smart_tags( $feed['postSettings']['password'] ?? '', $form_data )
				: $feed['postSettings']['password'];
		}

		// Handle post date.
		// Get the custom date.
		$custom_date = $feed['postSettings']['customDate'] ?? '';
		// Check if we need to use custom date.
		$use_custom = isset( $feed['postSettings']['date'] ) && 'custom_date' === $feed['postSettings']['date'] && ! empty( $custom_date );

		// Set post date based on custom date or submission date.
		if ( $use_custom ) {
			$hours    = $feed['postSettings']['hours'] ?? '12';
			$minutes  = $feed['postSettings']['minutes'] ?? '00';
			$meridiem = $feed['postSettings']['meridiem'] ?? 'AM';

			$time_stamp = strtotime( $custom_date . ' ' . $hours . ':' . $minutes . ' ' . $meridiem );

			if ( false === $time_stamp ) {
				// If strtotime fails, use current time.
				$time_stamp = current_time( 'mysql' );
			}

			// convert to int.
			$time_stamp = (int) $time_stamp;

			$post_data['post_date']     = gmdate( 'Y-m-d H:i:s', $time_stamp );
			$post_data['post_date_gmt'] = get_gmt_from_date( $post_data['post_date'] );
		} else {
			$submission_id = $form_data['submission_id'] ? Helper::get_integer_value( $form_data['submission_id'] ) : 0;

			if ( is_numeric( $submission_id ) && $submission_id ) {
				$submission_post            = Entries::get( $submission_id );
				$post_data['post_date']     = $submission_post['created_at'] ?? current_time( 'mysql' );
				$post_data['post_date_gmt'] = $submission_post['created_at'] ?? current_time( 'mysql', true );
				$post_data['post_date_gmt'] = get_gmt_from_date( Helper::get_string_value( $post_data['post_date_gmt'] ) );
			}
		}

		// Handle author assignment.
		if ( ! empty( $feed['postSettings']['author'] ) ) {
			$post_data['post_author'] = 'logged_in_user' === $feed['postSettings']['author'] ? get_current_user_id() : intval( $feed['postSettings']['author'] );
		}

		// Add content if provided.
		if ( ! empty( $feed['postGeneral']['content'] ) ) {
			$content = method_exists( $smart_tags, 'process_smart_tags' )
				? $smart_tags->process_smart_tags( $feed['postGeneral']['content'] ?? '', $form_data )
				: $feed['postGeneral']['content'];

			$post_data['post_content'] = Helper::sanitize_textarea( $content );
		}

		// Add excerpt if provided.
		if ( ! empty( $feed['postGeneral']['excerpt'] ) ) {
			$excerpt = method_exists( $smart_tags, 'process_smart_tags' )
			? $smart_tags->process_smart_tags( $feed['postGeneral']['excerpt'] ?? '', $form_data )
			: $feed['postGeneral']['excerpt'];

			$post_data['post_excerpt'] = sanitize_text_field( $excerpt );
		}

		return $post_data;
	}

	/**
	 * Handle uploading of featured image to the post.
	 *
	 * @since 1.10.0
	 *
	 * @param array  $featured_image Image settings array.
	 * @param array  $form_data      Form data.
	 * @param int    $post_id        Post ID.
	 * @param object $smart_tags     Smart tags processor.
	 *
	 * @return void
	 */
	private function maybe_upload_featured_image( $featured_image, &$form_data, $post_id, $smart_tags ) {
		$form_data['upload_format_type'] = 'raw';

		$image_url = method_exists( $smart_tags, 'process_smart_tags' )
			? $smart_tags->process_smart_tags( $featured_image['image'] ?? '', $form_data, $form_data )
			: $featured_image['image'];
		$alt_text  = method_exists( $smart_tags, 'process_smart_tags' )
			? $smart_tags->process_smart_tags( $featured_image['altText'] ?? '', $form_data )
			: $featured_image['altText'] ?? '';
		$caption   = method_exists( $smart_tags, 'process_smart_tags' )
			? $smart_tags->process_smart_tags( $featured_image['caption'] ?? '', $form_data )
			: $featured_image['caption'] ?? '';

		self::handle_media_upload(
			$image_url,
			[
				'post_parent' => $post_id ?? 0,
				'alt_text'    => $alt_text,
				'caption'     => $caption,
			]
		);
	}

}
