<?php
/**
 * Validates and sanitizes Basic Forms submission values.
 *
 * @package BasicForms
 * @since 1.0.0
 */

namespace BasicForms;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class SubmissionValidator {

	private const DEFAULT_MAX_LENGTH = 10000;

	/**
	 * @param array<string, mixed> $field
	 * @param mixed                $raw_value
	 * @return array{0: mixed, 1: ?string} Tuple of sanitized value and error message.
	 */
	public static function validateField( array $field, mixed $raw_value ): array {
		$type  = (string) ( $field['type'] ?? 'text' );
		$label = self::fieldLabel( $field );

		if ( in_array( $type, [ 'separator', 'group', 'html' ], true ) ) {
			return [ null, null ];
		}

		if ( $type === 'checkbox' ) {
			return self::validateCheckbox( $field, $raw_value, $label );
		}

		if ( is_array( $raw_value ) ) {
			return [ null, sprintf( __( '%s is invalid.', 'wp-powersuite' ), $label ) ];
		}

		$value = is_string( $raw_value ) ? wp_unslash( $raw_value ) : (string) $raw_value;
		$value = trim( $value );

		if ( ! empty( $field['required'] ) && $value === '' && $type !== 'file' ) {
			return [ '', sprintf( __( '%s is required.', 'wp-powersuite' ), $label ) ];
		}

		if ( $value === '' ) {
			return [ '', null ];
		}

		if ( ! empty( $field['maxlength'] ) && strlen( $value ) > (int) $field['maxlength'] ) {
			return [ '', sprintf( __( '%s is too long.', 'wp-powersuite' ), $label ) ];
		}

		$max_len = (int) apply_filters( 'wpps_basic_forms_max_field_length', self::DEFAULT_MAX_LENGTH, $field );
		if ( $max_len > 0 && strlen( $value ) > $max_len ) {
			return [ '', sprintf( __( '%s is too long.', 'wp-powersuite' ), $label ) ];
		}

		switch ( $type ) {
			case 'email':
				if ( ! is_email( $value ) ) {
					return [ '', sprintf( __( '%s must be a valid email address.', 'wp-powersuite' ), $label ) ];
				}
				return [ sanitize_email( $value ), null ];

			case 'url':
				$url = esc_url_raw( $value );
				if ( $url === '' || ! wp_http_validate_url( $url ) ) {
					return [ '', sprintf( __( '%s must be a valid URL.', 'wp-powersuite' ), $label ) ];
				}
				return [ $url, null ];

			case 'number':
				if ( ! is_numeric( $value ) ) {
					return [ '', sprintf( __( '%s must be a number.', 'wp-powersuite' ), $label ) ];
				}
				$num = (float) $value;
				if ( isset( $field['min'] ) && $field['min'] !== '' && $num < (float) $field['min'] ) {
					return [ '', sprintf( __( '%s is below the minimum allowed value.', 'wp-powersuite' ), $label ) ];
				}
				if ( isset( $field['max'] ) && $field['max'] !== '' && $num > (float) $field['max'] ) {
					return [ '', sprintf( __( '%s exceeds the maximum allowed value.', 'wp-powersuite' ), $label ) ];
				}
				return [ sanitize_text_field( $value ), null ];

			case 'phone':
				$phone = preg_replace( '/[^\d+\s().-]/', '', $value );
				if ( strlen( preg_replace( '/\D/', '', $phone ) ) < 4 ) {
					return [ '', sprintf( __( '%s must be a valid phone number.', 'wp-powersuite' ), $label ) ];
				}
				return [ sanitize_text_field( $phone ), null ];

			case 'select':
			case 'radio':
				$opts = self::fieldOptions( $field );
				if ( $opts !== [] && ! in_array( $value, $opts, true ) ) {
					return [ '', sprintf( __( '%s contains an invalid selection.', 'wp-powersuite' ), $label ) ];
				}
				return [ sanitize_text_field( $value ), null ];

			case 'textarea':
				return [ sanitize_textarea_field( $value ), null ];

			case 'hidden':
				return [ sanitize_text_field( $value ), null ];

			case 'password':
				if ( ! empty( $field['minlength'] ) && strlen( $value ) < (int) $field['minlength'] ) {
					return [ '', sprintf( __( '%s must be at least %d characters.', 'wp-powersuite' ), $label, (int) $field['minlength'] ) ];
				}
				return [ sanitize_text_field( $value ), null ];

			case 'date':
			case 'time':
			case 'datetime':
			case 'datetime-local':
				if ( ! self::isValidDateTimeInput( $type, $value ) ) {
					return [ '', sprintf( __( '%s must be a valid date or time.', 'wp-powersuite' ), $label ) ];
				}
				return [ sanitize_text_field( $value ), null ];

			default:
				return [ sanitize_text_field( $value ), null ];
		}
	}

