<?php

/**
 * JCOGS Image Pro - Colour Validator
 * ===================================
 * Validates colour values with CSS colour name support
 * 
 * @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;

use JCOGSDesign\JCOGSImagePro\Service\ColourManagementService;

/**
 * Colour Validator
 * 
 * Validates colour values using the ColourManagementService.
 * Supports hex, rgb(), rgba(), and CSS named colours (e.g., 'darkred', 'navy').
 * 
 * This validator leverages the existing colour validation logic in ColourManagementService
 * which already handles CSS colour names via _convertNamedColorToHex().
 * 
 * Config Options:
 * - allow_empty (bool): Whether empty values are allowed (default: false)
 * 
 * Supported Formats:
 * - Hex: #RGB, #RRGGBB, #RRGGBBAA (with or without #)
 * - RGB: rgb(255, 0, 0)
 * - RGBA: rgba(255, 0, 0, 0.5)
 * - CSS Names: red, darkred, navy, etc. (case-insensitive)
 * 
 * Example Usage:
 * 
 * ```php
 * // Basic colour validation
 * $validator = new ColourValidator();
 * $result = $validator->validate('#FF0000');      // Success: '#FF0000'
 * $result = $validator->validate('darkred');      // Success: 'darkred' (converted internally)
 * $result = $validator->validate('rgb(255,0,0)'); // Success: 'rgb(255,0,0)'
 * $result = $validator->validate('invalid');      // Error: invalid colour
 * 
 * // Allow empty values
 * $validator = new ColourValidator(['allow_empty' => true]);
 * $result = $validator->validate('');             // Success: ''
 * ```
 * 
 * @package JCOGSDesign\JCOGSImagePro\Service\Validation
 * @since 2.0.0
 */
class ColourValidator implements ValidatorInterface
{
    private ColourManagementService $colorService;
    
    /**
     * Default configuration
     * 
     * @var array
     */
    private array $defaultConfig = [
        'allow_empty' => false
    ];
    
    /**
     * Constructor
     * 
     * @param ColourManagementService|null $colorService Color management service (injected or created)
     */
    public function __construct(?ColourManagementService $colorService = null)
    {
        // If no service provided, get it from EE service container
        $this->colorService = $colorService ?? ee('jcogs_img_pro:ColourManagementService');
    }
    
    /**
     * Validate a color value
     * 
     * Uses ColourManagementService::validate_colour_string() which:
     * - Converts CSS named colors to hex
     * - Validates hex, rgb(), rgba() formats
     * - Returns validated ColorInterface or falls back to default
     * 
     * @param mixed $value Color value to validate
     * @param array $config Configuration options
     * 
     * @return ValidationResult Success with original value or error
     */
    public function validate($value, array $config = []): ValidationResult
    {
        // Merge config with defaults
        $config = array_merge($this->defaultConfig, $config);
        
        // Handle null
        if ($value === null) {
            if ($config['allow_empty']) {
                return ValidationResult::success('');
            }
            return ValidationResult::error('Color value cannot be null', $value);
        }
        
        // Convert to string
        $value = trim((string) $value);
        
        // Handle empty strings
        if ($value === '') {
            if ($config['allow_empty']) {
                return ValidationResult::success('');
            }
            return ValidationResult::error('Color value cannot be empty', $value);
        }
        
        // Try to validate using ColourManagementService
        // This method handles CSS names, hex, rgb, rgba and returns ColorInterface on success
        // or falls back to default color on failure
        try {
            $originalValue = $value;
            
            // Store default to detect if validation failed
            $settings = ee()->config->item('jcogs_img_pro') ?? [];
            $default = $settings['img_cp_default_bg_color'] ?? '#FFFFFF';
            
            // Validate using the service - this will convert CSS names to hex internally
            $colorObject = $this->colorService->validate_colour_string($value);
            
            // The service returns ColorInterface on success, but always returns something
            // We need to check if it returned the original color or fell back to default
            // 
            // Strategy: Try to validate, and if we get back a color that doesn't match
            // our input (accounting for CSS name conversion), check if it's the default
            
            // For now, we'll trust that if validate_colour_string didn't throw an exception,
            // the color is valid. The service is very permissive and converts invalid colors
            // to the default, which is actually the desired behavior for runtime but not for
            // validation.
            
            // Better approach: Check if input is in a known format
            if (!$this->isValidColorFormat($originalValue)) {
                return ValidationResult::error(
                    'Invalid color format. Use hex (#RGB, #RRGGBB), rgb(), rgba(), or CSS color name.',
                    $originalValue
                );
            }
            
            // Return original value (not the converted ColorInterface)
            return ValidationResult::success($originalValue);
            
        } catch (\Exception $e) {
            return ValidationResult::error(
                'Invalid color value: ' . $e->getMessage(),
                $value
            );
        }
    }
    
