<?php

declare(strict_types=1);

namespace Activetwist\Jsonm\Util;

/**
 * Pure color math helpers: RGB parsing, relative luminance, WCAG contrast,
 * rgba/hex derivation. No JSONM-payload awareness — operates on color
 * strings only.
 */
final class ColorMath
{
    /**
     * @return array{0:int,1:int,2:int}|null
     */
    public static function parseRgb(string $color): ?array
    {
        $trimmed = trim($color);
        if (preg_match('/^#([0-9a-f]{3})$/i', $trimmed, $match) === 1) {
            return [
                hexdec($match[1][0] . $match[1][0]),
                hexdec($match[1][1] . $match[1][1]),
                hexdec($match[1][2] . $match[1][2]),
            ];
        }
        if (preg_match('/^#([0-9a-f]{6})$/i', $trimmed, $match) === 1) {
            return [
                hexdec(substr($match[1], 0, 2)),
                hexdec(substr($match[1], 2, 2)),
                hexdec(substr($match[1], 4, 2)),
            ];
        }
        if (preg_match('/^rgba?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})/i', $trimmed, $match) === 1) {
            return [
                max(0, min(255, (int) $match[1])),
                max(0, min(255, (int) $match[2])),
                max(0, min(255, (int) $match[3])),
            ];
        }

        return null;
    }

    /**
     * @param array{0:int,1:int,2:int} $rgb
     */
    public static function relativeLuminance(array $rgb): float
    {
        $channels = array_map(static function (int $channel): float {
            $value = $channel / 255;
            return $value <= 0.03928 ? $value / 12.92 : (($value + 0.055) / 1.055) ** 2.4;
        }, $rgb);

        return (0.2126 * $channels[0]) + (0.7152 * $channels[1]) + (0.0722 * $channels[2]);
    }

    /**
     * @param array{0:int,1:int,2:int} $foreground
     * @param array{0:int,1:int,2:int} $background
     */
    public static function contrastRatio(array $foreground, array $background): float
    {
        $light = max(self::relativeLuminance($foreground), self::relativeLuminance($background));
        $dark = min(self::relativeLuminance($foreground), self::relativeLuminance($background));
        return ($light + 0.05) / ($dark + 0.05);
    }

    public static function rgbaFromColor(string $color, float $alpha, mixed $fallback): string
    {
        $rgb = self::parseRgb($color);
        if ($rgb === null) {
            return is_string($fallback) ? $fallback : $color;
        }

        return sprintf('rgba(%d, %d, %d, %.2F)', $rgb[0], $rgb[1], $rgb[2], $alpha);
    }

    public static function darkenHex(string $color, float $amount, mixed $fallback): string
    {
        $rgb = self::parseRgb($color);
        if ($rgb === null) {
            return is_string($fallback) ? $fallback : $color;
        }

        $factor = max(0.0, min(1.0, 1.0 - $amount));
        return sprintf(
            '#%02x%02x%02x',
            max(0, min(255, (int) round($rgb[0] * $factor))),
            max(0, min(255, (int) round($rgb[1] * $factor))),
            max(0, min(255, (int) round($rgb[2] * $factor)))
        );
    }
}
