<?php
/** Strict validation and semantic readback comparison for Astra writes. */

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

/** @return array<string,mixed>|WP_Error */
function wpagent_astra_validate_settings( string $operation_id, $settings ) {
	$catalog = wpagent_astra_operation_catalog();
	if ( ! isset( $catalog[ $operation_id ] ) ) {
		return new WP_Error( 'wpagent_astra_unknown_operation', __( 'Unbekannte Astra-Operation.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( ! is_array( $settings ) || ! $settings ) {
		return new WP_Error( 'wpagent_astra_no_changes', __( 'Gib mindestens eine zu ändernde Astra-Einstellung an.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$valid = rest_validate_value_from_schema( $settings, $catalog[ $operation_id ]['input_schema'], 'settings' );
	if ( is_wp_error( $valid ) ) {
		return new WP_Error( 'wpagent_astra_invalid_settings', $valid->get_error_message(), array( 'status' => 400 ) );
	}

	$settings = rest_sanitize_value_from_schema( $settings, $catalog[ $operation_id ]['input_schema'], 'settings' );
	if ( is_wp_error( $settings ) || ! is_array( $settings ) ) {
		return new WP_Error( 'wpagent_astra_invalid_settings', __( 'Die Astra-Einstellungen sind ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}

	if ( preg_match( '/^(?:body-font|heading-font|h[1-6]-font)$/', $operation_id ) ) {
		$settings = wpagent_astra_validate_font_settings( $settings );
		if ( is_wp_error( $settings ) ) {
			return $settings;
		}
	}
	if ( 'global-palette' === $operation_id ) {
		$settings = wpagent_astra_validate_palette_settings( $settings );
		if ( is_wp_error( $settings ) ) {
			return $settings;
		}
	}
	if ( 'background-colors' === $operation_id ) {
		$settings = wpagent_astra_validate_background_settings( $settings );
		if ( is_wp_error( $settings ) ) {
			return $settings;
		}
	}
	if ( 'buttons' === $operation_id ) {
		$settings = wpagent_astra_validate_button_settings( $settings );
		if ( is_wp_error( $settings ) ) {
			return $settings;
		}
	}
	if ( 'scroll-to-top' === $operation_id && isset( $settings['colors'] ) ) {
		foreach ( $settings['colors'] as $key => $value ) {
			if ( '' === $value ) {
				continue;
			}
			$color = wpagent_gs_validate_color( $value );
			if ( is_wp_error( $color ) ) {
				return new WP_Error( 'wpagent_astra_invalid_scroll_color', $color->get_error_message(), array( 'status' => 400 ) );
			}
			$settings['colors'][ $key ] = $color;
		}
	}
	if ( in_array( $operation_id, array( 'header-layout', 'footer-layout' ), true ) && ! wpagent_astra_lists_are_unique( $settings ) ) {
		return new WP_Error( 'wpagent_astra_duplicate_component', __( 'Eine Astra-Layoutzone darf dieselbe Komponente nicht mehrfach enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return $settings;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_astra_validate_palette_settings( array $settings ) {
	$map = array(
		'primary'              => 0,
		'primary_hover'        => 1,
		'heading'              => 2,
		'text'                 => 3,
		'background'           => 4,
		'secondary_background' => 5,
		'alternate_background' => 6,
		'subtle_background'    => 7,
		'supporting'           => 8,
	);
	$provider_colors = array();
	foreach ( $settings['colors'] ?? array() as $name => $value ) {
		$color = wpagent_gs_validate_color( $value );
		if ( is_wp_error( $color ) || ! isset( $map[ $name ] ) ) {
			return new WP_Error( 'wpagent_astra_invalid_palette_color', __( 'Die Astra-Palette enthält eine ungültige Farbe.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$provider_colors[ $map[ $name ] ] = $color;
	}
	if ( ! $provider_colors ) {
		return new WP_Error( 'wpagent_astra_no_changes', __( 'Gib mindestens eine Palettenfarbe an.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	ksort( $provider_colors, SORT_NUMERIC );
	return array( 'colors' => $provider_colors );
}

function wpagent_astra_lists_are_unique( $value ): bool {
	if ( ! is_array( $value ) ) {
		return true;
	}
	if ( array_is_list( $value ) ) {
		$all_strings = true;
		foreach ( $value as $item ) {
			if ( ! is_string( $item ) ) {
				$all_strings = false;
				if ( ! wpagent_astra_lists_are_unique( $item ) ) {
					return false;
				}
			}
		}
		return ! $all_strings || count( $value ) === count( array_unique( $value, SORT_STRING ) );
	}
	foreach ( $value as $child ) {
		if ( ! wpagent_astra_lists_are_unique( $child ) ) {
			return false;
		}
	}
	return true;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_astra_validate_font_settings( array $settings ) {
	if ( isset( $settings['font_family'] ) ) {
		$family = trim( $settings['font_family'] );
		if ( ! preg_match( '/^[\p{L}\p{N}][\p{L}\p{N} ._-]{0,99}$/u', $family ) || sanitize_text_field( $family ) !== $family ) {
			return new WP_Error( 'wpagent_astra_invalid_font_family', __( 'Der Fontname enthält nicht erlaubte Zeichen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$settings['font_family'] = $family;
	}
	if ( isset( $settings['line_height'] ) ) {
		$value = trim( $settings['line_height'] );
		if ( ! preg_match( '/^(?:0|[1-9][0-9]?)(?:\.[0-9]{1,3})?(?:px|em|rem)?$/', $value ) ) {
			return new WP_Error( 'wpagent_astra_invalid_line_height', __( 'Die Zeilenhöhe ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$settings['line_height'] = $value;
	}
	if ( isset( $settings['letter_spacing'] ) ) {
		$value = trim( $settings['letter_spacing'] );
		if ( ! preg_match( '/^-?(?:0|[1-9][0-9]?)(?:\.[0-9]{1,3})?(?:px|em|rem)?$/', $value ) ) {
			return new WP_Error( 'wpagent_astra_invalid_letter_spacing', __( 'Der Zeichenabstand ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$settings['letter_spacing'] = $value;
	}
	return $settings;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_astra_validate_background_settings( array $settings ) {
	foreach ( array( 'site_background', 'content_background' ) as $scope ) {
		if ( ! isset( $settings[ $scope ] ) ) {
			continue;
		}
		foreach ( $settings[ $scope ] as $device => $values ) {
			$color = wpagent_gs_validate_color( $values['background-color'] ?? null );
			if ( is_wp_error( $color ) ) {
				return new WP_Error( 'wpagent_astra_invalid_background_color', $color->get_error_message(), array( 'status' => 400 ) );
			}
			$settings[ $scope ][ $device ]['background-color'] = $color;
		}
	}
	return $settings;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_astra_validate_button_settings( array $settings ) {
	$mutable = array_diff( array_keys( $settings ), array( 'button_type' ) );
	if ( ! $mutable ) {
		return new WP_Error( 'wpagent_astra_no_changes', __( 'Gib neben dem Button-Typ mindestens eine zu ändernde Eigenschaft an.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$settings['button_type'] = $settings['button_type'] ?? 'primary';
	foreach ( array( 'text_color', 'text_hover_color', 'background_color', 'background_hover_color', 'border_color', 'border_hover_color' ) as $field ) {
		if ( ! isset( $settings[ $field ] ) ) {
			continue;
		}
		$color = wpagent_gs_validate_color( $settings[ $field ] );
		if ( is_wp_error( $color ) ) {
			return new WP_Error( 'wpagent_astra_invalid_button_color', $color->get_error_message(), array( 'status' => 400 ) );
		}
		$settings[ $field ] = $color;
	}
	return $settings;
}

function wpagent_astra_subset_matches( $expected, $actual ): bool {
	if ( is_array( $expected ) ) {
		if ( ! is_array( $actual ) ) {
			return false;
		}
		if ( array_is_list( $expected ) || array_is_list( $actual ) ) {
			return array_is_list( $expected ) && array_is_list( $actual ) && $expected === $actual;
		}
		foreach ( $expected as $key => $value ) {
			if ( ! array_key_exists( $key, $actual ) || ! wpagent_astra_subset_matches( $value, $actual[ $key ] ) ) {
				return false;
			}
		}
		return true;
	}
	return $expected === $actual;
}

/** @param array<string,mixed> $settings @param array<string,mixed> $current */
function wpagent_astra_readback_matches( string $operation_id, array $settings, array $current ): bool {
	if ( 'container' === $operation_id || 'background-colors' === $operation_id ) {
		return wpagent_astra_subset_matches( $settings, $current );
	}
	if ( preg_match( '/^(?:body-font|heading-font|h[1-6]-font)$/', $operation_id ) ) {
		$expected = array();
		if ( isset( $settings['font_family'] ) ) {
			$expected['font_family'] = "'" . $settings['font_family'] . "', sans-serif";
		}
		if ( isset( $settings['font_weight'] ) ) {
			$expected['font_weight'] = $settings['font_weight'];
		}
		if ( isset( $settings['font_size'] ) ) {
			$expected['font_size'] = $settings['font_size'];
			foreach ( $expected['font_size'] as $key => $value ) {
				if ( array_key_exists( $key, $current['font_size'] ?? array() ) ) {
					$current['font_size'][ $key ] = (string) $current['font_size'][ $key ];
				}
			}
		}
		$extra_map = array( 'line_height' => 'line-height', 'text_transform' => 'text-transform', 'letter_spacing' => 'letter-spacing' );
		foreach ( $extra_map as $input_key => $provider_key ) {
			if ( array_key_exists( $input_key, $settings ) ) {
				$expected['font_extras'][ $provider_key ] = $settings[ $input_key ];
			}
		}
		return wpagent_astra_subset_matches( $expected, $current );
	}
	if ( 'buttons' === $operation_id ) {
		$type    = $settings['button_type'] ?? 'primary';
		$targets = 'both' === $type ? array( 'primary_button', 'secondary_button' ) : array( $type . '_button' );
		$values  = array_diff_key( $settings, array( 'button_type' => true ) );
		foreach ( $targets as $target ) {
			if ( ! isset( $current[ $target ] ) || ! wpagent_astra_subset_matches( $values, $current[ $target ] ) ) {
				return false;
			}
		}
		return true;
	}
	if ( 'global-palette' === $operation_id ) {
		$colors = $current['active_palette']['colors'] ?? null;
		if ( ! is_array( $colors ) ) {
			return false;
		}
		foreach ( $settings['colors'] ?? array() as $index => $color ) {
			$actual = $colors[ $index ]['hex'] ?? null;
			if ( ! is_string( $actual ) || 0 !== strcasecmp( $color, $actual ) ) {
				return false;
			}
		}
		return true;
	}
	return wpagent_astra_subset_matches( $settings, $current );
}
