<?php
/**
 * Chip card shape presets and CSS variable helpers.
 *
 * @package Rsmspark_Content_Widgets
 */

defined( 'ABSPATH' ) || exit;

/**
 * Maps design controls to CSS custom properties.
 */
final class Rsmspark_Content_Widgets_Features_Styles {

	/**
	 * Corner / shape preset keys.
	 *
	 * @return string[]
	 */
	public static function shape_keys() {
		return array( 'inherit', 'none', 'small', 'medium', 'large', 'pill' );
	}

	/**
	 * SiteOrigin select options for chip corner presets.
	 *
	 * @param bool $include_inherit Include inherit option.
	 * @return array<string, string>
	 */
	public static function get_shape_options( $include_inherit = false ) {
		$options = array();
		if ( $include_inherit ) {
			$options['inherit'] = __( 'Use widget default', 'rsm-content-widgets' );
		}
		$options['none']   = __( 'Square', 'rsm-content-widgets' );
		$options['small']  = __( 'Slightly rounded', 'rsm-content-widgets' );
		$options['medium'] = __( 'Rounded', 'rsm-content-widgets' );
		$options['large']  = __( 'Very rounded', 'rsm-content-widgets' );
		$options['pill']   = __( 'Pill', 'rsm-content-widgets' );
		return $options;
	}

	/**
	 * Chip layout modes.
	 *
	 * @return array<string, string>
	 */
	public static function get_chip_layout_options() {
		return array(
			'stacked'    => __( 'Stacked (icon above text)', 'rsm-content-widgets' ),
			'media-left' => __( 'Icon left, text top-aligned', 'rsm-content-widgets' ),
		);
	}

	/**
	 * Sanitize chip layout key.
	 *
	 * @param mixed $value Raw value.
	 * @return string
	 */
	public static function sanitize_chip_layout( $value ) {
		$key = sanitize_key( (string) $value );
		return array_key_exists( $key, self::get_chip_layout_options() ) ? $key : 'stacked';
	}

	/**
	 * Chip surface style presets.
	 *
	 * @return array<string, string>
	 */
	public static function get_chip_style_options() {
		return array(
			'soft'     => __( 'Soft tint', 'rsm-content-widgets' ),
			'bordered' => __( 'Bordered card', 'rsm-content-widgets' ),
			'filled'   => __( 'Filled accent', 'rsm-content-widgets' ),
			'outline'  => __( 'Outline only', 'rsm-content-widgets' ),
		);
	}

	/**
	 * Convert a shape preset to border-radius CSS.
	 *
	 * @param string $shape Shape key.
	 * @return string
	 */
	public static function shape_to_radius( $shape ) {
		switch ( sanitize_key( (string) $shape ) ) {
			case 'small':
				return '4px';
			case 'medium':
				return '8px';
			case 'large':
				return '16px';
			case 'pill':
				return '999px';
			case 'none':
				return '0';
			default:
				return '8px';
		}
	}

	/**
	 * Resolve effective shape: per-chip override or widget default.
	 *
	 * @param string $chip_shape  Per-chip shape (may be inherit).
	 * @param string $default_shape Widget-level default.
	 * @return string
	 */
	public static function resolve_shape( $chip_shape, $default_shape ) {
		$chip_shape = sanitize_key( (string) $chip_shape );
		if ( 'inherit' !== $chip_shape && in_array( $chip_shape, self::shape_keys(), true ) ) {
			return $chip_shape;
		}
		$default_shape = sanitize_key( (string) $default_shape );
		if ( in_array( $default_shape, self::shape_keys(), true ) && 'inherit' !== $default_shape ) {
			return $default_shape;
		}
		return 'medium';
	}

	/**
	 * Icon size preset map — evenly spaced from 10px to 100px.
	 *
	 * @return array<string, int>
	 */
	public static function get_icon_size_presets() {
		return array(
			'xsmall' => 10,
			'small'  => 25,
			'medium' => 40,
			'large'  => 55,
			'xl'     => 70,
			'xxl'    => 85,
			'jumbo'  => 100,
		);
	}

	/**
	 * Minimum / maximum icon size for the custom slider (px).
	 *
	 * @return array{min: int, max: int, default: int}
	 */
	public static function get_icon_size_custom_bounds() {
		return array(
			'min'     => 1,
			'max'     => 200,
			'default' => 40,
		);
	}

