<?php
/**
 * Validator class for input validation.
 *
 * @package BoostBot
 * @since   1.0.0
 */

// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Class BoostBot_Validator
 *
 * Provides validation methods for user input.
 *
 * @since 1.0.0
 */
class BoostBot_Validator {

    /**
     * Validate an email address.
     *
     * @param string $email The email to validate.
     * @return bool True if valid, false otherwise.
     */
    public static function is_valid_email( $email ) {
        return is_email( $email ) !== false;
    }

    /**
     * Validate a phone number.
     *
     * Accepts Israeli phone formats and international formats.
     *
     * @param string $phone The phone number to validate.
     * @return bool True if valid, false otherwise.
     */
    public static function is_valid_phone( $phone ) {
        // Remove spaces, dashes, and parentheses
        $phone = preg_replace( '/[\s\-\(\)]/', '', $phone );

        // Israeli mobile: 05X-XXXXXXX or +972-5X-XXXXXXX
        // Israeli landline: 0X-XXXXXXX or +972-X-XXXXXXX
        // International: starts with + followed by digits
        $patterns = array(
            '/^0[23489]\d{7}$/',        // Israeli landline
            '/^05[0-9]\d{7}$/',          // Israeli mobile
            '/^\+972[23489]\d{7}$/',     // Israeli landline international
            '/^\+9725[0-9]\d{7}$/',      // Israeli mobile international
            '/^\+[1-9]\d{6,14}$/',       // General international
        );

        foreach ( $patterns as $pattern ) {
            if ( preg_match( $pattern, $phone ) ) {
                return true;
            }
        }

        return false;
    }

    /**
     * Validate a URL.
     *
     * @param string $url      The URL to validate.
     * @param bool   $required Whether the URL is required.
     * @return bool True if valid, false otherwise.
     */
    public static function is_valid_url( $url, $required = false ) {
        if ( empty( $url ) ) {
            return ! $required;
        }

        return filter_var( $url, FILTER_VALIDATE_URL ) !== false;
    }

    /**
     * Validate that a value is not empty.
     *
     * @param mixed $value The value to check.
     * @return bool True if not empty, false otherwise.
     */
    public static function is_not_empty( $value ) {
        if ( is_string( $value ) ) {
            return trim( $value ) !== '';
        }

        return ! empty( $value );
    }

    /**
     * Validate a string length.
     *
     * @param string   $value The string to validate.
     * @param int      $min   Minimum length.
     * @param int|null $max   Optional. Maximum length.
     * @return bool True if valid, false otherwise.
     */
    public static function is_valid_length( $value, $min, $max = null ) {
        $length = mb_strlen( $value );

        if ( $length < $min ) {
            return false;
        }

        if ( null !== $max && $length > $max ) {
            return false;
        }

        return true;
    }

    /**
     * Validate that a value is in a list of allowed values.
     *
     * @param mixed $value   The value to check.
     * @param array $allowed The list of allowed values.
     * @return bool True if in list, false otherwise.
     */
    public static function is_in_list( $value, $allowed ) {
        return in_array( $value, $allowed, true );
    }

    /**
     * Validate an integer value.
     *
     * @param mixed    $value The value to validate.
     * @param int|null $min   Optional. Minimum value.
     * @param int|null $max   Optional. Maximum value.
     * @return bool True if valid, false otherwise.
     */
    public static function is_valid_integer( $value, $min = null, $max = null ) {
        if ( ! is_numeric( $value ) ) {
            return false;
        }

        $int_value = intval( $value );

        if ( (string) $int_value !== (string) $value ) {
            return false;
        }

        if ( null !== $min && $int_value < $min ) {
            return false;
        }

        if ( null !== $max && $int_value > $max ) {
            return false;
        }

        return true;
    }

    /**
     * Validate a float value.
     *
     * @param mixed      $value The value to validate.
     * @param float|null $min   Optional. Minimum value.
     * @param float|null $max   Optional. Maximum value.
     * @return bool True if valid, false otherwise.
     */
    public static function is_valid_float( $value, $min = null, $max = null ) {
        if ( ! is_numeric( $value ) ) {
            return false;
        }

        $float_value = floatval( $value );

        if ( null !== $min && $float_value < $min ) {
            return false;
        }

        if ( null !== $max && $float_value > $max ) {
            return false;
        }

        return true;
    }

    /**
     * Validate a date string.
     *
     * @param string $date   The date string to validate.
     * @param string $format The expected format. Default 'Y-m-d'.
     * @return bool True if valid, false otherwise.
     */
    public static function is_valid_date( $date, $format = 'Y-m-d' ) {
        $d = DateTime::createFromFormat( $format, $date );
        return $d && $d->format( $format ) === $date;
    }

    /**
     * Validate a hex color code.
     *
     * @param string $color The color to validate.
     * @return bool True if valid, false otherwise.
     */
    public static function is_valid_hex_color( $color ) {
        return preg_match( '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/', $color ) === 1;
    }

    /**
     * Validate JSON string.
     *
     * @param string $json The JSON string to validate.
     * @return bool True if valid JSON, false otherwise.
     */
    public static function is_valid_json( $json ) {
        if ( ! is_string( $json ) ) {
            return false;
        }

        json_decode( $json );
        return json_last_error() === JSON_ERROR_NONE;
    }

