<?php
/** Builder-unabhängiges, validiertes Designprofil. */

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

function wpagent_design_profile_schema(): array {
	$token_map = array( 'type' => 'object', 'additionalProperties' => array( 'type' => 'string' ) );
	return array( 'type' => 'object', 'properties' => array(
		'colors' => $token_map, 'font_families' => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ),
		'base_size' => array( 'type' => 'string' ), 'spacing' => $token_map, 'radius' => $token_map, 'notes' => array( 'type' => 'string' ),
	) );
}

function wpagent_ability_get_design_profile(): array {
	return array(
		'label' => __( 'Designprofil lesen', 'wp-agent-companion' ), 'description' => __( 'Liest builder-unabhängige Farb-, Schrift-, Abstands- und Radius-Tokens.', 'wp-agent-companion' ), 'category' => 'wp-agent',
		'input_schema' => array( 'type' => 'object', 'properties' => array(), 'default' => array() ), 'output_schema' => array( 'type' => 'object', 'properties' => array( 'profile' => wpagent_design_profile_schema() ) ),
		'execute_callback' => 'wpagent_exec_get_design_profile', 'permission_callback' => 'wpagent_perm_edit_posts',
		'meta' => array(
			'show_in_rest' => true,
			'annotations'  => array( 'readonly' => true, 'destructive' => false, 'idempotent' => true ),
			'mcp'          => array(
				'uri'         => 'wpagently://site/design',
				'annotations' => array( 'audience' => array( 'assistant' ) ),
			),
		),
	);
}
function wpagent_ability_set_design_profile(): array {
	return array(
		'label' => __( 'Designprofil speichern', 'wp-agent-companion' ), 'description' => __( 'Validiert und speichert builder-unabhängige Design-Tokens.', 'wp-agent-companion' ), 'category' => 'wp-agent',
		'input_schema' => wpagent_design_profile_schema(), 'output_schema' => array( 'type' => 'object', 'properties' => array( 'profile' => wpagent_design_profile_schema(), 'verified' => array( 'type' => 'boolean' ) ) ),
		'execute_callback' => 'wpagent_exec_set_design_profile', 'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => true ) ),
	);
}

