<?php
/**
 * Form field validator
 *
 * Provides validation rules for form fields.
 * Used by BW_Schema_Form_Builder to validate individual fields.
 *
 * Supported rules:
 * - 'required' — field must not be empty
 * - 'email' — must be valid email
 * - 'url' — must be valid URL
 * - 'phone' — must be valid phone number
 * - 'number' — must be numeric
 * - 'integer' — must be integer
 * - 'min_length:N' — minimum N characters
 * - 'max_length:N' — maximum N characters
 * - 'pattern:regex' — must match regex pattern
 * - 'matches:field_name' — must match another field value
 * - 'unique:option_name' — value must be unique in option
 *
 * @package BW_Schema
 * @since 3.0.0-babel
 */

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

class BW_Schema_Form_Validator {

	/**
	 * Validate a value against a rule
	 *
	 * @param string $rule Validation rule (e.g., 'email', 'required', 'min_length:5')
	 * @param mixed  $value Value to validate
	 * @param array  $field Field definition (for context)
	 * @return bool True if valid, false if not
	 */
	public static function validate( $rule, $value, $field = array() ) {
		// Handle rules with parameters (e.g., 'min_length:5')
		$rule_parts = explode( ':', $rule, 2 );
		$rule_name  = $rule_parts[0];
		$rule_param = $rule_parts[1] ?? null;

		switch ( $rule_name ) {
			case 'required':
				return self::validate_required( $value );

			case 'email':
				return self::validate_email( $value );

			case 'url':
				return self::validate_url( $value );

			case 'phone':
				return self::validate_phone( $value );

			case 'number':
				return self::validate_number( $value );

			case 'integer':
				return self::validate_integer( $value );

			case 'min_length':
				return self::validate_min_length( $value, $rule_param );

			case 'max_length':
				return self::validate_max_length( $value, $rule_param );

			case 'pattern':
				return self::validate_pattern( $value, $rule_param );

			default:
				// Unknown rule passes
				return true;
		}
	}

	/**
	 * Validate required field
	 *
	 * @param mixed $value Value to check
	 * @return bool True if not empty
	 */
	private static function validate_required( $value ) {
		if ( is_array( $value ) ) {
			return ! empty( $value );
		}

		return ! empty( trim( (string) $value ) );
	}

	/**
	 * Validate email
	 *
	 * @param mixed $value Value to check
	 * @return bool True if valid email
	 */
	private static function validate_email( $value ) {
		if ( empty( $value ) ) {
			return true; // Empty is OK (use 'required' for mandatory)
		}

		return (bool) is_email( $value );
	}

	/**
	 * Validate URL
	 *
	 * @param mixed $value Value to check
	 * @return bool True if valid URL
	 */
	private static function validate_url( $value ) {
		if ( empty( $value ) ) {
			return true; // Empty is OK
		}

		return (bool) filter_var( $value, FILTER_VALIDATE_URL );
	}

	/**
	 * Validate phone number
	 *
	 * Accepts format: +1-201-555-0123 or variations
	 *
	 * @param mixed $value Value to check
	 * @return bool True if valid phone format
	 */
	private static function validate_phone( $value ) {
		if ( empty( $value ) ) {
			return true; // Empty is OK
		}

		// Allow: digits, +, -, ., (, ), spaces
		return (bool) preg_match( '/^\+?[0-9\s\-\.\(\)]{10,}$/', (string) $value );
	}

	/**
	 * Validate number (float or integer)
	 *
	 * @param mixed $value Value to check
	 * @return bool True if numeric
	 */
	private static function validate_number( $value ) {
		if ( empty( $value ) ) {
			return true; // Empty is OK
		}

		return is_numeric( $value );
	}

	/**
	 * Validate integer
	 *
	 * @param mixed $value Value to check
	 * @return bool True if integer
	 */
	private static function validate_integer( $value ) {
		if ( empty( $value ) ) {
			return true; // Empty is OK
		}

		return (bool) preg_match( '/^-?\d+$/', (string) $value );
	}

