<?php
/**
 * Color manipulation helper functions.
 *
 * @package Awesomemotive\Rewardswp\Helpers
 * @since 1.1.0
 */

namespace Awesomemotive\Rewardswp\Helpers;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Calculate contrasting text color for a given background color.
 *
 * When dark text is needed, derives a dark shade from the brand color
 * rather than using a fixed gray, keeping the header visually cohesive.
 *
 * @since 1.1.0
 *
 * @param string $hex The background color in hex format (with or without #).
 *
 * @return string The contrasting text color (derived dark or #ffffff).
 */
function get_contrasting_text_color( string $hex ): string {
	$bg_luminance = calculate_luminance_from_hex( $hex );

	// Calculate contrast ratios against black (luminance = 0) and white (luminance = 1).
	$black_contrast = calculate_contrast_ratio( 0, $bg_luminance );
	$white_contrast = calculate_contrast_ratio( 1, $bg_luminance );

	if ( $white_contrast > $black_contrast ) {
		return '#ffffff';
	}

	return derive_dark_foreground( $hex );
}

/**
 * Derive a dark foreground color from a brand color.
 *
 * Converts to HSL to preserve hue and saturation at low lightness, producing
 * a rich dark shade rather than a muddy scaled-down color.
 * Falls back to neutral dark gray for achromatic colors.
 *
 * @since 1.4.0
 *
 * @param string $hex The brand color in hex format (with or without #).
 *
 * @return string Dark foreground color in hex format.
 */
function derive_dark_foreground( string $hex ): string {
	$hex = ltrim( $hex, '#' );

	if ( strlen( $hex ) === 3 ) {
		$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
	}

	$r = hexdec( substr( $hex, 0, 2 ) );
	$g = hexdec( substr( $hex, 2, 2 ) );
	$b = hexdec( substr( $hex, 4, 2 ) );

	$bg_luminance = calculate_relative_luminance( $r, $g, $b );

	list( $h, $s ) = rgb_to_hsl( $r, $g, $b );

	// Achromatic or near-achromatic colors: use neutral dark gray.
	if ( $s < 0.1 ) {
		return '#1f2937';
	}

	// Keep hue, moderate saturation, low lightness.
	$dark_s   = min( $s, 0.9 );
	$target_l = 0.18;

	list( $dr, $dg, $db ) = hsl_to_rgb( $h, $dark_s, $target_l );
	$contrast = calculate_contrast_ratio(
		$bg_luminance,
		calculate_relative_luminance( $dr, $dg, $db )
	);

	// Darken further if contrast is insufficient.
	while ( $contrast < 4.5 && $target_l > 0.05 ) {
		$target_l -= 0.02;

		list( $dr, $dg, $db ) = hsl_to_rgb( $h, $dark_s, $target_l );
		$contrast = calculate_contrast_ratio(
			$bg_luminance,
			calculate_relative_luminance( $dr, $dg, $db )
		);
	}

	// Final fallback if still insufficient contrast.
	if ( $contrast < 4.5 ) {
		return '#000000';
	}

	return sprintf( '#%02x%02x%02x', $dr, $dg, $db );
}

/**
 * Calculate relative luminance from a hex color.
 *
 * @since 1.1.0
 *
 * @param string $hex The color in hex format (with or without #).
 *
 * @return float Relative luminance value (0-1).
 */
function calculate_luminance_from_hex( string $hex ): float {
	$hex = ltrim( $hex, '#' );

	// Handle shorthand hex (e.g., #FFF -> #FFFFFF).
	if ( strlen( $hex ) === 3 ) {
		$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
	}

	// Extract RGB components.
	$r = hexdec( substr( $hex, 0, 2 ) );
	$g = hexdec( substr( $hex, 2, 2 ) );
	$b = hexdec( substr( $hex, 4, 2 ) );

	return calculate_relative_luminance( $r, $g, $b );
}

/**
 * Calculate WCAG contrast ratio between two luminance values.
 *
 * @since 1.1.0
 *
 * @param float $l1 First luminance value (0-1).
 * @param float $l2 Second luminance value (0-1).
 *
 * @return float Contrast ratio (1-21).
 */
function calculate_contrast_ratio( float $l1, float $l2 ): float {
	$lighter = max( $l1, $l2 );
	$darker  = min( $l1, $l2 );

	return ( $lighter + 0.05 ) / ( $darker + 0.05 );
}

/**
 * Calculate relative luminance for a given RGB color.
 *
 * Uses the WCAG 2.1 formula for relative luminance calculation.
 *
 * @since 1.1.0
 *
 * @param int $r Red component (0-255).
 * @param int $g Green component (0-255).
 * @param int $b Blue component (0-255).
 *
 * @return float Relative luminance value (0-1).
 */
function calculate_relative_luminance( int $r, int $g, int $b ): float {
	// Convert to sRGB.
	$r_srgb = $r / 255;
	$g_srgb = $g / 255;
	$b_srgb = $b / 255;

	// Apply gamma correction.
	$r_lin = $r_srgb <= 0.04045 ? $r_srgb / 12.92 : pow( ( $r_srgb + 0.055 ) / 1.055, 2.4 );
	$g_lin = $g_srgb <= 0.04045 ? $g_srgb / 12.92 : pow( ( $g_srgb + 0.055 ) / 1.055, 2.4 );
	$b_lin = $b_srgb <= 0.04045 ? $b_srgb / 12.92 : pow( ( $b_srgb + 0.055 ) / 1.055, 2.4 );

	// Calculate relative luminance using WCAG coefficients.
	return 0.2126 * $r_lin + 0.7152 * $g_lin + 0.0722 * $b_lin;
}

/**
 * Convert RGB to HSL.
 *
 * @since 1.4.0
 *
 * @param int $r Red component (0-255).
 * @param int $g Green component (0-255).
 * @param int $b Blue component (0-255).
 *
 * @return array{float, float, float} Array of [hue (0-1), saturation (0-1), lightness (0-1)].
 */
function rgb_to_hsl( int $r, int $g, int $b ): array {
	$r /= 255;
	$g /= 255;
	$b /= 255;

	$max = max( $r, $g, $b );
	$min = min( $r, $g, $b );
	$l   = ( $max + $min ) / 2;

	if ( $max === $min ) {
		return array( 0.0, 0.0, $l );
	}

	$d = $max - $min;
	$s = $l > 0.5 ? $d / ( 2 - $max - $min ) : $d / ( $max + $min );

	if ( $max === $r ) {
		$h = ( ( $g - $b ) / $d + ( $g < $b ? 6 : 0 ) ) / 6;
	} elseif ( $max === $g ) {
		$h = ( ( $b - $r ) / $d + 2 ) / 6;
	} else {
		$h = ( ( $r - $g ) / $d + 4 ) / 6;
	}

	return array( $h, $s, $l );
}

/**
 * Convert HSL to RGB.
 *
 * @since 1.4.0
 *
 * @param float $h Hue (0-1).
 * @param float $s Saturation (0-1).
 * @param float $l Lightness (0-1).
 *
 * @return array{int, int, int} Array of [red (0-255), green (0-255), blue (0-255)].
 */
function hsl_to_rgb( float $h, float $s, float $l ): array {
	if ( 0.0 === $s ) {
		$v = (int) round( $l * 255 );
		return array( $v, $v, $v );
	}

	$q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s;
	$p = 2 * $l - $q;

	$r = hue_to_rgb_channel( $p, $q, $h + 1 / 3 );
	$g = hue_to_rgb_channel( $p, $q, $h );
	$b = hue_to_rgb_channel( $p, $q, $h - 1 / 3 );

	return array(
		(int) round( $r * 255 ),
		(int) round( $g * 255 ),
		(int) round( $b * 255 ),
	);
}

/**
 * Helper for HSL to RGB conversion.
 *
 * @since 1.4.0
 *
 * @param float $p First intermediate value.
 * @param float $q Second intermediate value.
 * @param float $t Hue fraction.
 *
 * @return float RGB channel value (0-1).
 */
function hue_to_rgb_channel( float $p, float $q, float $t ): float {
	if ( $t < 0 ) {
		$t += 1;
	}
	if ( $t > 1 ) {
		$t -= 1;
	}
	if ( $t < 1 / 6 ) {
		return $p + ( $q - $p ) * 6 * $t;
	}
	if ( $t < 1 / 2 ) {
		return $q;
	}
	if ( $t < 2 / 3 ) {
		return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6;
	}

	return $p;
}
