<?php
/**
 * CSS custom property emission.
 *
 * @package MenuVerse
 */

namespace WPXero\MenuVerse\Frontend;

defined( 'ABSPATH' ) || exit;

/**
 * Turns schema declared `ui.token` hints into custom property declarations.
 *
 * Both places that emit tokens — the plugin wide `:root` block and a module's
 * own `style` attribute — read the same hint out of a schema, so the loop that
 * reads it lives here rather than once per caller.
 */
final class Tokens {

	/**
	 * Builds the custom property declarations for a set of values.
	 *
	 * A property is emitted only when its schema declares `ui.token` and the
	 * value is a non empty scalar: booleans and lists have no meaningful CSS
	 * form, and an empty string would produce a declaration that overrides the
	 * stylesheet default with nothing.
	 *
	 * @param array    $schema  Object schema declaring the properties.
	 * @param array    $values  Resolved values keyed by property name.
	 * @param string[] $exclude Property keys to leave out.
	 * @return string Semicolon separated declarations, unescaped.
	 */
	public static function declarations( array $schema, array $values, array $exclude = array() ) {
		if ( empty( $schema['properties'] ) || ! is_array( $schema['properties'] ) ) {
			return '';
		}

		$declarations = array();

		foreach ( $schema['properties'] as $key => $property ) {
			$property = (array) $property;

			if ( in_array( $key, $exclude, true ) ) {
				continue;
			}

			if ( empty( $property['ui']['token'] ) || ! isset( $values[ $key ] ) ) {
				continue;
			}

			$value = $values[ $key ];

			if ( is_bool( $value ) || is_array( $value ) || '' === $value ) {
				continue;
			}

			$unit = isset( $property['ui']['unit'] ) ? $property['ui']['unit'] : '';

			$declarations[] = sprintf(
				'%s:%s',
				self::name( $property['ui']['token'] ),
				$value . ( is_numeric( $value ) ? $unit : '' )
			);
		}

		return implode( ';', $declarations );
	}

	/**
	 * Reduces a token name to characters a custom property may contain.
	 *
	 * The hint comes from a schema, which a filter can extend, so it is
	 * cleaned rather than trusted.
	 *
	 * @param string $token Declared token name.
	 * @return string
	 */
	private static function name( $token ) {
		return preg_replace( '/[^a-zA-Z0-9_-]/', '', (string) $token );
	}
}
