<?php
/**
 * Validates widget settings against Elementor control schemas.
 *
 * @package Elementor_MCP
 * @since   1.0.0
 */

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

/**
 * Validates widget settings against their control definitions.
 *
 * @since 1.0.0
 */
class Elementor_MCP_Settings_Validator {

	/**
	 * The schema generator instance.
	 *
	 * @var Elementor_MCP_Schema_Generator
	 */
	private $schema_generator;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 *
	 * @param Elementor_MCP_Schema_Generator $schema_generator The schema generator.
	 */
	public function __construct( Elementor_MCP_Schema_Generator $schema_generator ) {
		$this->schema_generator = $schema_generator;
	}

	/**
	 * Responsive breakpoint suffixes generated by Elementor's add_responsive_control().
	 *
	 * @var string[]
	 */
	private static $responsive_suffixes = array(
		'_mobile',
		'_tablet',
		'_widescreen',
		'_laptop',
		'_mobile_extra',
		'_tablet_extra',
	);

	/**
	 * Common advanced control keys valid for ALL widget types.
	 * From Elementor's Widget_Common_Base (common-base.php).
	 *
	 * @var string[]
	 */
	private static $common_advanced_keys = array(
		// Positioning.
		'_element_width', '_element_custom_width', '_element_vertical_align',
		'_position', '_offset_orientation_h', '_offset_x', '_offset_x_end',
		'_offset_orientation_v', '_offset_y', '_offset_y_end',
		'_z_index', '_element_id', '_css_classes',
		// Spacing.
		'_margin', '_padding',
		// Background (normal + hover).
		'_background_background', '_background_color', '_background_image',
		'_background_position', '_background_attachment', '_background_repeat',
		'_background_size', '_background_bg_width',
		'_background_hover_background', '_background_hover_color',
		'_background_hover_transition',
		// Border (normal + hover).
		'_border_border', '_border_width', '_border_color',
		'_border_radius', '_border_hover_border', '_border_hover_width',
		'_border_hover_color', '_border_radius_hover',
		'_border_hover_transition',
		// Box shadow (normal + hover).
		'_box_shadow_box_shadow_type', '_box_shadow_box_shadow',
		'_box_shadow_hover_box_shadow_type', '_box_shadow_hover_box_shadow',
		// Mask.
		'_mask_switch', '_mask_shape', '_mask_image',
		'_mask_size', '_mask_size_scale',
		'_mask_position', '_mask_position_x', '_mask_position_y',
		'_mask_repeat',
		// Animation.
		'_animation', 'animation_duration', '_animation_delay',
		// Flex item.
		'_flex_align_self', '_flex_order', '_flex_order_custom',
		'_flex_size', '_flex_grow', '_flex_shrink',
		// Grid item.
		'_grid_column', '_grid_column_custom', '_grid_row', '_grid_row_custom',
		// Responsive visibility.
		'hide_desktop', 'hide_tablet', 'hide_mobile',
		// Motion effects (Pro) — actual Elementor schema keys use motion_fx_ prefix.
		'motion_fx_motion_fx_scrolling', 'motion_fx_motion_fx_mouse',
		'motion_fx_devices', 'motion_fx_range',
		'motion_fx_translateY_effect', 'motion_fx_translateY_direction',
		'motion_fx_translateY_speed', 'motion_fx_translateY_affectedRange',
		'motion_fx_translateX_effect', 'motion_fx_translateX_direction',
		'motion_fx_translateX_speed', 'motion_fx_translateX_affectedRange',
		'motion_fx_opacity_effect', 'motion_fx_opacity_direction',
		'motion_fx_opacity_level', 'motion_fx_opacity_range',
		'motion_fx_blur_effect', 'motion_fx_blur_direction',
		'motion_fx_blur_level', 'motion_fx_blur_range',
		'motion_fx_rotateZ_effect', 'motion_fx_rotateZ_direction',
		'motion_fx_rotateZ_speed', 'motion_fx_rotateZ_affectedRange',
		'motion_fx_scale_effect', 'motion_fx_scale_direction',
		'motion_fx_scale_speed', 'motion_fx_scale_range',
		'motion_fx_tilt_effect', 'motion_fx_tilt_direction', 'motion_fx_tilt_speed',
		'motion_fx_mouseTrack_effect', 'motion_fx_mouseTrack_direction', 'motion_fx_mouseTrack_speed',
		'motion_fx_transform_origin_x', 'motion_fx_transform_origin_y',
		'motion_fx_transform_x_anchor_point', 'motion_fx_transform_y_anchor_point',
		'handle_motion_fx_asset_loading',
		// CSS Transform controls.
		'_transform_rotate_popover', '_transform_rotateZ_effect',
		'_transform_rotate_3d', '_transform_rotateX_effect', '_transform_rotateY_effect',
		'_transform_translate_popover', '_transform_translateX_effect', '_transform_translateY_effect',
		'_transform_scale_popover', '_transform_scale_effect',
		'_transform_scaleX_effect', '_transform_scaleY_effect',
		'_transform_rotate_popover_hover', '_transform_rotateZ_effect_hover',
		'_transform_rotate_3d_hover', '_transform_rotateX_effect_hover', '_transform_rotateY_effect_hover',
		'_transform_translate_popover_hover', '_transform_translateX_effect_hover', '_transform_translateY_effect_hover',
		'_transform_scale_popover_hover', '_transform_scale_effect_hover',
		'_transform_scaleX_effect_hover', '_transform_scaleY_effect_hover',
	);

