<?php
/**
 * Field Validation Class file.
 *
 * Handles all field validation for SureForms Pro.
 *
 * @package SureForms Pro
 * @since 1.12.2
 */

namespace SRFM_Pro\Inc\Extensions;

use SRFM\Inc\Helper;
use SRFM\Inc\Translatable;
use SRFM_Pro\Inc\Traits\Get_Instance;

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

/**
 * Field Validation Class
 */
class Field_Validation {
	use Get_Instance;

	/**
	 * Constructor
	 */
	public function __construct() {
		add_filter( 'srfm_field_validation_data', [ $this, 'prepare_field_validation_data' ], 10, 1 );
		add_filter( 'srfm_block_config', [ $this, 'add_block_config' ], 10, 1 );
		add_filter( 'srfm_validate_form_data', [ $this, 'validate_field_data' ], 10, 1 );
	}

	/**
	 * Prepare field validation data by merging uploaded files into form data.
	 *
	 * This function checks if there are any uploaded files in the $_FILES superglobal.
	 * If so, it merges the $_FILES array into the provided $form_data array.
	 * This ensures that file upload fields are included in the validation process.
	 *
	 * Example:
	 * - If $form_data = [ 'field1' => 'value1' ] and $_FILES = [ 'upload1' => [ ... ] ],
	 *   the result will be [ 'field1' => 'value1', 'upload1' => [ ... ] ].
	 *
	 * @param array $form_data The submitted form data.
	 * @return array The merged form data including uploaded files.
	 */
	public function prepare_field_validation_data( $form_data ) {
		// If there are uploaded files, merge them into the form data.
		// Example: $_FILES contains file fields, which are added to $form_data for validation.
		if ( ! empty( $_FILES ) ) {
			$form_data = array_merge( $form_data, $_FILES );
		}

		// Return the merged form data, now including any uploaded files.
		return $form_data;
	}

	/**
	 * Process and add configuration for form blocks.
	 *
	 * Processes block configuration, specifically handling upload field blocks.
	 * Validates and extracts relevant attributes from the block and returns a processed
	 * configuration array.
	 *
	 * @param array $block {
	 *     Block data array.
	 *     @type array $block {
	 *         The actual block data.
	 *         @type string $blockName The name of the block.
	 *         @type array  $attrs     Block attributes.
	 *     }
	 * }
	 * @return array|null Returns processed block config array or null if not processed
	 * @since 1.12.2
	 */
	public function add_block_config( $block ) {
		if ( ! isset( $block['block'] ) || ! is_array( $block['block'] ) ) {
			return null;
		}

		$block = $block['block'];

		if ( ! isset( $block['blockName'] ) || ! isset( $block['attrs'] ) ) {
			return null;
		}

		$block_name  = sanitize_text_field( $block['blockName'] );
		$block_attrs = $block['attrs'];

		$processed_value = [];

		// Example: If the block is an upload field, extract and validate its configuration.
		if ( 'srfm/upload' === $block_name ) {
			// Extract and validate upload field configuration attributes.
			$is_required     = isset( $block_attrs['required'] ) ? (bool) $block_attrs['required'] : false;
			$is_multiple     = isset( $block_attrs['multiple'] ) ? (bool) $block_attrs['multiple'] : false;
			$file_size_limit = isset( $block_attrs['fileSizeLimit'] ) ? absint( $block_attrs['fileSizeLimit'] ) : 10;
			$allowed_formats = isset( $block_attrs['allowedFormats'] ) && is_array( $block_attrs['allowedFormats'] )
				? $block_attrs['allowedFormats']
				: [];
			$max_files       = isset( $block_attrs['maxFiles'] ) ? absint( $block_attrs['maxFiles'] ) : 2;
			$slug            = isset( $block_attrs['slug'] ) ? sanitize_text_field( $block_attrs['slug'] ) : '';
			$block_id        = isset( $block_attrs['block_id'] ) ? sanitize_text_field( $block_attrs['block_id'] ) : '';

			// Example: $processed_value will contain all validated and sanitized attributes.
			$processed_value = [
				'blockName'      => $block_name,
				'required'       => $is_required,
				'multiple'       => $is_multiple,
				'fileSizeLimit'  => $file_size_limit,
				'allowedFormats' => $allowed_formats,
				'maxFiles'       => $max_files,
				'slug'           => $slug,
				'block_id'       => $block_id,
			];
		}

		// Example: If $processed_value is not empty, return it wrapped in 'processed_value' key.
		if ( ! empty( $processed_value ) ) {
			return [ 'processed_value' => $processed_value ];
		}

		return null;
	}