	/**
	 * Preset picker range (px) for admin descriptions.
	 *
	 * @return array{min: int, max: int}
	 */
	public static function get_icon_size_preset_bounds() {
		$presets = self::get_icon_size_presets();

		return array(
			'min' => (int) min( $presets ),
			'max' => (int) max( $presets ),
		);
	}

	/**
	 * Icon size preset keys.
	 *
	 * @return string[]
	 */
	public static function icon_size_keys() {
		return array_merge(
			array( 'inherit' ),
			array_keys( self::get_icon_size_presets() ),
			array( 'xlarge', 'custom' )
		);
	}

	/**
	 * Icon size pulldown options.
	 *
	 * @param bool $include_inherit Include inherit option (per-chip overrides).
	 * @return array<string, string>
	 */
	public static function get_icon_size_options( $include_inherit = false ) {
		$options = array();
		if ( $include_inherit ) {
			$options['inherit'] = __( 'Use widget default', 'rsm-content-widgets' );
		}

		$labels = array(
			'xsmall' => __( 'Extra small', 'rsm-content-widgets' ),
			'small'  => __( 'Small', 'rsm-content-widgets' ),
			'medium' => __( 'Medium', 'rsm-content-widgets' ),
			'large'  => __( 'Large', 'rsm-content-widgets' ),
			'xl'     => __( 'XL', 'rsm-content-widgets' ),
			'xxl'    => __( 'XXL', 'rsm-content-widgets' ),
			'jumbo'  => __( 'Jumbo', 'rsm-content-widgets' ),
		);

		foreach ( self::get_icon_size_presets() as $key => $px ) {
			$label           = isset( $labels[ $key ] ) ? $labels[ $key ] : ucfirst( $key );
			$options[ $key ] = sprintf(
				/* translators: 1: size label, 2: pixel value */
				__( '%1$s (%2$dpx)', 'rsm-content-widgets' ),
				$label,
				$px
			);
		}

		$options['custom'] = __( 'Custom (slider)', 'rsm-content-widgets' );
		return $options;
	}

	/**
	 * Icon horizontal alignment options.
	 *
	 * @param bool $include_inherit Include inherit option (per-chip overrides).
	 * @return array<string, string>
	 */
	public static function get_icon_align_options( $include_inherit = false ) {
		$options = array();
		if ( $include_inherit ) {
			$options['inherit'] = __( 'Use widget default', 'rsm-content-widgets' );
		}
		$options['left']   = __( 'Left', 'rsm-content-widgets' );
		$options['center'] = __( 'Center', 'rsm-content-widgets' );
		$options['right']  = __( 'Right', 'rsm-content-widgets' );
		return $options;
	}

	/**
	 * Map an icon size preset to pixels.
	 *
	 * @param string $preset Preset key.
	 * @return int
	 */
	public static function icon_preset_to_px( $preset ) {
		$presets = self::get_icon_size_presets();
		$key     = sanitize_key( (string) $preset );

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

		// Legacy layouts saved with the previous xlarge key.
		if ( 'xlarge' === $key ) {
			return $presets['jumbo'];
		}

		return $presets['medium'];
	}

	/**
	 * Resolve icon size preset + optional custom slider to CSS.
	 *
	 * @param string $preset      Preset key.
	 * @param int    $custom_size Slider value when preset is custom.
	 * @return string
	 */
	public static function resolve_icon_size( $preset, $custom_size = 0 ) {
		$preset = sanitize_key( (string) $preset );
		$bounds = self::get_icon_size_custom_bounds();

		if ( 'custom' === $preset ) {
			$size = (int) $custom_size;
			if ( $size <= 0 ) {
				$size = $bounds['default'];
			}
			return max( $bounds['min'], min( $bounds['max'], $size ) ) . 'px';
		}
		return self::icon_preset_to_px( $preset ) . 'px';
	}

