<?php
/**
 * WP PowerSuite admin interface appearance (scoped brand colors).
 *
 * Custom brand mode sets the same --wp-admin-theme-color* variables WordPress
 * uses for profile admin colors, but only on PowerSuite UI roots (not body)
 * so the rest of wp-admin is not repainted.
 *
 * @package WP_PowerSuite\Core
 * @since 1.2.0
 */

namespace WP_PowerSuite\Core;

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

final class AdminAppearance {

	public const MODE_WORDPRESS_DEFAULT = 'wordpress_default';

	public const MODE_CUSTOM_BRAND = 'custom_brand';

	public const DEFAULT_BRAND_COLOR = '#57BBB6';

	public const WRAP_CLASS = 'wpps-admin-wrap';

	/** @var array{mode: string, color: string, palette: array<string, string>|null}|null */
	private static ?array $runtime_cache = null;

	public static function clearCache(): void {
		self::$runtime_cache = null;
	}

	/**
	 * @return array{mode: string, color: string, palette: array<string, string>|null}
	 */
	public static function getSettingsSnapshot(): array {
		if ( self::$runtime_cache !== null ) {
			return self::$runtime_cache;
		}

		$store = OptionStore::getInstance();
		$mode  = $store->get( 'interface_color_mode', self::MODE_WORDPRESS_DEFAULT );
		$mode  = is_string( $mode ) ? sanitize_key( $mode ) : self::MODE_WORDPRESS_DEFAULT;
		if ( ! in_array( $mode, self::validModes(), true ) ) {
			$mode = self::MODE_WORDPRESS_DEFAULT;
		}

		$raw_color = $store->get( 'custom_brand_color', self::DEFAULT_BRAND_COLOR );
		$color     = self::sanitizeHexColor( is_string( $raw_color ) ? $raw_color : self::DEFAULT_BRAND_COLOR );

		self::$runtime_cache = [
			'mode'    => $mode,
			'color'   => $color,
			'palette' => $mode === self::MODE_CUSTOM_BRAND ? self::wordpressThemePalette( $color ) : null,
		];

		return self::$runtime_cache;
	}

	/**
	 * @return list<string>
	 */
	public static function validModes(): array {
		return [ self::MODE_WORDPRESS_DEFAULT, self::MODE_CUSTOM_BRAND ];
	}

	public static function getMode(): string {
		return self::getSettingsSnapshot()['mode'];
	}

	public static function getBrandColor(): string {
		return self::getSettingsSnapshot()['color'];
	}

	public static function isCustomBrandMode(): bool {
		return self::getMode() === self::MODE_CUSTOM_BRAND;
	}

	/**
	 * PowerSuite UI roots that receive theme variables (never body — avoids full admin repaints).
	 *
	 * @return list<string>
	 */
	public static function scopedCssSelectors(): array {
		return [
			'.' . self::WRAP_CLASS,
			'.wp-powersuite-admin',
			'.wp-powersuite-modal',
			'.wp-powersuite-settings',
			'.wp-powersuite-module-settings',
			'.wpps-command-palette',
			'.wrap[class*="wpps-"]',
		];
	}

	public static function scopedCssSelectorString(): string {
		return implode( ',', self::scopedCssSelectors() );
	}

	public static function sanitizeHexColor( string $color ): string {
		$color = trim( $color );
		if ( $color === '' ) {
			return self::DEFAULT_BRAND_COLOR;
		}

		if ( preg_match( '|^#([A-Fa-f0-9]{3})$|', $color, $matches ) ) {
			$hex = $matches[1];

			return sprintf(
				'#%s%s%s',
				str_repeat( $hex[0], 2 ),
				str_repeat( $hex[1], 2 ),
				str_repeat( $hex[2], 2 )
			);
		}

		if ( preg_match( '|^#([A-Fa-f0-9]{6})$|', $color ) ) {
			return strtoupper( $color );
		}

		return self::DEFAULT_BRAND_COLOR;
	}