    /**
     * Check if value is in a valid color format
     * 
     * This checks the format without converting, to catch truly invalid values
     * before they get converted to the default color by validate_colour_string()
     * 
     * @param string $value Color value to check
     * @return bool True if format is valid
     */
    private function isValidColorFormat(string $value): bool
    {
        $value = trim($value);
        
        // Check for hex format (with or without #)
        if (preg_match('/^#?[0-9a-fA-F]{3,8}$/', $value)) {
            return true;
        }
        
        // Check for rgb() format
        if (preg_match('/^rgba?\s*\([^)]+\)$/i', $value)) {
            return true;
        }
        
        // Check for CSS named color (use the same list as ColourManagementService)
        $lowerValue = strtolower($value);
        $cssColorNames = [
            'black', 'white', 'red', 'green', 'blue', 'yellow', 'cyan', 'magenta', 
            'gray', 'grey', 'maroon', 'darkred', 'brown', 'firebrick', 'crimson', 
            'tomato', 'coral', 'indianred', 'lightcoral', 'darksalmon', 'salmon', 
            'lightsalmon', 'orangered', 'darkorange', 'orange', 'gold', 'darkgoldenrod', 
            'goldenrod', 'palegoldenrod', 'darkkhaki', 'khaki', 'olive', 'yellowgreen', 
            'darkolivegreen', 'olivedrab', 'lawngreen', 'chartreuse', 'greenyellow', 
            'darkgreen', 'forestgreen', 'lime', 'limegreen', 'lightgreen', 'palegreen', 
            'darkseagreen', 'mediumspringgreen', 'springgreen', 'seagreen', 'mediumaquamarine', 
            'mediumseagreen', 'lightseagreen', 'darkslategray', 'darkslategrey', 'teal', 
            'darkcyan', 'aqua', 'lightcyan', 'darkturquoise', 'turquoise', 'mediumturquoise', 
            'paleturquoise', 'aquamarine', 'powderblue', 'cadetblue', 'steelblue', 
            'cornflowerblue', 'deepskyblue', 'dodgerblue', 'lightblue', 'skyblue', 
            'lightskyblue', 'midnightblue', 'navy', 'darkblue', 'mediumblue', 'royalblue', 
            'blueviolet', 'indigo', 'darkslateblue', 'slateblue', 'mediumslateblue', 
            'mediumpurple', 'darkmagenta', 'darkviolet', 'darkorchid', 'mediumorchid', 
            'purple', 'thistle', 'plum', 'violet', 'fuchsia', 'orchid', 'mediumvioletred', 
            'palevioletred', 'deeppink', 'hotpink', 'lightpink', 'pink', 'antiquewhite', 
            'beige', 'bisque', 'blanchedalmond', 'wheat', 'cornsilk', 'lemonchiffon', 
            'lightgoldenrodyellow', 'lightyellow', 'saddlebrown', 'sienna', 'chocolate', 
            'peru', 'sandybrown', 'burlywood', 'tan', 'rosybrown', 'moccasin', 
            'navajowhite', 'peachpuff', 'mistyrose', 'lavenderblush', 'linen', 'oldlace', 
            'papayawhip', 'seashell', 'mintcream', 'slategray', 'slategrey', 
            'lightslategray', 'lightslategrey', 'lightsteelblue', 'lavender', 'floralwhite', 
            'aliceblue', 'ghostwhite', 'honeydew', 'ivory', 'azure', 'snow', 'dimgray', 
            'dimgrey', 'darkgray', 'darkgrey', 'silver', 'lightgray', 'lightgrey', 
            'gainsboro', 'whitesmoke'
        ];
        
        if (in_array($lowerValue, $cssColorNames, true)) {
            return true;
        }
        
        return false;
    }
}