	/**
	 * Resolve per-chip icon size against widget default.
	 *
	 * @param string               $chip_preset   Per-chip preset (may be inherit).
	 * @param string               $default_preset Widget default preset.
	 * @param int                  $default_custom Widget custom slider value.
	 * @param int                  $chip_custom   Per-chip custom slider value.
	 * @return string
	 */
	public static function resolve_chip_icon_size( $chip_preset, $default_preset, $default_custom, $chip_custom = 0 ) {
		$chip_preset = sanitize_key( (string) $chip_preset );
		if ( 'inherit' !== $chip_preset && in_array( $chip_preset, self::icon_size_keys(), true ) ) {
			return self::resolve_icon_size( $chip_preset, $chip_custom );
		}
		return self::resolve_icon_size( $default_preset, $default_custom );
	}

	/**
	 * Resolve icon alignment: per-chip override or widget default.
	 *
	 * @param string $chip_align    Per-chip align (may be inherit).
	 * @param string $default_align Widget default.
	 * @return string
	 */
	public static function resolve_icon_align( $chip_align, $default_align ) {
		$chip_align = sanitize_key( (string) $chip_align );
		if ( 'inherit' !== $chip_align && in_array( $chip_align, array( 'left', 'center', 'right' ), true ) ) {
			return $chip_align;
		}
		$default_align = sanitize_key( (string) $default_align );
		if ( in_array( $default_align, array( 'left', 'center', 'right' ), true ) ) {
			return $default_align;
		}
		return 'left';
	}

	/**
	 * Font size: 0 means inherit theme Customizer body size.
	 *
	 * @param int $size Slider value in px.
	 * @return string
	 */
	public static function font_size_to_css( $size ) {
		$size = (int) $size;
		if ( $size <= 0 ) {
			return 'var(--openrsm-body-font-size, 1rem)';
		}
		return max( 10, min( 48, $size ) ) . 'px';
	}

	/**
	 * Build CSS variables for the widget root from design settings.
	 *
	 * @param array<string, mixed> $design Design section values.
	 * @return array<string, string>
	 */
	public static function build_css_variables( array $design ) {
		$px = static function ( $v, $fallback = 0 ) {
			return max( 0, (int) $v ) . 'px';
		};

		$default_shape = isset( $design['chip_shape'] ) ? sanitize_key( (string) $design['chip_shape'] ) : 'medium';
		if ( ! in_array( $default_shape, self::shape_keys(), true ) || 'inherit' === $default_shape ) {
			$default_shape = 'medium';
		}

		$title_font = Rsmspark_Content_Widgets_Features_Fonts::get_stack( $design['title_font'] ?? 'inherit' );
		$body_font  = Rsmspark_Content_Widgets_Features_Fonts::get_stack( $design['body_font'] ?? 'inherit' );

		$chip_style = isset( $design['chip_style'] ) ? sanitize_key( (string) $design['chip_style'] ) : 'soft';
		if ( ! array_key_exists( $chip_style, self::get_chip_style_options() ) ) {
			$chip_style = 'soft';
		}

		$columns = isset( $design['columns'] ) ? (int) $design['columns'] : 3;
		$columns = max( 1, min( 6, $columns ) );

		// Back-compat: legacy icon_size slider (pre-preset layouts).
		$icon_preset = isset( $design['icon_size_preset'] ) ? sanitize_key( (string) $design['icon_size_preset'] ) : '';
		$icon_custom = isset( $design['icon_size_custom'] ) ? (int) $design['icon_size_custom'] : self::get_icon_size_custom_bounds()['default'];
		if ( '' === $icon_preset || ! in_array( $icon_preset, self::icon_size_keys(), true ) ) {
			if ( isset( $design['icon_size'] ) ) {
				$icon_preset = 'custom';
				$icon_custom = (int) $design['icon_size'];
			} else {
				$icon_preset = 'medium';
			}
		}

		$icon_align = isset( $design['icon_align'] ) ? sanitize_key( (string) $design['icon_align'] ) : 'left';
		if ( ! in_array( $icon_align, array( 'left', 'center', 'right' ), true ) ) {
			$icon_align = 'left';
		}

		$container_border = self::is_container_border_enabled( $design['container_border'] ?? false );
		$container_shape  = isset( $design['container_shape'] ) ? sanitize_key( (string) $design['container_shape'] ) : 'medium';
		if ( ! in_array( $container_shape, self::shape_keys(), true ) || 'inherit' === $container_shape ) {
			$container_shape = 'medium';
		}
		$container_border_width = $container_border
			? max( 1, min( 8, (int) ( $design['container_border_width'] ?? 1 ) ) )
			: 0;

		return array(
			'--rf-container-border-width'  => $px( $container_border_width ),
			'--rf-container-border-color'  => $design['container_border_color'] ?? '#e2e8f0',
			'--rf-container-radius'        => $container_border ? self::shape_to_radius( $container_shape ) : '0',
			'--rf-container-pad'           => $container_border ? $px( $design['container_pad'] ?? 16 ) : '0',
			'--rf-chip-radius'        => self::shape_to_radius( $default_shape ),
			'--rf-chip-bg'            => $design['chip_bg'] ?? '#ffffff',
			'--rf-chip-border'        => $design['chip_border_color'] ?? '#e2e8f0',
			'--rf-chip-border-width'  => $px( $design['chip_border_width'] ?? 1 ),
			'--rf-chip-accent'        => $design['chip_accent_color'] ?? '#2563eb',
			'--rf-chip-title-color'   => $design['chip_title_color'] ?? '#0f172a',
			'--rf-chip-text-color'    => $design['chip_text_color'] ?? '#475569',
			'--rf-chip-pad'           => sprintf(
				'%1$s %2$s %3$s %4$s',
				$px( $design['chip_pad_top'] ?? 16 ),
				$px( $design['chip_pad_right'] ?? 18 ),
				$px( $design['chip_pad_bottom'] ?? 16 ),
				$px( $design['chip_pad_left'] ?? 18 )
			),
			'--rf-title-font'         => $title_font,
			'--rf-title-font-size'    => self::font_size_to_css( $design['title_font_size'] ?? 0 ),
			'--rf-title-weight'       => (string) max( 400, min( 900, (int) ( $design['title_font_weight'] ?? 700 ) ) ),
			'--rf-body-font'          => $body_font,
			'--rf-body-font-size'     => self::font_size_to_css( $design['body_font_size'] ?? 0 ),
			'--rf-grid-columns'       => (string) $columns,
			'--rf-grid-gap'           => $px( $design['grid_gap'] ?? 16 ),
			'--rf-icon-size'          => self::resolve_icon_size( $icon_preset, $icon_custom ),
			'--rf-icon-align'         => $icon_align,
		);
	}

