<?php
/**
 * Theme-token discovery, color contrast, and preset selection for pattern rendering.
 *
 * @package WPAgentCompanion
 */

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

/* ---------------------------------------------------------------------------
 * Kleine Theme-/Auswahl-Helfer
 * ------------------------------------------------------------------------- */

/**
 * Sammelt slug-Werte rekursiv aus theme.json-Preset-Strukturen. WordPress kann
 * Paletten flach oder nach Herkunft (theme/default/custom) gruppiert liefern.
 *
 * @param mixed $node
 * @return string[]
 */
function wpagent_pattern_collect_slugs( $node ): array {
	$slugs = array();
	if ( ! is_array( $node ) ) {
		return $slugs;
	}

	if ( isset( $node['slug'] ) && is_scalar( $node['slug'] ) ) {
		$slug = trim( (string) $node['slug'] );
		if ( '' !== $slug ) {
			$slugs[] = $slug;
		}
	}

	foreach ( $node as $value ) {
		if ( is_array( $value ) ) {
			$slugs = array_merge( $slugs, wpagent_pattern_collect_slugs( $value ) );
		}
	}

	return $slugs;
}

/**
 * Sammelt Farbwerte rekursiv als slug => color aus flachen und origin-keyed Paletten.
 *
 * @param mixed $node
 * @return array<string,string>
 */
function wpagent_pattern_collect_color_map( $node ): array {
	$colors = array();
	if ( ! is_array( $node ) ) {
		return $colors;
	}

	if ( isset( $node['slug'], $node['color'] ) && is_scalar( $node['slug'] ) && is_scalar( $node['color'] ) ) {
		$slug = trim( (string) $node['slug'] );
		if ( '' !== $slug ) {
			$colors[ $slug ] = trim( (string) $node['color'] );
		}
	}

	foreach ( $node as $value ) {
		if ( is_array( $value ) ) {
			$colors = array_replace( $colors, wpagent_pattern_collect_color_map( $value ) );
		}
	}

	return $colors;
}

/**
 * Liest einen Settings-Knoten aus den theme.json-Dateien des Eltern- und
 * Child-Themes. Der Elternwert kommt zuerst, damit das Child-Theme gleichnamige
 * Presets überschreiben kann.
 *
 * @param string[] $path Pfad unterhalb von settings.
 * @return array<int,mixed>
 */
function wpagent_pattern_theme_json_nodes( array $path ): array {
	$nodes = array();
	$dirs  = array_values( array_unique( array( get_template_directory(), get_stylesheet_directory() ) ) );
	foreach ( $dirs as $theme_dir ) {
		$theme_json = trailingslashit( $theme_dir ) . 'theme.json';
		if ( ! is_readable( $theme_json ) ) {
			continue;
		}
		$decoded = json_decode( (string) file_get_contents( $theme_json ), true );
		$node    = is_array( $decoded ) && isset( $decoded['settings'] ) ? $decoded['settings'] : null;
		foreach ( $path as $segment ) {
			$node = is_array( $node ) && isset( $node[ $segment ] ) ? $node[ $segment ] : null;
		}
		if ( is_array( $node ) ) {
			$nodes[] = $node;
		}
	}
	return $nodes;
}

/** Request-lokaler Cache-Schlüssel, der Theme-Wechsel im selben Lauf trennt. */
function wpagent_pattern_theme_cache_key(): string {
	return (string) get_template() . '|' . (string) get_stylesheet();
}

/**
 * Aktive Theme-Farbpalette aus WordPress' global settings lesen.
 *
 * @return string[]
 */
function wpagent_pattern_theme_color_slugs(): array {
	static $cache = array();
	$key = wpagent_pattern_theme_cache_key();

	if ( isset( $cache[ $key ] ) ) {
		return $cache[ $key ];
	}

	$palettes = wpagent_pattern_theme_json_nodes( array( 'color', 'palette' ) );
	if ( function_exists( 'wp_get_global_settings' ) ) {
		$settings_palette = wp_get_global_settings( array( 'color', 'palette' ) );
		if ( is_array( $settings_palette ) ) {
			$palettes[] = $settings_palette;
		}
	}

	$slugs = array();
	foreach ( $palettes as $palette ) {
		$slugs = array_merge( $slugs, wpagent_pattern_collect_slugs( $palette ) );
	}
	$cache[ $key ] = array_values( array_unique( $slugs ) );
	return $cache[ $key ];
}

/**
 * Aktive Theme-Farbpalette als slug => color lesen.
 *
 * @return array<string,string>
 */