	/**
	 * Build phone value from country + number POST fields when hidden value is empty.
	 *
	 * @param array<string, mixed> $field
	 */
	public static function resolvePhoneValue( array $field, string $field_name ): string {
		$hidden = isset( $_POST[ $field_name ] ) ? trim( (string) wp_unslash( $_POST[ $field_name ] ) ) : '';
		if ( $hidden !== '' ) {
			return $hidden;
		}

		$country_key = $field_name . '_country';
		$number_key  = $field_name . '_number';
		$country     = isset( $_POST[ $country_key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $country_key ] ) ) : '';
		$number      = isset( $_POST[ $number_key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $number_key ] ) ) : '';
		$number      = trim( $number );

		if ( $number === '' ) {
			return '';
		}

		$country = preg_replace( '/\D/', '', $country );
		return $country !== '' ? '+' . $country . ' ' . $number : $number;
	}

	/**
	 * @param array<string, mixed> $field
	 */
	private static function validateCheckbox( array $field, mixed $raw_value, string $label ): array {
		$opts = self::fieldOptions( $field );

		if ( count( $opts ) > 1 ) {
			$selected = is_array( $raw_value ) ? $raw_value : ( $raw_value !== '' ? [ $raw_value ] : [] );
			$clean    = [];
			foreach ( $selected as $item ) {
				$item = sanitize_text_field( (string) wp_unslash( $item ) );
				if ( $item === '' ) {
					continue;
				}
				if ( ! in_array( $item, $opts, true ) ) {
					return [ null, sprintf( __( '%s contains an invalid selection.', 'wp-powersuite' ), $label ) ];
				}
				$clean[] = $item;
			}
			if ( ! empty( $field['required'] ) && $clean === [] ) {
				return [ '—', sprintf( __( '%s is required.', 'wp-powersuite' ), $label ) ];
			}
			$value = $clean === [] ? '—' : implode( ', ', $clean );
			return [ $value, null ];
		}

		$is_checked = ( $raw_value !== '' && $raw_value !== [] && $raw_value !== null );
		if ( ! empty( $field['required'] ) && ! $is_checked ) {
			return [ '—', sprintf( __( '%s is required.', 'wp-powersuite' ), $label ) ];
		}
		$cb_label = ! empty( $field['checkbox_label'] ) ? $field['checkbox_label'] : ( ! empty( $field['label'] ) ? $field['label'] : __( 'I agree', 'wp-powersuite' ) );
		return [ $is_checked ? sanitize_text_field( (string) $cb_label ) : '—', null ];
	}

	/**
	 * @param array<string, mixed> $field
	 * @return list<string>
	 */
	private static function fieldOptions( array $field ): array {
		if ( empty( $field['options'] ) ) {
			return [];
		}
		return array_values( array_filter( array_map( 'trim', explode( "\n", (string) $field['options'] ) ) ) );
	}

	/**
	 * @param array<string, mixed> $field
	 */
	private static function fieldLabel( array $field ): string {
		$label = (string) ( $field['label'] ?? '' );
		if ( $label === '' ) {
			return sprintf( __( 'Field %s', 'wp-powersuite' ), $field['id'] ?? '' );
		}
		return sanitize_text_field( $label );
	}

	private static function isValidDateTimeInput( string $type, string $value ): bool {
		if ( $type === 'date' ) {
			return (bool) preg_match( '/^\d{4}-\d{2}-\d{2}$/', $value );
		}
		if ( $type === 'time' ) {
			return (bool) preg_match( '/^\d{2}:\d{2}(:\d{2})?$/', $value );
		}
		return (bool) preg_match( '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?$/', $value );
	}
}