/** @return string|WP_Error */
function wpagent_design_font_family_value( $value ) {
	$clean = wpagent_state_text( $value, 100, 'font_family' );
	if ( is_wp_error( $clean ) || '' === $clean ) {
		return $clean;
	}
	if (
		false !== strpbrk( $clean, ";{}()[]\\" )
		|| str_contains( $clean, '/*' )
		|| preg_match( '/^(?:inherit|initial|revert(?:-layer)?|unset)$/i', $clean )
		|| preg_match( '/^\d+(?:\.\d+)?(?:px|rem|em|%|vw|vh|pt|pc|cm|mm|in|ex|ch)$/i', $clean )
	) {
		return new WP_Error( 'wpagent_design_bad_font', __( 'Schriftfamilien dürfen nur sichere, literale CSS-Schriftlisten enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}

	$characters = preg_split( '//u', $clean, -1, PREG_SPLIT_NO_EMPTY );
	if ( false === $characters ) {
		return new WP_Error( 'wpagent_design_bad_font', __( 'Schriftfamilien dürfen nur sichere, literale CSS-Schriftlisten enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$items   = array();
	$current = '';
	$quote   = '';
	foreach ( $characters as $character ) {
		if ( '' !== $quote ) {
			$current .= $character;
			if ( $character === $quote ) {
				$quote = '';
			}
			continue;
		}
		if ( '"' === $character || "'" === $character ) {
			$quote = $character;
			$current .= $character;
		} elseif ( ',' === $character ) {
			$items[] = trim( $current );
			$current = '';
		} else {
			$current .= $character;
		}
	}
	$items[] = trim( $current );
	if ( '' !== $quote || count( $items ) > 20 || in_array( '', $items, true ) ) {
		return new WP_Error( 'wpagent_design_bad_font', __( 'Schriftfamilien dürfen nur sichere, literale CSS-Schriftlisten enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}

	foreach ( $items as $item ) {
		$first = $item[0];
		$last  = $item[ strlen( $item ) - 1 ];
		if ( '"' === $first || "'" === $first ) {
			if ( $last !== $first || strlen( $item ) < 3 ) {
				return new WP_Error( 'wpagent_design_bad_font', __( 'Schriftfamilien dürfen nur sichere, literale CSS-Schriftlisten enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			}
			$content = substr( $item, 1, -1 );
			$pattern = '"' === $first
				? "/^[\\p{L}\\p{M}\\p{N}\\s,._&+’'\\-]+$/u"
				: '/^[\\p{L}\\p{M}\\p{N}\\s,._&+’"\\-]+$/u';
		} else {
			$content = $item;
			$pattern = '/^[\\p{L}\\p{M}\\p{N}\\s._&+’\\-]+$/u';
		}
		if ( 1 !== preg_match( $pattern, $content ) || 1 !== preg_match( '/\p{L}/u', $content ) ) {
			return new WP_Error( 'wpagent_design_bad_font', __( 'Schriftfamilien dürfen nur sichere, literale CSS-Schriftlisten enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
	}

	return $clean;
}

function wpagent_exec_get_design_profile( $input = array() ): array {
	return array( 'profile' => wpagent_design_profile_for_output( wpagent_normalized_design_profile() ) );
}

/**
 * Übernimmt sichere, literal definierte Tokens aus den effektiven globalen
 * Theme-Einstellungen. Dynamische CSS-Ausdrücke werden bewusst ausgelassen,
 * weil das portable Profil nur validierte Hex-Farben und Längen speichert.
 *
 * @return array<string,mixed>|WP_Error
 */
function wpagent_companion_capture_theme_design_profile() {
	$settings = function_exists( 'wp_get_global_settings' ) ? wp_get_global_settings() : array();
	$settings = apply_filters( 'wpagent_companion_global_settings', $settings );
	$settings = is_array( $settings ) ? $settings : array();
	$current  = wpagent_normalized_design_profile();
	$colors   = array();
	$font_origins = array( 'default' => array(), 'theme' => array(), 'custom' => array() );
	$spacing  = array();

	// WordPress resolves presets from defaults through theme values to user customizations.
	// Later origins must therefore overwrite earlier values with the same slug.
	foreach ( array( 'default', 'theme', 'custom' ) as $origin ) {
		$palette = $settings['color']['palette'][ $origin ] ?? array();
		foreach ( is_array( $palette ) ? $palette : array() as $item ) {
			if ( ! is_array( $item ) || ! isset( $item['slug'], $item['color'] ) ) {
				continue;
			}
			$slug  = sanitize_key( (string) $item['slug'] );
			$color = (string) $item['color'];
			if ( '' !== $slug && preg_match( '/^#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{5})?$/', $color ) ) {
				$colors[ $slug ] = $color;
			}
		}
		$families = $settings['typography']['fontFamilies'][ $origin ] ?? array();
		foreach ( is_array( $families ) ? $families : array() as $item ) {
			if ( count( $font_origins[ $origin ] ) >= 10 ) {
				break;
			}
			$family = is_array( $item ) && isset( $item['fontFamily'] ) ? trim( (string) $item['fontFamily'] ) : '';
			$clean  = wpagent_design_font_family_value( $family );
			if ( ! is_wp_error( $clean ) && '' !== $clean && ! in_array( $clean, $font_origins[ $origin ], true ) ) {
				$font_origins[ $origin ][] = $clean;
			}
		}
		$sizes = $settings['spacing']['spacingSizes'][ $origin ] ?? array();
		foreach ( is_array( $sizes ) ? $sizes : array() as $item ) {
			if ( ! is_array( $item ) || ! isset( $item['slug'], $item['size'] ) ) {
				continue;
			}
			$slug = sanitize_key( (string) $item['slug'] );
			$size = (string) $item['size'];
			if ( '' !== $slug && preg_match( '/^(?:0|\d+(?:\.\d+)?(?:px|rem|em|%|vw|vh))$/', $size ) ) {
				$spacing[ $slug ] = $size;
			}
		}
	}
	$fonts = array();
	foreach ( array( 'custom', 'theme', 'default' ) as $origin ) {
		foreach ( $font_origins[ $origin ] as $font ) {
			if ( ! in_array( $font, $fonts, true ) ) {
				$fonts[] = $font;
			}
		}
	}

	return wpagent_exec_set_design_profile(
		array(
			'colors'        => array_slice( empty( $colors ) ? $current['colors'] : $colors, 0, 30, true ),
			'font_families' => array_slice( empty( $fonts ) ? $current['font_families'] : $fonts, 0, 10 ),
			'base_size'     => $current['base_size'] ?? '',
			'spacing'       => array_slice( empty( $spacing ) ? $current['spacing'] : $spacing, 0, 30, true ),
			'radius'        => $current['radius'] ?? array(),
			'notes'         => $current['notes'] ?? '',
		)
	);
}

/** @return array<string,string>|WP_Error */
function wpagent_design_token_map( $raw, string $kind ) {
	if ( null === $raw ) {
		return array();
	}
	if ( ! is_array( $raw ) || ( ! empty( $raw ) && array_is_list( $raw ) ) || count( $raw ) > 30 ) {
		/* translators: %s: design token group name, for example colors or spacing. */
		return new WP_Error( 'wpagent_design_bad_tokens', sprintf( __( '%s muss eine Map mit höchstens 30 Tokens sein.', 'wp-agent-companion' ), $kind ), array( 'status' => 400 ) );
	}
	$result = array();
	foreach ( $raw as $name => $value ) {
		$raw_name = (string) $name;
		$key      = sanitize_key( $raw_name );
		if ( '' === $key || $key !== $raw_name || strlen( $raw_name ) > 64 || ! is_string( $value ) || strlen( $value ) > 100 ) {
			return new WP_Error( 'wpagent_design_bad_tokens', __( 'Token-Namen und Werte sind ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		if ( 'colors' === $kind ) {
			if ( ! preg_match( '/^#[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{5})?$/', $value ) ) {
				return new WP_Error( 'wpagent_design_bad_color', __( 'Farben müssen als Hex-Wert mit 3, 6 oder 8 Stellen angegeben werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			}
		} elseif ( ! preg_match( '/^(?:0|\d+(?:\.\d+)?(?:px|rem|em|%|vw|vh))$/', $value ) ) {
			return new WP_Error( 'wpagent_design_bad_length', __( 'Abstände und Radien müssen sichere CSS-Längen sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$result[ $key ] = $value;
	}
	return $result;
}

/** @return array<string,mixed> */
function wpagent_normalized_design_profile(): array {
	$stored = wpagent_state_array_option( WPAGENT_COMPANION_OPT_DESIGN_PROFILE );
	$maps   = array();
	foreach ( array( 'colors', 'spacing', 'radius' ) as $kind ) {
		$value = wpagent_design_token_map( $stored[ $kind ] ?? array(), $kind );
		$maps[ $kind ] = is_wp_error( $value ) ? array() : $value;
	}

	$fonts = array();
	$raw_fonts = $stored['font_families'] ?? array();
	if ( is_array( $raw_fonts ) && count( $raw_fonts ) <= 10 ) {
		$fonts_valid = true;
		foreach ( $raw_fonts as $font ) {
			$clean = wpagent_design_font_family_value( $font );
			if ( is_wp_error( $clean ) ) {
				$fonts_valid = false;
				break;
			}
			if ( '' !== $clean ) {
				$fonts[] = $clean;
			}
		}
		if ( ! $fonts_valid ) {
			$fonts = array();
		}
	}

	$base_size = $stored['base_size'] ?? '';
	if ( ! is_string( $base_size ) || ( '' !== $base_size && ! preg_match( '/^(?:\d+(?:\.\d+)?(?:px|rem|em|%))$/', $base_size ) ) ) {
		$base_size = '';
	}
	$notes = wpagent_state_text( $stored['notes'] ?? '', 5000, 'notes' );
	if ( is_wp_error( $notes ) ) {
		$notes = '';
	}

	return array(
		'colors'        => $maps['colors'],
		'font_families' => array_values( array_unique( $fonts ) ),
		'base_size'     => $base_size,
		'spacing'       => $maps['spacing'],
		'radius'        => $maps['radius'],
		'notes'         => $notes,
	);
}

/**
 * Leere PHP-Arrays werden als JSON-Listen kodiert. Token-Maps sind laut
 * Ability-Schema jedoch Objekte und müssen deshalb leer als {} erscheinen.
 *
 * @param array<string,mixed> $profile
 * @return array<string,mixed>
 */
function wpagent_design_profile_for_output( array $profile ): array {
	foreach ( array( 'colors', 'spacing', 'radius' ) as $kind ) {
		if ( isset( $profile[ $kind ] ) && array() === $profile[ $kind ] ) {
			$profile[ $kind ] = (object) array();
		}
	}
	return $profile;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_validate_design_profile( $input ) {
	$input   = is_array( $input ) ? $input : array();
	$colors  = wpagent_design_token_map( $input['colors'] ?? array(), 'colors' );
	$spacing = wpagent_design_token_map( $input['spacing'] ?? array(), 'spacing' );
	$radius  = wpagent_design_token_map( $input['radius'] ?? array(), 'radius' );
	foreach ( array( $colors, $spacing, $radius ) as $value ) {
		if ( is_wp_error( $value ) ) {
			return $value;
		}
	}
	if ( array_key_exists( 'font_families', $input ) && ! is_array( $input['font_families'] ) ) {
		return new WP_Error( 'wpagent_design_bad_fonts', __( 'font_families muss eine Liste mit Schriftfamilien sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$fonts = $input['font_families'] ?? array();
	if ( count( $fonts ) > 10 ) {
		return new WP_Error( 'wpagent_design_font_limit', __( 'Es sind höchstens 10 Schriftfamilien erlaubt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$clean_fonts = array();
	foreach ( $fonts as $font ) {
		$clean = wpagent_design_font_family_value( $font );
		if ( is_wp_error( $clean ) ) {
			return $clean;
		}
		if ( '' !== $clean ) {
			$clean_fonts[] = $clean;
		}
	}
	$raw_base_size = $input['base_size'] ?? '';
	if ( ! is_string( $raw_base_size ) && ! is_numeric( $raw_base_size ) ) {
		return new WP_Error( 'wpagent_design_bad_base_size', __( 'base_size muss eine sichere CSS-Länge sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$base_size = (string) $raw_base_size;
	if ( '' !== $base_size && ! preg_match( '/^(?:\d+(?:\.\d+)?(?:px|rem|em|%))$/', $base_size ) ) {
		return new WP_Error( 'wpagent_design_bad_base_size', __( 'base_size muss eine sichere CSS-Länge sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$notes = wpagent_state_text( $input['notes'] ?? '', 5000, 'notes' );
	if ( is_wp_error( $notes ) ) {
		return $notes;
	}
	return array( 'colors' => $colors, 'font_families' => array_values( array_unique( $clean_fonts ) ), 'base_size' => $base_size, 'spacing' => $spacing, 'radius' => $radius, 'notes' => $notes );
}

/** @return array|WP_Error */
function wpagent_exec_set_design_profile( $input ) {
	$profile = wpagent_validate_design_profile( $input );
	if ( is_wp_error( $profile ) ) {
		return $profile;
	}
	$persisted = wpagent_state_persist_options(
		array( WPAGENT_COMPANION_OPT_DESIGN_PROFILE => $profile ),
		'wpagent_design_persist_failed',
		__( 'Designprofil konnte nicht verifiziert gespeichert werden.', 'wp-agent-companion' )
	);
	if ( is_wp_error( $persisted ) ) {
		return $persisted;
	}
	return array( 'profile' => wpagent_design_profile_for_output( $profile ), 'verified' => true );
}