	/**
	 * Validate upload field data for SureForms.
	 *
	 * This function validates the uploaded file(s) for a given upload field, checking:
	 * - Required field presence (e.g., if a file is required but not uploaded, validation fails)
	 * - Allowed file types (e.g., only jpg, png, pdf, etc. are accepted; 'exe' would be rejected)
	 * - Maximum number of files (e.g., if maxFiles is 2, uploading 3 files will fail)
	 * - Maximum file size (e.g., if fileSizeLimit is 10MB, a 12MB file will fail)
	 *
	 * @param array<mixed> $field_data Field data.
	 * @return array|void
	 */
	public function validate_field_data( $field_data ) {
		// Validate input structure.
		// Example: $field_data must be an array and contain required keys.
		if (
			! is_array( $field_data ) ||
			! isset( $field_data['field_key'], $field_data['field_value'], $field_data['form_config'], $field_data['block_id'], $field_data['name_with_id'], $field_data['field_name'], $field_data['block_slug'] )
		) {
			return;
		}

		$field_value  = $field_data['field_value'];
		$block_id     = $field_data['block_id'];
		$form_config  = $field_data['form_config'];
		$name_with_id = $field_data['name_with_id'];
		$field_name   = $field_data['field_name'];
		$block_slug   = $field_data['block_slug'];

		// Only process upload fields with array value.
		// Example: Only proceed if $field_name is 'srfm-upload'.
		if ( 'srfm-upload' !== $field_name ) {
			return;
		}

		$return_data = [
			'validated' => false,
			'error'     => __( 'Field is not valid.', 'sureforms-pro' ),
		];

		if ( ! isset( $form_config[ $block_id ] ) ) {
			return $return_data;
		}

		// Example: $form_config[$block_id] should contain config for this field.
		$get_form_config = $form_config[ $block_id ];

		// Example: Config must have 'slug' and 'name_with_id' keys.
		if ( ! isset( $get_form_config['slug'] ) || ! isset( $get_form_config['name_with_id'] ) ) {
			return $return_data;
		}

		// Verify the slug.
		if ( $get_form_config['slug'] !== $block_slug ) {
			return $return_data;
		}

		// "name_with_id" should be same as $name_with_id.
		// Example: If config's name_with_id is 'upload-123', $name_with_id must also be 'upload-123'.
		if ( $get_form_config['name_with_id'] !== $name_with_id ) {
			return $return_data;
		}

		$is_required     = ! empty( $get_form_config['required'] );
		$is_multiple     = ! empty( $get_form_config['multiple'] );
		$file_size_limit = isset( $get_form_config['fileSizeLimit'] ) ? absint( $get_form_config['fileSizeLimit'] ) : 10;
		$file_size_limit = $file_size_limit * 1024 * 1024; // Convert MB to bytes. Example: 10 => 10485760 bytes.
		$allowed_formats = isset( $get_form_config['allowedFormats'] ) && is_array( $get_form_config['allowedFormats'] ) && ! empty( $get_form_config['allowedFormats'] )
			? $get_form_config['allowedFormats']
			: [
				[
					'value' => 'jpg',
					'label' => 'jpg',
				],
				[
					'value' => 'jpeg',
					'label' => 'jpeg',
				],
				[
					'value' => 'gif',
					'label' => 'gif',
				],
				[
					'value' => 'png',
					'label' => 'png',
				],
				[
					'value' => 'pdf',
					'label' => 'pdf',
				],
			];

		$max_files = isset( $get_form_config['maxFiles'] ) ? absint( $get_form_config['maxFiles'] ) : 1;

		// Check for required field.
		// Example: If required and no file uploaded, return error.
		if ( $is_required ) {
			if ( ! isset( $field_value['name'] ) || ! is_array( $field_value['name'] ) || empty( $field_value['name'][0] ) ) {
				return [
					'validated' => false,
					'error'     => Helper::get_common_err_msg()['required'],
				];
			}
		}

		// Validate file types.
		// Example: Only allow files with types in $allowed_formats (e.g., 'jpg', 'png').
		if ( ! empty( $field_value['type'] ) && is_array( $field_value['type'] ) ) {
			// Normalize allowed formats to a flat array of values.
			// Example: $allowed_types = ['jpg', 'png', 'pdf'].
			$allowed_types = [];
			foreach ( $allowed_formats as $format ) {
				if ( is_array( $format ) && isset( $format['value'] ) ) {
					$allowed_types[] = strtolower( $format['value'] );
				} elseif ( is_string( $format ) ) {
					$allowed_types[] = strtolower( $format );
				}
			}
			foreach ( $field_value['type'] as $file_type ) {
				// Extract the file extension from the MIME type (e.g., "image/jpeg" => "jpeg").
				// Skip if no file uploaded.
				if ( empty( $file_type ) ) {
					continue;
				}
				$file_type_parts = explode( '/', $file_type );
				$file_type       = isset( $file_type_parts[1] ) ? strtolower( $file_type_parts[1] ) : '';
				// Example: If $file_type is 'exe', and not in $allowed_types, return error.
				if ( $file_type && ! in_array( $file_type, $allowed_types, true ) ) {
					return [
						'validated' => false,
						'error'     => Translatable::dynamic_validation_messages()['srfm_file_type_not_allowed'],
					];
				}
			}
		}

		// Validate multiple files.
		// Example: If not multiple and more than one file uploaded, return error.
		if (
			isset( $field_value['name'] ) &&
			is_array( $field_value['name'] ) &&
			! $is_multiple &&
			count( $field_value['name'] ) > 1
		) {
			return [
				'validated' => false,
				'error'     => sprintf( Translatable::dynamic_validation_messages()['srfm_file_upload_limit'], 1 ),
			];
		}

		// Validate max files.
		// Example: If maxFiles is 2 and 3 files uploaded, return error.
		if (
			isset( $field_value['name'] ) &&
			is_array( $field_value['name'] ) &&
			$max_files < count( $field_value['name'] )
		) {
			return [
				'validated' => false,
				'error'     => sprintf( Translatable::dynamic_validation_messages()['srfm_file_upload_limit'], $max_files ),
			];
		}

		// Validate file sizes.
		// Example: If fileSizeLimit is 10MB and a file is 12MB, return error.
		if (
			! empty( $field_value['size'] ) &&
			is_array( $field_value['size'] )
		) {
			foreach ( $field_value['size'] as $file_size ) {
				if ( $file_size_limit < $file_size ) {
					$convert_to_mb = $file_size / 1024 / 1024;
					return [
						'validated' => false,
						'error'     => sprintf( Translatable::dynamic_validation_messages()['srfm_file_size_exceed'], $convert_to_mb ),
					];
				}
			}
		}

		return [
			'validated' => true,
			'error'     => '',
		];
	}
}