function wpagent_pattern_theme_color_map(): array {
	static $cache = array();
	$key = wpagent_pattern_theme_cache_key();

	if ( isset( $cache[ $key ] ) ) {
		return $cache[ $key ];
	}

	$palettes = wpagent_pattern_theme_json_nodes( array( 'color', 'palette' ) );
	if ( function_exists( 'wp_get_global_settings' ) ) {
		$settings_palette = wp_get_global_settings( array( 'color', 'palette' ) );
		if ( is_array( $settings_palette ) ) {
			$palettes[] = $settings_palette;
		}
	}

	$colors = array();
	foreach ( $palettes as $palette ) {
		$colors = array_replace( $colors, wpagent_pattern_collect_color_map( $palette ) );
	}
	$cache[ $key ] = $colors;
	return $cache[ $key ];
}

/**
 * Prüft, ob ein Farb-Slug im aktiven Theme existiert.
 */
function wpagent_pattern_color_exists( string $slug ): bool {
	return '' !== $slug && in_array( $slug, wpagent_pattern_theme_color_slugs(), true );
}

/**
 * Wählt den ersten vorhandenen Farb-Slug aus einer Kandidatenliste.
 *
 * @param string[] $candidates
 */
function wpagent_pattern_default_color( array $candidates, string $avoid = '' ): string {
	foreach ( $candidates as $candidate ) {
		$slug = trim( (string) $candidate );
		if ( '' === $slug || $slug === $avoid ) {
			continue;
		}
		if ( wpagent_pattern_color_exists( $slug ) ) {
			return $slug;
		}
	}

	return '';
}

/**
 * Liefert den Farb-Slug aus der Sektion, sofern er im aktiven Theme existiert,
 * sonst den ersten passenden Theme-Default. Unbekannte Slugs werden leer
 * zurückgegeben, damit keine wirkungslosen has-*-Klassen gespeichert werden.
 *
 * @param string|string[] $default_candidates
 */
function wpagent_pattern_pick_color( array $section, string $key, $default_candidates = array() ): string {
	$value = trim( (string) ( $section[ $key ] ?? '' ) );
	if ( '' !== $value ) {
		return wpagent_pattern_color_exists( $value ) ? $value : '';
	}

	$candidates = is_array( $default_candidates ) ? $default_candidates : array( $default_candidates );
	return wpagent_pattern_default_color( $candidates );
}

/**
 * Gedämpfte Textfarbe für Subheadings und Metazeilen.
 */
function wpagent_pattern_muted_color(): string {
	return wpagent_pattern_default_color( array( 'accent-4', 'contrast-2', 'contrast-3', 'foreground', 'secondary', 'primary' ) );
}

/**
 * Akzentfarbe für Badges.
 */
function wpagent_pattern_badge_color(): string {
	return wpagent_pattern_default_color( array( 'accent-3', 'accent', 'primary', 'secondary' ) );
}

/**
 * Rahmenfarbe für Karten.
 */
function wpagent_pattern_border_color(): string {
	return wpagent_pattern_default_color( array( 'accent-6', 'contrast-3', 'contrast-2', 'foreground', 'contrast' ) );
}

/**
 * Relative Luminanz für Hexfarben. Nicht-Hex-Werte (z. B. color-mix()) liefern null.
 */
function wpagent_pattern_hex_luminance( string $color ): ?float {
	$color = trim( $color );
	if ( preg_match( '/^#([0-9a-fA-F]{3})$/', $color, $m ) ) {
		$hex = $m[1][0] . $m[1][0] . $m[1][1] . $m[1][1] . $m[1][2] . $m[1][2];
	} elseif ( preg_match( '/^#([0-9a-fA-F]{3})([0-9a-fA-F])$/', $color, $m ) && 15 === hexdec( $m[2] ) ) {
		$hex = $m[1][0] . $m[1][0] . $m[1][1] . $m[1][1] . $m[1][2] . $m[1][2];
	} elseif ( preg_match( '/^#([0-9a-fA-F]{6})$/', $color, $m ) ) {
		$hex = $m[1];
	} elseif ( preg_match( '/^#([0-9a-fA-F]{6})([0-9a-fA-F]{2})$/', $color, $m ) && 255 === hexdec( $m[2] ) ) {
		$hex = $m[1];
	} else {
		return null;
	}

	$rgb = array(
		hexdec( substr( $hex, 0, 2 ) ) / 255,
		hexdec( substr( $hex, 2, 2 ) ) / 255,
		hexdec( substr( $hex, 4, 2 ) ) / 255,
	);
	foreach ( $rgb as $i => $channel ) {
		$rgb[ $i ] = $channel <= 0.03928 ? $channel / 12.92 : pow( ( $channel + 0.055 ) / 1.055, 2.4 );
	}

	return 0.2126 * $rgb[0] + 0.7152 * $rgb[1] + 0.0722 * $rgb[2];
}