	/**
	 * Whether the widget container border is enabled.
	 *
	 * @param mixed $value Raw checkbox value.
	 * @return bool
	 */
	public static function is_container_border_enabled( $value ) {
		if ( is_bool( $value ) ) {
			return $value;
		}
		if ( is_numeric( $value ) ) {
			return (int) $value !== 0;
		}
		if ( is_string( $value ) ) {
			$normalized = strtolower( trim( $value ) );
			if ( in_array( $normalized, array( '1', 'true', 'yes', 'on' ), true ) ) {
				return true;
			}
			if ( in_array( $normalized, array( '', '0', 'false', 'no', 'off' ), true ) ) {
				return false;
			}
			return false;
		}
		return ! empty( $value );
	}

	/**
	 * Inline style for a single chip (accent + optional shape override).
	 *
	 * @param array<string, mixed> $chip          Chip data.
	 * @param string               $default_shape Widget default shape.
	 * @return string
	 */
	public static function chip_inline_style( array $chip, $default_shape, array $icon_defaults = array() ) {
		$shape  = self::resolve_shape( $chip['chip_shape'] ?? 'inherit', $default_shape );
		$radius = self::shape_to_radius( $shape );
		$accent = isset( $chip['accent_color'] ) ? sanitize_hex_color( (string) $chip['accent_color'] ) : '';

		$default_icon_preset = isset( $icon_defaults['icon_size_preset'] ) ? $icon_defaults['icon_size_preset'] : 'medium';
		$default_icon_custom = isset( $icon_defaults['icon_size_custom'] ) ? (int) $icon_defaults['icon_size_custom'] : self::get_icon_size_custom_bounds()['default'];
		$icon_size           = self::resolve_chip_icon_size(
			$chip['icon_size'] ?? 'inherit',
			$default_icon_preset,
			$default_icon_custom,
			isset( $chip['icon_size_custom'] ) ? (int) $chip['icon_size_custom'] : 0
		);

		$parts = array(
			'--rf-chip-item-radius:' . $radius,
			'--rf-chip-item-icon-size:' . $icon_size,
		);
		if ( $accent ) {
			$parts[] = '--rf-chip-item-accent:' . $accent;
		}

		$icon_color = isset( $chip['icon_color'] ) ? sanitize_hex_color( (string) $chip['icon_color'] ) : '';
		if ( $icon_color ) {
			$parts[] = '--rf-chip-item-icon-color:' . $icon_color;
		}

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

	/**
	 * Normalize icon class strings from SiteOrigin icon picker, blocks, or legacy text fields.
	 *
	 * @param mixed $icon Raw icon value.
	 * @return string
	 */
	public static function normalize_icon_classes( $icon ) {
		if ( is_array( $icon ) ) {
			if ( isset( $icon['icon'] ) ) {
				$icon = $icon['icon'];
			} elseif ( isset( $icon['value'] ) ) {
				$icon = $icon['value'];
			} else {
				$icon = '';
			}
		}

		$icon = trim( sanitize_text_field( (string) $icon ) );
		if ( '' === $icon ) {
			return '';
		}

		$converted = self::convert_siteorigin_icon_token( $icon );
		if ( '' !== $converted ) {
			return $converted;
		}

		if ( preg_match( '/\bfa-solid\b|\bfa-regular\b|\bfa-brands\b|\bfa-light\b|\bfa-thin\b/i', $icon ) ) {
			return preg_replace( '/\s+/', ' ', $icon );
		}

		if ( preg_match( '/\bfa\s+fa-/i', $icon ) ) {
			return preg_replace( '/\bfa\s+fa-/i', 'fa-solid fa-', $icon );
		}

		if ( preg_match( '/^dashicons\s+dashicons-/', $icon ) ) {
			return $icon;
		}

		if ( preg_match( '/^dashicons-/', $icon ) ) {
			return 'dashicons ' . $icon;
		}

		if ( preg_match( '/^[a-z0-9-]+$/', $icon ) && ! preg_match( '/^fontawesome/i', $icon ) ) {
			return 'dashicons dashicons-' . ltrim( $icon, '-' );
		}

		return $icon;
	}

	/**
	 * Maps SiteOrigin Widgets Bundle icon picker tokens to Font Awesome 6 / Dashicons classes.
	 *
	 * @param string $icon Raw picker value.
	 * @return string Normalized classes, or empty when no SiteOrigin token matched.
	 */
	public static function convert_siteorigin_icon_token( $icon ) {
		$icon = trim( (string) $icon );
		if ( '' === $icon ) {
			return '';
		}

		if ( preg_match( '/^fontawesome-sow-(fas|far|fab|fal|fat)-(.+)$/i', $icon, $matches ) ) {
			return self::fontawesome_family_classes( strtolower( $matches[1] ), strtolower( $matches[2] ) );
		}

		if ( preg_match( '/^fontawesome\/(.+)$/i', $icon, $matches ) ) {
			$slug = strtolower( trim( $matches[1], '/' ) );
			if ( preg_match( '/^(fas|far|fab|fal|fat)-(.+)$/', $slug, $parts ) ) {
				return self::fontawesome_family_classes( $parts[1], $parts[2] );
			}
			return self::fontawesome_family_classes( 'fas', $slug );
		}

		if ( preg_match( '/^dashicons\/(.+)$/i', $icon, $matches ) ) {
			$slug = sanitize_key( $matches[1] );
			return '' !== $slug ? 'dashicons dashicons-' . $slug : '';
		}

		return '';
	}

	/**
	 * @param string $style fas|far|fab|fal|fat.
	 * @param string $slug  Icon slug without fa- prefix.
	 * @return string
	 */
	private static function fontawesome_family_classes( $style, $slug ) {
		$slug = sanitize_title( (string) $slug );
		if ( '' === $slug ) {
			return '';
		}

		$family = 'fa-solid';
		if ( 'far' === $style ) {
			$family = 'fa-regular';
		} elseif ( 'fab' === $style ) {
			$family = 'fa-brands';
		} elseif ( 'fal' === $style ) {
			$family = 'fa-light';
		} elseif ( 'fat' === $style ) {
			$family = 'fa-thin';
		}

		return $family . ' fa-' . $slug;
	}

	/**
	 * @param string $icon Normalized icon classes.
	 * @return bool
	 */
	private static function is_fontawesome_icon_class( $icon ) {
		return (bool) preg_match( '/\bfa-solid\b|\bfa-regular\b|\bfa-brands\b|\bfa-light\b|\bfa-thin\b/i', (string) $icon );
	}

	/**
	 * @param string $style Raw icon style.
	 * @return string glyph|number_badge
	 */
	public static function sanitize_icon_style( $style ) {
		$style = sanitize_key( (string) $style );
		return in_array( $style, array( 'glyph', 'number_badge' ), true ) ? $style : 'glyph';
	}

	/**
	 * Icon style options for SiteOrigin / blocks.
	 *
	 * @return array<string, string>
	 */
	public static function get_icon_style_options() {
		return array(
			'glyph'         => __( 'Icon glyph', 'rsm-content-widgets' ),
			'number_badge'  => __( 'Number in circle (Font Awesome)', 'rsm-content-widgets' ),
		);
	}

	/**
	 * Extract a Font Awesome glyph slug (e.g. 4) from picker class strings.
	 *
	 * @param string $icon Icon classes.
	 * @return string
	 */
	public static function extract_fa_glyph_slug( $icon ) {
		$icon = trim( (string) $icon );
		if ( '' === $icon ) {
			return '';
		}

		if ( preg_match( '/\bfa-([0-9]+)\b/i', $icon, $matches ) ) {
			return (string) $matches[1];
		}

		if ( preg_match( '/dashicons-([0-9]+)\b/', $icon, $matches ) ) {
			return (string) $matches[1];
		}

		if ( preg_match( '/^(\d)$/', $icon, $matches ) ) {
			return (string) $matches[1];
		}

		return '';
	}

	/**
	 * Resolve the Font Awesome glyph slug used inside a number badge.
	 *
	 * @param string $icon     Normalized icon classes.
	 * @param string $override Optional badge character from widget settings.
	 * @return string
	 */
	public static function resolve_badge_glyph( $icon, $override = '' ) {
		$glyph = sanitize_key( (string) $override );
		if ( '' !== $glyph ) {
			return $glyph;
		}

		$icon = trim( (string) $icon );
		if ( '' === $icon ) {
			return '';
		}

		$slug = self::extract_fa_glyph_slug( $icon );
		if ( '' !== $slug ) {
			return $slug;
		}

		if ( preg_match( '/\bfa-([a-z0-9-]+)\b/i', $icon, $matches ) ) {
			return sanitize_key( (string) $matches[1] );
		}

		return '';
	}

	/**
	 * Whether icon classes should render as a Font Awesome number badge stack.
	 *
	 * @param string $icon        Normalized icon classes.
	 * @param string $style       glyph|number_badge.
	 * @param string $badge_glyph Optional badge character override.
	 * @return bool
	 */
	public static function should_render_number_badge( $icon, $style, $badge_glyph = '' ) {
		$style = self::sanitize_icon_style( $style );
		if ( 'number_badge' === $style ) {
			return '' !== self::resolve_badge_glyph( $icon, $badge_glyph );
		}

		if ( 'glyph' !== $style ) {
			return false;
		}

		$icon = trim( (string) $icon );
		if ( '' === $icon ) {
			return false;
		}

		$glyph = self::extract_fa_glyph_slug( $icon );
		if ( '' === $glyph || ! preg_match( '/^[0-9]$/', $glyph ) ) {
			return false;
		}

		return (bool) preg_match( '/\bfa-[0-9]+\b|\bdashicons-[0-9]+\b/i', $icon );
	}

	/**
	 * Build front-end icon markup for feature chips and popouts.
	 *
	 * @param array<string, mixed> $args {
	 *     @type string $icon                     Normalized or raw icon classes.
	 *     @type string $icon_style               glyph|number_badge.
	 *     @type string $icon_color               Glyph color.
	 *     @type string $icon_badge_circle_color  Badge circle color.
	 *     @type string $icon_badge_text_color    Badge glyph color.
	 *     @type string $icon_badge_glyph         Optional badge character override (0-9, A, etc.).
	 *     @type string $accent_color             Fallback circle color.
	 *     @type string $wrapper_class            Optional class on glyph span.
	 * }
	 * @return string
	 */
	public static function render_icon_html( array $args ) {
		$icon          = self::normalize_icon_classes( $args['icon'] ?? '' );
		$style         = self::sanitize_icon_style( $args['icon_style'] ?? 'glyph' );
		$badge_glyph   = (string) ( $args['icon_badge_glyph'] ?? '' );
		$accent        = sanitize_hex_color( (string) ( $args['accent_color'] ?? '' ) ) ?: '';
		$icon_color    = sanitize_hex_color( (string) ( $args['icon_color'] ?? '' ) ) ?: '';
		$circle        = sanitize_hex_color( (string) ( $args['icon_badge_circle_color'] ?? '' ) ) ?: $accent;
		$text          = sanitize_hex_color( (string) ( $args['icon_badge_text_color'] ?? '' ) ) ?: '#ffffff';
		$wrap_class    = sanitize_html_class( (string) ( $args['wrapper_class'] ?? 'rsmspark-features__chip-media__class' ) );

		if ( self::should_render_number_badge( $icon, $style, $badge_glyph ) ) {
			$glyph = self::resolve_badge_glyph( $icon, $badge_glyph );
			if ( '' !== $glyph ) {
				$glyph_class = 'fa-' . ltrim( $glyph, '-' );

				return sprintf(
					'<span class="fa-stack fa-2x rsmspark-features__icon-stack" aria-hidden="true"><i class="fa-solid fa-circle fa-stack-2x" style="color:%1$s;"></i><i class="fa-solid %2$s fa-stack-1x" style="color:%3$s;"></i></span>',
					esc_attr( $circle ?: '#ff6b35' ),
					esc_attr( $glyph_class ),
					esc_attr( $text )
				);
			}
		}

		if ( '' === $icon ) {
			return '';
		}

		$style_attr = $icon_color ? ' style="color:' . esc_attr( $icon_color ) . ';"' : '';

		if ( self::is_fontawesome_icon_class( $icon ) ) {
			return sprintf(
				'<span class="%1$s rsmspark-features__icon-fa-wrap" aria-hidden="true"><i class="%2$s"%3$s></i></span>',
				esc_attr( $wrap_class ),
				esc_attr( $icon ),
				$style_attr
			);
		}

		return sprintf(
			'<span class="%1$s %2$s"%3$s aria-hidden="true"></span>',
			esc_attr( $wrap_class ),
			esc_attr( $icon ),
			$style_attr
		);
	}

	/**
	 * @param array<string, mixed> $feature Chip or merged popout row.
	 * @param string               $context chip|popout.
	 * @return array<string, mixed>
	 */
	public static function icon_render_args_from_feature( array $feature, $context = 'chip' ) {
		if ( 'popout' === $context ) {
			return array(
				'icon'                    => (string) ( $feature['popout_icon'] ?? '' ),
				'icon_style'              => $feature['popout_icon_style'] ?? 'glyph',
				'icon_color'              => $feature['popout_icon_color'] ?? '',
				'icon_badge_circle_color' => $feature['popout_icon_badge_circle_color'] ?? '',
			'icon_badge_text_color'   => $feature['popout_icon_badge_text_color'] ?? '',
				'icon_badge_glyph'        => $feature['popout_icon_badge_glyph'] ?? '',
				'accent_color'            => $feature['accent_color'] ?? '',
				'wrapper_class'           => 'rsmspark-features__popout-media__class',
			);
		}

		return array(
			'icon'                    => (string) ( $feature['title_icon'] ?? '' ),
			'icon_style'              => $feature['icon_style'] ?? 'glyph',
			'icon_color'              => $feature['icon_color'] ?? '',
			'icon_badge_circle_color' => $feature['icon_badge_circle_color'] ?? '',
			'icon_badge_text_color'   => $feature['icon_badge_text_color'] ?? '',
			'icon_badge_glyph'        => $feature['icon_badge_glyph'] ?? $feature['popout_icon_badge_glyph'] ?? '',
			'accent_color'            => $feature['accent_color'] ?? '',
			'wrapper_class'           => 'rsmspark-features__chip-media__class',
		);
	}
}
