<?php

/**
 * JCOGS Image Pro - String Validator
 * ===================================
 * Validates string values for length and format
 * 
 * @category   ExpressionEngine Add-on
 * @package    JCOGS Image Pro
 * @author     JCOGS Design <contact@jcogs.net>
 * @copyright  Copyright (c) 2021 - 2025 JCOGS Design
 * @license    https://jcogs.net/add-ons/license/jcogs_img_pro
 * @version    2.0.6
 * @link       https://JCOGS.net/
 * @since      2.0.0
 */

namespace JCOGSDesign\JCOGSImagePro\Service\Validation;

/**
 * String Validator
 * 
 * Validates string values for length, format, and character restrictions.
 * 
 * This validator is used for parameters that accept free-form text but require
 * specific constraints, such as text content, file paths, color codes, etc.
 * 
 * Config Options:
 * - min_length (int): Minimum string length (default: no minimum)
 * - max_length (int): Maximum string length (default: no maximum)
 * - pattern (string): Regular expression pattern (PCRE format) (default: null)
 * - allowed_chars (string): String of allowed characters (default: null, allows all)
 * - trim (bool): Trim whitespace before validation (default: true)
 * 
 * Example Usage:
 * 
 * ```php
 * // Text content validation (max 500 characters)
 * $validator = new StringValidator([
 *     'max_length' => 500
 * ]);
 * $result = $validator->validate('Hello World'); // Success: 'Hello World'
 * 
 * // Hex color validation (pattern matching)
 * $validator = new StringValidator([
 *     'pattern' => '/^#?[0-9A-Fa-f]{6}$/'
 * ]);
 * $result = $validator->validate('#FF5733'); // Success: '#FF5733'
 * $result = $validator->validate('FF5733'); // Success: 'FF5733'
 * $result = $validator->validate('invalid'); // Error: doesn't match pattern
 * 
 * // Alphanumeric validation (allowed chars)
 * $validator = new StringValidator([
 *     'allowed_chars' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'
 * ]);
 * $result = $validator->validate('my-file_123'); // Success
 * $result = $validator->validate('my file!'); // Error: contains disallowed chars
 * ```
 * 
 * @package JCOGSDesign\JCOGSImagePro\Service\Validation
 * @since 2.0.0
 */
class StringValidator implements ValidatorInterface
{
    /**
     * Default configuration
     * 
     * @var array
     */
    private array $defaultConfig = [
        'min_length' => null,
        'max_length' => null,
        'pattern' => null,
        'allowed_chars' => null,
        'allow_empty' => false,
        'trim' => true
    ];
    
    /**
     * Validate a string value
     * 
     * @param mixed $value Value to validate (will be converted to string)
     * @param array $config Configuration options
     * 
     * @return ValidationResult Success with sanitized string or error
     */
    public function validate($value, array $config = []): ValidationResult
    {
        // Merge config with defaults
        $config = array_merge($this->defaultConfig, $config);
        
        // Handle null
        if ($value === null) {
            return ValidationResult::error('Value cannot be null', $value);
        }
        
        // Convert to string
        $value_str = (string)$value;
        
        // Trim if configured
        if ($config['trim']) {
            $value_str = trim($value_str);
        }
        
        // Check if empty (after trimming)
        if ($value_str === '') {
            if ($config['allow_empty']) {
                return ValidationResult::success($value_str);
            }
            return ValidationResult::error('Value is required', $value);
        }
        
        // Check minimum length
        if ($config['min_length'] !== null) {
            $length = mb_strlen($value_str);
            if ($length < $config['min_length']) {
                return ValidationResult::error(
                    sprintf('String must be at least %d characters (minimum length)', $config['min_length']),
                    $value
                );
            }
        }
        
        // Check maximum length
        if ($config['max_length'] !== null) {
            $length = mb_strlen($value_str);
            if ($length > $config['max_length']) {
                return ValidationResult::error(
                    sprintf('String must be at most %d characters (maximum length)', $config['max_length']),
                    $value
                );
            }
        }
        
        // Check allowed characters
        if ($config['allowed_chars'] !== null) {
            $allowed_chars = $config['allowed_chars'];
            $chars = mb_str_split($value_str);
            
            foreach ($chars as $char) {
                if (mb_strpos($allowed_chars, $char) === false) {
                    return ValidationResult::error(
                        sprintf('String contains disallowed character: %s', $char),
                        $value
                    );
                }
            }
        }
        
        // Check pattern (regex)
        if ($config['pattern'] !== null) {
            if (!is_string($config['pattern'])) {
                throw new \InvalidArgumentException('Pattern must be a string');
            }
            
            // Validate pattern is valid regex
            if (@preg_match($config['pattern'], '') === false) {
                throw new \InvalidArgumentException('Invalid regex pattern provided');
            }
            
            if (!preg_match($config['pattern'], $value_str)) {
                return ValidationResult::error(
                    'String does not match required format',
                    $value
                );
            }
        }
        
        return ValidationResult::success($value_str);
    }
}