function wpagent_pattern_contrast_ratio( float $a, float $b ): float {
	$lighter = max( $a, $b );
	$darker  = min( $a, $b );
	return ( $lighter + 0.05 ) / ( $darker + 0.05 );
}

/**
 * Wählt aus einer Palette den Farb-Slug mit dem höchsten Kontrast zum
 * Hintergrund. Theme-übliche Textfarben werden zuerst geprüft, anschließend
 * sämtliche Custom-Slugs. Dadurch funktionieren auch Themes, die Core-Paletten
 * deaktivieren und ausschließlich eigene Brand-Slugs definieren.
 *
 * @param string               $background_slug
 * @param array<string,string> $colors
 * @return string
 */
function wpagent_pattern_best_contrast_color( string $background_slug, array $colors ): string {
	if ( ! isset( $colors[ $background_slug ] ) ) {
		return '';
	}

	$bg_luminance = wpagent_pattern_hex_luminance( (string) $colors[ $background_slug ] );
	if ( null === $bg_luminance ) {
		return '';
	}

	$preferred  = array( 'contrast', 'foreground', 'black', 'base', 'background', 'white', 'contrast-2', 'contrast-3' );
	$candidates = array_values( array_unique( array_merge( $preferred, array_keys( $colors ) ) ) );
	$best       = '';
	$best_ratio = 0.0;

	foreach ( $candidates as $candidate ) {
		if ( $candidate === $background_slug || ! isset( $colors[ $candidate ] ) ) {
			continue;
		}
		$luminance = wpagent_pattern_hex_luminance( (string) $colors[ $candidate ] );
		if ( null === $luminance ) {
			continue;
		}
		$ratio = wpagent_pattern_contrast_ratio( $bg_luminance, $luminance );
		if ( $ratio > $best_ratio ) {
			$best       = (string) $candidate;
			$best_ratio = $ratio;
		}
	}

	return $best;
}

/**
 * Lesbare Textfarbe für einen bekannten Hintergrund-Slug.
 */
function wpagent_pattern_readable_text_color( string $background_slug ): string {
	$background_slug = trim( $background_slug );
	$colors          = wpagent_pattern_theme_color_map();
	$best            = wpagent_pattern_best_contrast_color( $background_slug, $colors );
	if ( '' !== $best ) {
		return $best;
	}

	if ( in_array( $background_slug, array( 'contrast', 'foreground', 'black', 'primary', 'accent', 'secondary', 'accent-3' ), true ) ) {
		return wpagent_pattern_default_color( array( 'base', 'background', 'white', 'contrast', 'foreground', 'black' ), $background_slug );
	}
	return wpagent_pattern_default_color( array( 'contrast', 'foreground', 'black', 'base', 'background', 'white' ), $background_slug );
}

/**
 * Lesbare Textfarbe für Karten mit eigenem Hintergrund.
 */
function wpagent_pattern_card_text_color( string $background_slug = '' ): string {
	return '' !== $background_slug ? wpagent_pattern_readable_text_color( $background_slug ) : wpagent_pattern_default_color( array( 'contrast', 'foreground', 'black', 'contrast-2', 'contrast-3' ) );
}

/**
 * Generischer Leser für theme.json-Preset-Slugs (analog wpagent_pattern_theme_color_slugs
 * für Abstände und Schriftgrössen). Liest zuerst wp_get_global_settings, fällt auf die
 * theme.json-Datei zurück und cached pro Settings-Pfad.
 *
 * @param string[] $path settings-Pfad, z. B. array( 'spacing', 'spacingSizes' ).
 * @return string[]
 */
function wpagent_pattern_theme_preset_slugs( array $path ): array {
	static $cache = array();
	$key = wpagent_pattern_theme_cache_key() . '|' . implode( '.', $path );
	if ( isset( $cache[ $key ] ) ) {
		return $cache[ $key ];
	}

	$presets = wpagent_pattern_theme_json_nodes( $path );
	if ( function_exists( 'wp_get_global_settings' ) ) {
		$settings_preset = wp_get_global_settings( $path );
		if ( is_array( $settings_preset ) ) {
			$presets[] = $settings_preset;
		}
	}

	$slugs = array();
	foreach ( $presets as $preset ) {
		$slugs = array_merge( $slugs, wpagent_pattern_collect_slugs( $preset ) );
	}
	$cache[ $key ] = array_values( array_unique( $slugs ) );
	return $cache[ $key ];
}

/**
 * Aktive Schriftgrössen-Slugs des Themes (z. B. small..xx-large).
 *
 * @return string[]
 */