	/**
	 * Validate minimum length
	 *
	 * @param mixed $value Value to check
	 * @param int   $min Minimum length
	 * @return bool True if length >= min
	 */
	private static function validate_min_length( $value, $min ) {
		if ( empty( $value ) ) {
			return true; // Empty is OK
		}

		$min = (int) $min;

		if ( is_array( $value ) ) {
			return count( $value ) >= $min;
		}

		return strlen( (string) $value ) >= $min;
	}

	/**
	 * Validate maximum length
	 *
	 * @param mixed $value Value to check
	 * @param int   $max Maximum length
	 * @return bool True if length <= max
	 */
	private static function validate_max_length( $value, $max ) {
		$max = (int) $max;

		if ( is_array( $value ) ) {
			return count( $value ) <= $max;
		}

		return strlen( (string) $value ) <= $max;
	}

	/**
	 * Validate against regex pattern
	 *
	 * @param mixed  $value Value to check
	 * @param string $pattern Regex pattern
	 * @return bool True if matches pattern
	 */
	private static function validate_pattern( $value, $pattern ) {
		if ( empty( $value ) || empty( $pattern ) ) {
			return true; // Empty is OK
		}

		return (bool) preg_match( $pattern, (string) $value );
	}

	/**
	 * Get error message for a validation rule
	 *
	 * @param string $rule Validation rule name
	 * @param string $field_label Field label (for personalized message)
	 * @param mixed  $param Rule parameter (if any)
	 * @return string Error message
	 */
	public static function get_error_message( $rule, $field_label = '', $param = null ) {
		$field_label = $field_label ?: __( 'This field', 'bw-schema' );

		switch ( $rule ) {
			case 'required':
				return sprintf( __( '%s is required.', 'bw-schema' ), $field_label );

			case 'email':
				return sprintf( __( '%s must be a valid email address.', 'bw-schema' ), $field_label );

			case 'url':
				return sprintf( __( '%s must be a valid URL.', 'bw-schema' ), $field_label );

			case 'phone':
				return sprintf( __( '%s must be a valid phone number.', 'bw-schema' ), $field_label );

			case 'number':
				return sprintf( __( '%s must be a number.', 'bw-schema' ), $field_label );

			case 'integer':
				return sprintf( __( '%s must be an integer.', 'bw-schema' ), $field_label );

			case 'min_length':
				return sprintf(
					__( '%s must be at least %d characters.', 'bw-schema' ),
					$field_label,
					(int) $param
				);

			case 'max_length':
				return sprintf(
					__( '%s must not exceed %d characters.', 'bw-schema' ),
					$field_label,
					(int) $param
				);

			case 'pattern':
				return sprintf( __( '%s format is invalid.', 'bw-schema' ), $field_label );

			default:
				return sprintf( __( '%s is invalid.', 'bw-schema' ), $field_label );
		}
	}

	/**
	 * Validate email uniqueness (within WordPress users or custom option)
	 *
	 * @param string $email Email to check
	 * @param string $option_name Option name to check (optional)
	 * @return bool True if unique
	 */
	public static function is_email_unique( $email, $option_name = null ) {
		// Check if email exists as WordPress user
		if ( email_exists( $email ) ) {
			return false;
		}

		// Check custom option if provided
		if ( $option_name ) {
			$stored_email = get_option( $option_name . '_email' );
			if ( $stored_email && $stored_email === $email ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Validate value uniqueness in an option
	 *
	 * Checks if a value already exists in a stored array/list.
	 *
	 * @param mixed  $value Value to check
	 * @param string $option_name Option name containing the list
	 * @param string $key_to_check Which key to search in (for arrays of objects)
	 * @return bool True if unique
	 */
	public static function is_value_unique( $value, $option_name, $key_to_check = null ) {
		$stored = get_option( $option_name, array() );

		if ( ! is_array( $stored ) ) {
			return true;
		}

		if ( null === $key_to_check ) {
			// Simple array search
			return ! in_array( $value, $stored, true );
		}

		// Search in array of objects/arrays
		foreach ( $stored as $item ) {
			if ( is_array( $item ) || is_object( $item ) ) {
				$item_value = is_object( $item ) ? $item->{$key_to_check} : $item[ $key_to_check ] ?? null;
				if ( $item_value === $value ) {
					return false;
				}
			}
		}

		return true;
	}
}