    /**
     * Validate an API key format.
     *
     * @param string $key The API key to validate.
     * @return bool True if valid format, false otherwise.
     */
    public static function is_valid_api_key( $key ) {
        // Claude API keys typically start with 'sk-ant-'
        if ( strpos( $key, 'sk-ant-' ) === 0 ) {
            return strlen( $key ) > 20;
        }

        // Also accept generic format (alphanumeric with dashes)
        return preg_match( '/^[a-zA-Z0-9\-_]{20,}$/', $key ) === 1;
    }

    /**
     * Validate a slug (URL-friendly string).
     *
     * @param string $slug The slug to validate.
     * @return bool True if valid, false otherwise.
     */
    public static function is_valid_slug( $slug ) {
        return preg_match( '/^[a-z0-9]+(?:-[a-z0-9]+)*$/', $slug ) === 1;
    }

    /**
     * Validate multiple fields at once.
     *
     * @param array $data  The data to validate.
     * @param array $rules The validation rules.
     * @return true|WP_Error True if all valid, WP_Error with messages otherwise.
     *
     * @example
     * $rules = [
     *     'email' => ['required', 'email'],
     *     'name'  => ['required', 'min:2', 'max:100'],
     *     'age'   => ['integer', 'min:18', 'max:120'],
     * ];
     */
    public static function validate( $data, $rules ) {
        $errors = new WP_Error();

        foreach ( $rules as $field => $field_rules ) {
            $value = isset( $data[ $field ] ) ? $data[ $field ] : null;

            foreach ( $field_rules as $rule ) {
                // Parse rule and parameter
                $parts     = explode( ':', $rule );
                $rule_name = $parts[0];
                $param     = isset( $parts[1] ) ? $parts[1] : null;

                $error = self::apply_rule( $field, $value, $rule_name, $param );

                if ( $error ) {
                    $errors->add( $field . '_' . $rule_name, $error );
                }
            }
        }

        if ( $errors->has_errors() ) {
            return $errors;
        }

        return true;
    }

    /**
     * Apply a single validation rule.
     *
     * @param string      $field     The field name.
     * @param mixed       $value     The field value.
     * @param string      $rule_name The rule name.
     * @param string|null $param     The rule parameter.
     * @return string|null Error message or null if valid.
     */
    private static function apply_rule( $field, $value, $rule_name, $param ) {
        switch ( $rule_name ) {
            case 'required':
                if ( ! self::is_not_empty( $value ) ) {
                    /* translators: %s: field name */
                    return sprintf( __( '%s is required', 'boostbot' ), $field );
                }
                break;

            case 'email':
                if ( ! empty( $value ) && ! self::is_valid_email( $value ) ) {
                    /* translators: %s: field name */
                    return sprintf( __( '%s must be a valid email', 'boostbot' ), $field );
                }
                break;

            case 'url':
                if ( ! empty( $value ) && ! self::is_valid_url( $value ) ) {
                    /* translators: %s: field name */
                    return sprintf( __( '%s must be a valid URL', 'boostbot' ), $field );
                }
                break;

            case 'phone':
                if ( ! empty( $value ) && ! self::is_valid_phone( $value ) ) {
                    /* translators: %s: field name */
                    return sprintf( __( '%s must be a valid phone number', 'boostbot' ), $field );
                }
                break;

            case 'integer':
                if ( ! empty( $value ) && ! self::is_valid_integer( $value ) ) {
                    /* translators: %s: field name */
                    return sprintf( __( '%s must be an integer', 'boostbot' ), $field );
                }
                break;

            case 'min':
                if ( is_numeric( $value ) ) {
                    if ( floatval( $value ) < floatval( $param ) ) {
                        /* translators: 1: field name, 2: minimum value */
                        return sprintf( __( '%1$s must be at least %2$s', 'boostbot' ), $field, $param );
                    }
                } elseif ( is_string( $value ) ) {
                    if ( mb_strlen( $value ) < intval( $param ) ) {
                        /* translators: 1: field name, 2: minimum length */
                        return sprintf( __( '%1$s must be at least %2$s characters', 'boostbot' ), $field, $param );
                    }
                }
                break;

            case 'max':
                if ( is_numeric( $value ) ) {
                    if ( floatval( $value ) > floatval( $param ) ) {
                        /* translators: 1: field name, 2: maximum value */
                        return sprintf( __( '%1$s must be at most %2$s', 'boostbot' ), $field, $param );
                    }
                } elseif ( is_string( $value ) ) {
                    if ( mb_strlen( $value ) > intval( $param ) ) {
                        /* translators: 1: field name, 2: maximum length */
                        return sprintf( __( '%1$s must be at most %2$s characters', 'boostbot' ), $field, $param );
                    }
                }
                break;

            case 'hex_color':
                if ( ! empty( $value ) && ! self::is_valid_hex_color( $value ) ) {
                    /* translators: %s: field name */
                    return sprintf( __( '%s must be a valid hex color', 'boostbot' ), $field );
                }
                break;

            case 'json':
                if ( ! empty( $value ) && ! self::is_valid_json( $value ) ) {
                    /* translators: %s: field name */
                    return sprintf( __( '%s must be valid JSON', 'boostbot' ), $field );
                }
                break;
        }

        return null;
    }
}