	/**
	 * WordPress admin-schemes.css variable set plus precomputed accent surfaces (no runtime color-mix).
	 *
	 * @return array{
	 *     color: string,
	 *     darker_10: string,
	 *     darker_20: string,
	 *     rgb: string,
	 *     darker_10_rgb: string,
	 *     darker_20_rgb: string,
	 *     surface: string,
	 *     border_soft: string
	 * }
	 */
	public static function wordpressThemePalette( string $color ): array {
		$color     = self::sanitizeHexColor( $color );
		$darker_10 = self::adjustHslLightness( $color, -10.0 );
		$darker_20 = self::adjustHslLightness( $color, -20.0 );

		return [
			'color'         => $color,
			'darker_10'     => $darker_10,
			'darker_20'     => $darker_20,
			'rgb'           => self::hexToRgbString( $color ),
			'darker_10_rgb' => self::hexToRgbString( $darker_10 ),
			'darker_20_rgb' => self::hexToRgbString( $darker_20 ),
			'surface'       => self::mixWithWhite( $color, 88.0 ),
			'border_soft'   => self::mixHexColors( $color, '#C3C4C7', 35.0 ),
		];
	}

	/**
	 * @deprecated 1.2.0 Use wordpressThemePalette() instead.
	 *
	 * @return array<string, string>
	 */
	public static function paletteFromColor( string $color ): array {
		return self::wordpressThemePalette( $color );
	}

	/**
	 * CSS custom properties matching WordPress profile admin colors.
	 */
	public static function cssVariablesBlock(): string {
		$snapshot = self::getSettingsSnapshot();
		if ( $snapshot['mode'] !== self::MODE_CUSTOM_BRAND || ! is_array( $snapshot['palette'] ) ) {
			return '';
		}

		$palette = $snapshot['palette'];

		return sprintf(
			'--wp-admin-theme-color:%1$s;--wp-admin-theme-color--rgb:%2$s;--wp-admin-theme-color-darker-10:%3$s;--wp-admin-theme-color-darker-10--rgb:%4$s;--wp-admin-theme-color-darker-20:%5$s;--wp-admin-theme-color-darker-20--rgb:%6$s;--wpps-accent-surface:%7$s;--wpps-accent-border-soft:%8$s;--wpps-accent:var(--wp-admin-theme-color);--wpps-accent-hover:var(--wp-admin-theme-color-darker-10);',
			esc_attr( $palette['color'] ),
			esc_attr( $palette['rgb'] ),
			esc_attr( $palette['darker_10'] ),
			esc_attr( $palette['darker_10_rgb'] ),
			esc_attr( $palette['darker_20'] ),
			esc_attr( $palette['darker_20_rgb'] ),
			esc_attr( $palette['surface'] ),
			esc_attr( $palette['border_soft'] )
		);
	}

	/**
	 * Scoped inline CSS for PowerSuite UI when custom brand mode is active.
	 */
	public static function inlineCss(): string {
		$vars = self::cssVariablesBlock();
		if ( $vars === '' ) {
			return '';
		}

		return self::scopedCssSelectorString() . '{' . $vars . '}';
	}

	/**
	 * @param array<string, mixed> $settings
	 * @return array<string, mixed>
	 */
	public static function sanitizeSettings( array $settings ): array {
		$mode = isset( $settings['interface_color_mode'] )
			? sanitize_key( (string) $settings['interface_color_mode'] )
			: self::MODE_WORDPRESS_DEFAULT;
		$settings['interface_color_mode'] = in_array( $mode, self::validModes(), true )
			? $mode
			: self::MODE_WORDPRESS_DEFAULT;

		$color = isset( $settings['custom_brand_color'] )
			? (string) $settings['custom_brand_color']
			: self::DEFAULT_BRAND_COLOR;
		$settings['custom_brand_color'] = self::sanitizeHexColor( $color );

		return $settings;
	}