	/**
	 * Validates settings for a widget type.
	 *
	 * @since 1.0.0
	 *
	 * @param string $widget_type The widget type name.
	 * @param array  $settings    The settings to validate.
	 * @return true|\WP_Error True if valid, WP_Error on failure.
	 */
	public function validate( string $widget_type, array $settings ) {
		$schema = $this->schema_generator->generate( $widget_type );

		if ( is_wp_error( $schema ) ) {
			return $schema;
		}

		$valid_keys = array_keys( $schema['properties'] ?? array() );

		foreach ( array_keys( $settings ) as $key ) {
			// Direct match against widget schema.
			if ( in_array( $key, $valid_keys, true ) ) {
				continue;
			}

			// Common advanced controls valid for all widgets.
			if ( in_array( $key, self::$common_advanced_keys, true ) ) {
				continue;
			}

			// Check responsive variant: strip suffix and test base key.
			if ( $this->is_responsive_variant( $key, $valid_keys ) ) {
				continue;
			}

			// Group control sub-keys: if 'text_stroke_text_stroke' is valid,
			// allow 'text_stroke_stroke_width', 'text_stroke_stroke_color', etc.
			if ( $this->is_group_control_subkey( $key, $valid_keys ) ) {
				continue;
			}

			return new \WP_Error(
				'invalid_setting',
				sprintf(
					/* translators: 1: setting key, 2: widget type */
					__( 'Setting "%1$s" is not a valid control for widget type "%2$s".', 'elementor-mcp' ),
					$key,
					$widget_type
				)
			);
		}

		return true;
	}

	/**
	 * Checks if a key is a responsive variant of a valid schema key or common key.
	 *
	 * @param string   $key        The settings key to check.
	 * @param string[] $valid_keys Valid keys from the widget schema.
	 * @return bool
	 */
	/**
	 * Checks if a key is a sub-field of a group control that has other keys in the schema.
	 *
	 * Group controls in Elementor use a shared prefix (e.g. 'text_stroke_', 'typography_',
	 * 'border_'). If any key with the same prefix exists in the schema, other sub-fields
	 * with that prefix are also valid.
	 *
	 * @param string   $key        The settings key to check.
	 * @param string[] $valid_keys Valid keys from the widget schema.
	 * @return bool
	 */
	private function is_group_control_subkey( string $key, array $valid_keys ): bool {
		// Try progressively shorter prefixes (split on '_').
		$parts = explode( '_', $key );
		// Need at least 2 parts for a group prefix (e.g. 'text_stroke' from 'text_stroke_stroke_width').
		for ( $i = 1, $len = count( $parts ) - 1; $i <= $len; $i++ ) {
			$prefix = implode( '_', array_slice( $parts, 0, $i ) ) . '_';
			foreach ( $valid_keys as $valid_key ) {
				if ( str_starts_with( $valid_key, $prefix ) && $valid_key !== $key ) {
					return true;
				}
			}
		}
		return false;
	}

	private function is_responsive_variant( string $key, array $valid_keys ): bool {
		foreach ( self::$responsive_suffixes as $suffix ) {
			if ( str_ends_with( $key, $suffix ) ) {
				$base_key = substr( $key, 0, -strlen( $suffix ) );
				if ( in_array( $base_key, $valid_keys, true ) ) {
					return true;
				}
				if ( in_array( $base_key, self::$common_advanced_keys, true ) ) {
					return true;
				}
			}
		}

		return false;
	}
}
