<?php

/**
 * JCOGS Image Pro - Enum Validator
 * =================================
 * Validates values against predefined allowed values
 * 
 * @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;

/**
 * Enum Validator
 * 
 * Validates values against a predefined list of allowed values.
 * 
 * This validator is used for parameters that accept only specific string values,
 * such as fit modes, crop modes, alignment values, etc.
 * 
 * Config Options:
 * - allowed_values (array): Required. Array of allowed values
 * - case_sensitive (bool): Whether comparison is case-sensitive (default: true)
 * 
 * Example Usage:
 * 
 * ```php
 * // Fit parameter validation (case-insensitive)
 * $validator = new EnumValidator([
 *     'allowed_values' => ['contain', 'cover', 'fill', 'scale-down', 'none', 'distort'],
 *     'case_sensitive' => false
 * ]);
 * $result = $validator->validate('CONTAIN'); // Success: 'contain' (normalized to lowercase)
 * $result = $validator->validate('invalid'); // Error: not in allowed values
 * 
 * // Crop mode validation (case-sensitive)
 * $validator = new EnumValidator([
 *     'allowed_values' => ['yes', 'no', 'face_detect']
 * ]);
 * $result = $validator->validate('yes'); // Success: 'yes'
 * $result = $validator->validate('YES'); // Error: not in allowed values (case mismatch)
 * ```
 * 
 * @package JCOGSDesign\JCOGSImagePro\Service\Validation
 * @since 2.0.0
 */
class EnumValidator implements ValidatorInterface
{
    /**
     * Default configuration
     * 
     * @var array
     */
    private array $defaultConfig = [
        'allowed_values' => [],
        'case_sensitive' => false  // Default to case-insensitive
    ];
    
    /**
     * Validate an enum value
     * 
     * @param mixed $value Value to validate (typically string)
     * @param array $config Configuration options (must include allowed_values)
     * 
     * @return ValidationResult Success with sanitized value or error
     */
    public function validate($value, array $config = []): ValidationResult
    {
        // Merge config with defaults
        $config = array_merge($this->defaultConfig, $config);
        
        // Validate config
        if (empty($config['allowed_values']) || !is_array($config['allowed_values'])) {
            throw new \InvalidArgumentException('EnumValidator requires allowed_values config with at least one value');
        }
        
        // Handle null/empty values
        if ($value === null || $value === '') {
            return ValidationResult::error('Value is required', $value);
        }
        
        // Convert to string
        $value_str = (string)$value;
        $value_str = trim($value_str);
        
        // Check if only whitespace after trim
        if ($value_str === '') {
            return ValidationResult::error('Value is required', $value);
        }
        
        // Case-insensitive comparison
        if (!$config['case_sensitive']) {
            $value_lower = strtolower($value_str);
            $allowed_lower = array_map('strtolower', $config['allowed_values']);
            
            if (in_array($value_lower, $allowed_lower)) {
                // Return normalized lowercase value for consistency
                return ValidationResult::success($value_lower);
            }
            
            // Generate error with allowed values
            return ValidationResult::error(
                sprintf(
                    'Invalid value "%s". Must be one of: %s',
                    $value_str,
                    implode(', ', $config['allowed_values'])
                ),
                $value
            );
        }
        
        // Case-sensitive comparison
        if (in_array($value_str, $config['allowed_values'], true)) {
            return ValidationResult::success($value_str);
        }
        
        // Generate error with allowed values
        return ValidationResult::error(
            sprintf(
                'Invalid value "%s". Must be one of: %s',
                $value_str,
                implode(', ', $config['allowed_values'])
            ),
            $value
        );
    }
}