	private static function hexToRgb( string $hex ): array {
		$hex = ltrim( self::sanitizeHexColor( $hex ), '#' );

		return [
			(int) hexdec( substr( $hex, 0, 2 ) ),
			(int) hexdec( substr( $hex, 2, 2 ) ),
			(int) hexdec( substr( $hex, 4, 2 ) ),
		];
	}

	private static function hexToRgbString( string $hex ): string {
		[ $r, $g, $b ] = self::hexToRgb( $hex );

		return sprintf( '%d, %d, %d', $r, $g, $b );
	}

	private static function rgbToHex( int $r, int $g, int $b ): string {
		return sprintf(
			'#%02X%02X%02X',
			max( 0, min( 255, $r ) ),
			max( 0, min( 255, $g ) ),
			max( 0, min( 255, $b ) )
		);
	}

	private static function mixHexColors( string $hex1, string $hex2, float $weight1_percent ): string {
		[ $r1, $g1, $b1 ] = self::hexToRgb( $hex1 );
		[ $r2, $g2, $b2 ] = self::hexToRgb( $hex2 );
		$weight            = max( 0.0, min( 100.0, $weight1_percent ) ) / 100.0;

		return self::rgbToHex(
			(int) round( $r1 * $weight + $r2 * ( 1 - $weight ) ),
			(int) round( $g1 * $weight + $g2 * ( 1 - $weight ) ),
			(int) round( $b1 * $weight + $b2 * ( 1 - $weight ) )
		);
	}

	/**
	 * HSL lightness adjustment aligned with WordPress admin-schemes (Sass color.adjust lightness).
	 */
	private static function adjustHslLightness( string $hex, float $lightness_delta ): string {
		[ $h, $s, $l ] = self::hexToHsl( $hex );
		$l              = max( 0.0, min( 100.0, $l + $lightness_delta ) );

		return self::hslToHex( $h, $s, $l );
	}

	/**
	 * @return array{0: float, 1: float, 2: float} Hue 0-360, saturation/lightness 0-100.
	 */
	private static function hexToHsl( string $hex ): array {
		[ $r, $g, $b ] = self::hexToRgb( $hex );
		$r /= 255;
		$g /= 255;
		$b /= 255;

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

		if ( $max === $min ) {
			return [ 0.0, 0.0, $l * 100.0 ];
		}

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

		switch ( $max ) {
			case $r:
				$h = ( $g - $b ) / $d + ( $g < $b ? 6 : 0 );
				break;
			case $g:
				$h = ( $b - $r ) / $d + 2;
				break;
			default:
				$h = ( $r - $g ) / $d + 4;
				break;
		}

		$h *= 60;

		return [ $h, $s * 100.0, $l * 100.0 ];
	}

	private static function hslToHex( float $h, float $s, float $l ): string {
		$h = fmod( $h, 360.0 );
		if ( $h < 0 ) {
			$h += 360.0;
		}

		$s /= 100.0;
		$l /= 100.0;

		if ( $s <= 0.0 ) {
			$gray = (int) round( $l * 255 );

			return self::rgbToHex( $gray, $gray, $gray );
		}

		$h /= 360.0;

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

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

		return self::rgbToHex(
			(int) round( $r * 255 ),
			(int) round( $g * 255 ),
			(int) round( $b * 255 )
		);
	}

	private static function hueToRgb( float $p, float $q, float $t ): float {
		if ( $t < 0 ) {
			++$t;
		}
		if ( $t > 1 ) {
			--$t;
		}
		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;
	}

	private static function mixWithWhite( string $hex, float $white_percent ): string {
		[ $r, $g, $b ] = self::hexToRgb( $hex );
		$mix            = max( 0.0, min( 100.0, $white_percent ) ) / 100.0;

		return self::rgbToHex(
			(int) round( $r + ( 255 - $r ) * $mix ),
			(int) round( $g + ( 255 - $g ) * $mix ),
			(int) round( $b + ( 255 - $b ) * $mix )
		);
	}
}