function wpagent_pattern_theme_font_size_slugs(): array {
	return wpagent_pattern_theme_preset_slugs( array( 'typography', 'fontSizes' ) );
}

/**
 * Aktive Abstands-Slugs des Themes (Twenty Twenty-Five: 20..80).
 *
 * @return string[]
 */
function wpagent_pattern_theme_spacing_slugs(): array {
	return wpagent_pattern_theme_preset_slugs( array( 'spacing', 'spacingSizes' ) );
}

/**
 * Prüft, ob ein Schriftgrössen-Slug im aktiven Theme existiert.
 */
function wpagent_pattern_font_size_exists( string $slug ): bool {
	return '' !== $slug && in_array( $slug, wpagent_pattern_theme_font_size_slugs(), true );
}

/**
 * Prüft, ob ein Abstands-Slug im aktiven Theme existiert.
 */
function wpagent_pattern_spacing_exists( string $slug ): bool {
	return '' !== $slug && in_array( $slug, wpagent_pattern_theme_spacing_slugs(), true );
}

/**
 * Löst einen gewünschten Schriftgrössen-Slug gegen die aktive Theme-Skala auf.
 * Existiert er, bleibt er unverändert (der Normalfall in Twenty Twenty-Five und in
 * jedem Theme mit den WordPress-Standard-Slugs). Fehlt er, wird über eine
 * Nachbarschaftskarte der Standard-Grössennamen die nächstbeste vorhandene Grösse
 * gewählt. Findet sich nichts, wird '' zurückgegeben, sodass der Emitter die Grössen-
 * Klasse weglässt (Standardgrösse) statt eine wirkungslose has-*-font-size-Klasse in
 * ein fremdes Theme zu schreiben.
 */
function wpagent_pattern_pick_font( string $desired ): string {
	$desired = trim( $desired );
	if ( '' === $desired || wpagent_pattern_font_size_exists( $desired ) ) {
		return $desired;
	}

	$neighbors = array(
		'xx-large' => array( 'x-large', 'xxx-large', 'huge', 'large' ),
		'x-large'  => array( 'large', 'xx-large', 'huge' ),
		'large'    => array( 'x-large', 'medium' ),
		'medium'   => array( 'normal', 'large', 'small' ),
		'small'    => array( 'x-small', 'medium' ),
	);
	foreach ( ( $neighbors[ $desired ] ?? array() ) as $alt ) {
		if ( wpagent_pattern_font_size_exists( $alt ) ) {
			return $alt;
		}
	}

	return '';
}

/**
 * Löst einen gewünschten Abstands-Slug gegen die aktive Theme-Skala auf. Existiert er,
 * bleibt er unverändert. Fehlt er und sind sowohl der Wunsch als auch die Theme-Slugs
 * numerisch (der übliche Fall, Twenty Twenty-Five nutzt 20..80), wird der numerisch
 * nächstliegende vorhandene Slug gewählt. Sonst wird '' zurückgegeben, sodass der
 * Emitter den Abstand weglässt statt ein wirkungsloses var:preset|spacing zu schreiben.
 */
function wpagent_pattern_pick_spacing( string $desired ): string {
	$desired = trim( $desired );
	if ( '' === $desired || wpagent_pattern_spacing_exists( $desired ) ) {
		return $desired;
	}

	if ( is_numeric( $desired ) ) {
		$best      = '';
		$best_dist = null;
		foreach ( wpagent_pattern_theme_spacing_slugs() as $slug ) {
			if ( ! is_numeric( $slug ) ) {
				continue;
			}
			$dist = abs( (float) $slug - (float) $desired );
			if ( null === $best_dist || $dist < $best_dist ) {
				$best_dist = $dist;
				$best      = $slug;
			}
		}
		if ( '' !== $best ) {
			return $best;
		}
	}

	return '';
}

/**
 * Löst das Button-Label/URL-Paar auf und akzeptiert beide Schemata
 * (cta_label/cta_url UND button_label/button_url), $primary zuerst. Behebt die
 * frühere Schema-Asymmetrie, bei der hero cta_* und cta button_* erwartete und
 * das jeweils andere Feld still verschluckt wurde.
 *
 * @return array{0:string,1:string} [label, url]
 */
function wpagent_pattern_pick_button( array $section, string $primary = 'cta', string $fallback = 'button' ): array {
	$label = trim( (string) ( $section[ $primary . '_label' ] ?? '' ) );
	$url   = (string) ( $section[ $primary . '_url' ] ?? '' );
	if ( '' === $label ) {
		$label = trim( (string) ( $section[ $fallback . '_label' ] ?? '' ) );
		$url   = (string) ( $section[ $fallback . '_url' ] ?? '' );
	}
	return array( $label, $url );
}
