<?php
/**
 * Review Shortcode
 *
 * @package Ugcify\Review
 */

namespace Ugcify\Review;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class ReviewShortcode
 */
class ReviewShortcode {
	/**
	 * Allowed social review types.
	 *
	 * @var array<int,string>
	 */
	private const SOCIAL_TYPES = array( 'instagram', 'youtube', 'tiktok' );

	/**
	 * Non-social review types.
	 *
	 * @var array<int,string>
	 */
	private const MANUAL_TYPES = array( 'text', 'image', 'video', 'mixed' );

	/**
	 * Form renderer instance
	 *
	 * @var \Ugcify\Form\FormRenderer
	 */
	private $form_renderer;

	/**
	 * Constructor
	 *
	 * @param \Ugcify\Form\FormRenderer $form_renderer Form renderer instance.
	 */
	public function __construct( $form_renderer ) {
		$this->form_renderer = $form_renderer;
		$this->register_shortcodes();
	}

	/**
	 * Register shortcodes
	 *
	 * @return void
	 */
	private function register_shortcodes() {
		add_shortcode( 'ugcify_reviews', array( $this, 'render_reviews_shortcode' ) );
	}

	/**
	 * Render reviews shortcode
	 *
	 * [ugcify_reviews product_id="123" tags="summer,featured" categories="shoes,sale"]
	 *
	 * @param array $atts Shortcode attributes.
	 * @return string
	 */
	public function render_reviews_shortcode( $atts ) {
		// Signal that UGC assets are needed on this page.
		do_action( 'ugcify_assets_needed' );

		$raw_atts = is_array( $atts ) ? $atts : array();

		$atts = shortcode_atts(
			array(
				'product_id'  => 0,
				'tags'        => '',
				'categories'  => '',
				'instance_id' => '',
				'source'      => 'all',
				'platforms'   => '',
			),
			$raw_atts,
			'ugcify_reviews'
		);

		$product_id = intval( $atts['product_id'] );
		$shortcode_tags = $this->normalize_slug_csv( $atts['tags'] );
		$shortcode_categories = $this->normalize_slug_csv( $atts['categories'] );
		$shortcode_type_filter = $this->resolve_shortcode_type_filter( $raw_atts );
		
		// If product_id is not provided, try to get it from global product if on a single product page.
		if ( 0 === $product_id ) {
			global $product;
			if ( $product instanceof \WC_Product ) {
				$product_id = $product->get_id();
			}
		}

		$instance_id = sanitize_html_class( (string) $atts['instance_id'] );

		// Get template customization settings + per-shortcode overrides.
		$template_settings = $this->get_template_customization_settings();
		$template_settings = array_merge(
			$template_settings,
			$this->extract_template_settings_from_atts( $raw_atts )
		);
		$template_settings = $this->normalize_shortcode_template_selection( $template_settings );


		$template = UGCIFY_PATH . 'templates/reviews-listing.php';

		if ( ! file_exists( $template ) ) {
			return '';
		}

		// Set variables for the template.
		$form_renderer = $this->form_renderer;

		ob_start();
		
		// Enqueue dynamic CSS via WordPress inline style API.
		$dynamic_css = self::generate_template_css( $template_settings, $instance_id );
		if ( $dynamic_css ) {
			$style_handle = '' !== $instance_id
				? 'ugcify-template-custom-styles-' . sanitize_html_class( $instance_id )
				: 'ugcify-template-custom-styles';
			wp_register_style( $style_handle, false ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters
			wp_enqueue_style( $style_handle );
			wp_add_inline_style( $style_handle, $dynamic_css );
			printf(
				'<style id="%1$s-inline-css">%2$s</style>',
				esc_attr( $style_handle ),
				wp_strip_all_tags( $dynamic_css ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			);
		}
		
		// Extract variables to be available in the template.
		include $template;
		return ob_get_clean();
	}

	/**
	 * Resolve effective type filter from shortcode attributes.
	 *
	 * @param array $atts Shortcode attributes.
	 * @return string CSV of review types or empty string for no type filter.
	 */
	private function resolve_shortcode_type_filter( $atts ) {
		$source_raw = $this->get_shortcode_att_value( $atts, 'source' );
		$source     = sanitize_key( is_string( $source_raw ) ? $source_raw : 'all' );
		if ( ! in_array( $source, array( 'all', 'social', 'manual' ), true ) ) {
			$source = 'all';
		}

		$platforms_raw = $this->get_shortcode_att_value( $atts, 'platforms' );
		$platforms     = $this->normalize_allowed_platform_csv( $platforms_raw );

		if ( 'manual' === $source ) {
			return implode( ',', self::MANUAL_TYPES );
		}

		if ( ! empty( $platforms ) ) {
			return implode( ',', $platforms );
		}

		if ( 'social' === $source ) {
			return implode( ',', self::SOCIAL_TYPES );
		}

		return '';
	}

	/**
	 * Normalize a comma-separated platform list to allowed social platforms.
	 *
	 * @param mixed $raw Raw shortcode value.
	 * @return array<int,string>
	 */
	private function normalize_allowed_platform_csv( $raw ) {
		$values = $this->normalize_slug_csv( $raw );
		if ( empty( $values ) ) {
			return array();
		}

		$values = array_values(
			array_filter(
				$values,
				function ( $value ) {
					return in_array( $value, self::SOCIAL_TYPES, true );
				}
			)
		);

		return array_values( array_unique( $values ) );
	}

	/**
	 * Normalize a comma-separated slug list.
	 *
	 * @param mixed $raw Raw shortcode value.
	 * @return array
	 */
	private function normalize_slug_csv( $raw ) {
		if ( ! is_string( $raw ) || '' === trim( $raw ) ) {
			return array();
		}

		$parts = array_map( 'trim', explode( ',', $raw ) );
		$parts = array_map( 'sanitize_title', $parts );
		$parts = array_values(
			array_unique(
				array_filter( $parts )
			)
		);

		return $parts;
	}

	/**
	 * Get template customization settings
	 *
	 * @return array
	 */
	public static function get_default_template_customization_settings() {
		return array(
			// General.
			'enableWidget'            => false,
			'widgetType'              => 'grid',
			'mediaMosaicStyle'        => 'grid',

			// Header Area.
			'showHeaderTitle'         => true,
			'title'                   => 'Customers are saying',
			'titleFontWeight'         => '600',
			'titleFontSize'           => 'medium',
			'emptyReviewText'         => 'No customer review yet.',
			'showMediaCarousel'       => true,
			'mediaCarouselItemsToShow' => '10',

			// Colors and Styles.
			'primaryColor'            => '#201CFE',
			'titleColor'              => '#000000',
			'bodyFontColor'           => '#444444',
			'cardBgColor'             => '#FFFFFF',
			'ratingColor'             => '#FFA800',
			'buttonTextColor'         => '#FFFFFF',
			'filterFillColor'         => '#FFFFFF',
			'filterStrokeColor'       => '#201CFE',
			'filterIconColor'         => '#292D32',
			'readMoreColor'           => '#000000',
			'loadMoreTextColor'       => '#201CFE',
			'loadMoreBgColor'         => '#FFFFFF',
			'loadMoreBorderColor'     => '#201CFE',

			// Review Section.
			'reviewerNameFormat'      => 'full',
			'reviewerNameFontSize'    => 'medium',
			'showVerifiedCheck'       => true,
			'showInfluencerBadge'     => true,
			'showCustomerAvatar'      => true,
			'reviewDateFormat'        => 'd M, Y',
			'starRatingSize'          => 'medium',
			'reviewTextSize'          => 'medium',
			'reviewTextLength'        => 'large',
			'showReviews'             => true,
			'reviewCardBorderRadius'  => '10',
			'testimonialQuoteSize'    => 'medium',
			'testimonialQuoteColor'   => '#322EFE',
			'testimonialQuoteOpacity' => '1',
			'reviewColumn'            => '3',
			'reviewsPerPage'          => '3',
			'showLoadMoreButton'      => true,
			'loadMoreTextSize'        => 'medium',
			'loadMoreBorderRadius'    => '10',
			'paginationType'          => 'page_number',

			// Advanced.
			'customCSS'               => '',
			'shortcode'               => '[ugcify_reviews product_id="123"]',
			// Template
			'blockTemplate'           => 'minimal_gallery',
			// Layout & Grid
			'gridStyle'               => 'masonry',
			'columnsDesktop'          => '4',
			'gapSpacing'              => '16',
			// Media Behavior
			'lazyLoadImages'          => true,
			'autoplayVideo'           => false,
			'enableLightbox'          => true,
			// Shoppable Tags
			'tagStyle'                => 'pulsing_dot',
			'directAddToCart'         => true,
			// Interactions
			'enableHelpfulVotes'      => false,
			'enableUpvotes'           => false,
			'socialShareButtons'      => false,
			'customersAlsoBought'     => false,
		);
	}

	/**
	 * Get template customization settings
	 *
	 * @return array
	 */
	private function get_template_customization_settings() {
		$defaults = self::get_default_template_customization_settings();

		$gallery_settings = get_option( 'ugcify_review_template_settings', array() );
		$active_id        = isset( $gallery_settings['activeTemplateId'] ) ? sanitize_key( (string) $gallery_settings['activeTemplateId'] ) : '';
		$saved            = get_option( 'ugcify_template_customization', array() );
		if ( is_array( $saved ) ) {
			unset( $saved['tagStyle'] );
		}

		if ( is_array( $saved ) && ! empty( $saved ) ) {
			$settings = array_merge( $defaults, $saved );
		} else {
			$settings = $defaults;
		}

		if ( 'card_carousel' === $active_id ) {
			$card_carousel_saved = get_option( 'ugcify_template_customization_card_carousel', array() );
			if ( is_array( $card_carousel_saved ) ) {
				unset( $card_carousel_saved['tagStyle'] );
			}
			$settings = array_merge(
				$settings,
				is_array( $card_carousel_saved ) ? $card_carousel_saved : array()
			);
		}

		if ( 'testimonial_carousel' === $active_id ) {
			$testimonial_saved = get_option( 'ugcify_template_customization_testimonial_carousel', array() );
			if ( is_array( $testimonial_saved ) ) {
				unset( $testimonial_saved['tagStyle'] );
			}
			$settings = array_merge(
				$settings,
				array(
					'reviewColumn'   => '2',
					'reviewsPerPage' => '2',
				),
				is_array( $testimonial_saved ) ? $testimonial_saved : array()
			);
		}

		if ( 'media_mosaic' === $active_id ) {
			$media_mosaic_saved = get_option( 'ugcify_template_customization_media_mosaic', array() );
			if ( is_array( $media_mosaic_saved ) ) {
				unset( $media_mosaic_saved['tagStyle'] );
			}
			$settings = array_merge(
				$settings,
				is_array( $media_mosaic_saved ) ? $media_mosaic_saved : array()
			);
		}

		// Gallery template selection overrides widgetType.
		// Maps activeTemplateId → widgetType so enabling a gallery template
		// immediately takes effect on the frontend without a separate setting.
		$gallery_template_map = array(
			'reels'                  => 'reels',
			'gallery'                => 'gallery',
			'card_carousel'          => 'card_carousel',
			'media_mosaic'           => 'media_mosaic',
			'testimonial_carousel'   => 'testimonial_carousel',
		);

		if ( '' !== $active_id && isset( $gallery_template_map[ $active_id ] ) ) {
			$settings['widgetType'] = $gallery_template_map[ $active_id ];
		} elseif ( in_array( $settings['widgetType'], array( 'gallery', 'reels', 'media_mosaic', 'testimonial_carousel' ), true ) ) {
			$settings['widgetType'] = 'masonry' === ( $settings['gridStyle'] ?? '' ) ? 'masonry' : 'grid';
		}

		// When reels is active, merge reels-specific settings so the template can
		// use them directly from $template_settings.
		if ( 'reels' === $settings['widgetType'] ) {
			$settings = array_merge( $settings, $this->get_reels_settings() );
		}

		return $settings;
	}

	/**
	 * Get merged reels customization settings.
	 *
	 * @return array
	 */
	private function get_reels_settings() {
		$defaults = array(
			'showStories'        => true,
			'storiesTitle'       => 'Customer Stories',
			'storiesMaxCount'    => 8,
			'showAddYours'       => true,
			'showCommunity'      => true,
			'communityTitle'     => 'Community Reviews',
			'communitySubtitle'  => 'See what our customers are saying',
			'showShareBtn'       => true,
			'enableHelpfulVotes' => false,
			'reviewerNameFormat' => 'first_last_initial',
			'reviewDateFormat'   => 'd M, Y',
			'showVerifiedCheck'  => true,
			'reviewsPerPage'     => '12',
			'primaryColor'       => '#201CFE',
			'ratingColor'        => '#FFA800',
			'customCSS'          => '',
			// Media Behavior.
			'lazyLoadImages'     => true,
			'autoplayVideo'      => false,
			'enableLightbox'     => true,
			// Shoppable Tags.
			'tagStyle'           => 'pulsing_dot',
			'directAddToCart'    => true,
			// Interactions.
			'enableUpvotes'      => false,
			'socialShareButtons' => false,
			'customersAlsoBought' => false,
		);

		$saved = get_option( 'ugcify_reels_settings', array() );
		if ( is_array( $saved ) && ! empty( $saved ) ) {
			return array_merge( $defaults, $saved );
		}

		return $defaults;
	}

	/**
	 * Normalize explicit template selections passed by builders.
	 *
	 * @param array $settings Template settings.
	 * @return array
	 */
	private function normalize_shortcode_template_selection( $settings ) {
		if ( ! is_array( $settings ) ) {
			return array();
		}

		$block_template = ! empty( $settings['blockTemplate'] ) ? sanitize_key( (string) $settings['blockTemplate'] ) : '';

		if ( 'carousel' === $block_template ) {
			$settings['widgetType'] = 'gallery';
			unset( $settings['mediaMosaicStyle'] );
			return $settings;
		}

		if ( in_array( $block_template, array( 'card_carousel', 'media_mosaic', 'testimonial_carousel' ), true ) ) {
			$settings['widgetType'] = $block_template;
			return $settings;
		}

		if ( 'reels' === $block_template ) {
			$settings['widgetType'] = 'reels';
			return $settings;
		}

		if ( 'minimal_gallery' === $block_template ) {
			$widget_type = ! empty( $settings['widgetType'] ) ? sanitize_key( (string) $settings['widgetType'] ) : 'grid';
			$settings['widgetType'] = in_array( $widget_type, array( 'grid', 'masonry', 'slider' ), true ) ? $widget_type : 'grid';
		}

		return $settings;
	}

	/**
	 * Generate dynamic CSS for template customization.
	 *
	 * Returns only the CSS rules string (no wrapping <style> tag).
	 * Callers are responsible for enqueueing via wp_add_inline_style().
	 *
	 * @param array  $settings    Template customization settings.
	 * @param string $instance_id Optional instance ID for scoped output.
	 * @return string CSS rules string (empty if no rules apply).
	 */
	public static function generate_template_css( $settings, $instance_id = '' ) {
		$instance_id = sanitize_html_class( (string) $instance_id );
		$scope       = '.ugcify-reviews-section';
		if ( '' !== $instance_id ) {
			$scope = '.ugcify-reviews-section[data-ugcify-instance="' . esc_attr( $instance_id ) . '"]';
		}

		$css = '';
		$widget_type_for_css = ! empty( $settings['widgetType'] ) ? sanitize_key( (string) $settings['widgetType'] ) : 'grid';
		$css_variables = array(
			'primaryColor'        => '--ugcify-primary-color',
			'titleColor'          => '--ugcify-title-color',
			'bodyFontColor'       => '--ugcify-body-color',
			'cardBgColor'         => '--ugcify-card-background',
			'buttonTextColor'     => '--ugcify-button-text-color',
			'filterFillColor'     => '--ugcify-filter-background',
			'filterStrokeColor'   => '--ugcify-filter-border-color',
			'filterIconColor'     => '--ugcify-filter-icon-color',
			'readMoreColor'       => '--ugcify-read-more-color',
			'loadMoreTextColor'   => '--ugcify-load-more-text-color',
			'loadMoreBgColor'     => '--ugcify-load-more-background',
			'loadMoreBorderColor' => '--ugcify-load-more-border-color',
		);
		$variable_declarations = '';
		foreach ( $css_variables as $setting_key => $variable_name ) {
			if ( ! empty( $settings[ $setting_key ] ) ) {
				$variable_declarations .= $variable_name . ':' . esc_attr( $settings[ $setting_key ] ) . ';';
			}
		}
		if ( '' !== $variable_declarations ) {
			$css .= $scope . '{' . $variable_declarations . '}';
		}

		// Font sizes mapping for titles.
		$title_font_sizes = array(
			'small'  => '20px',
			'medium' => '24px',
			'large'  => '28px',
		);

		// Font sizes mapping for text content.
		$text_font_sizes = array(
			'small'  => '14px',
			'medium' => '16px',
			'large'  => '18px',
		);

		// Title styles (includes title, stats label, author name).
		if ( ! empty( $settings['titleColor'] ) ) {
			$css .= $scope . ' .ugcify-main-title { color: ' . esc_attr( $settings['titleColor'] ) . ' !important; }';
			$css .= $scope . ' .ugcify-stats-label, ' . $scope . ' .ugcify-review-card .ugcify-author-name { color: ' . esc_attr( $settings['titleColor'] ) . ' !important; }';
		}
		if ( ! empty( $settings['titleFontWeight'] ) ) {
			$css .= $scope . ' .ugcify-main-title { font-weight: ' . esc_attr( $settings['titleFontWeight'] ) . ' !important; }';
		}
		if ( ! empty( $settings['titleFontSize'] ) && isset( $title_font_sizes[ $settings['titleFontSize'] ] ) ) {
			$css .= $scope . ' .ugcify-main-title { font-size: ' . esc_attr( $title_font_sizes[ $settings['titleFontSize'] ] ) . ' !important; }';
		}

		// Primary color (buttons, links, pagination).
		if ( ! empty( $settings['primaryColor'] ) ) {
			$css .= $scope . ' .ugcify-write-review-btn { background-color: ' . esc_attr( $settings['primaryColor'] ) . ' !important; border-color: ' . esc_attr( $settings['primaryColor'] ) . ' !important; }';
			$css .= $scope . ' .ugcify-filter-btn { border-color: ' . esc_attr( $settings['filterStrokeColor'] ) . ' !important; }';
			$css .= $scope . ' .ugcify-pagination .page-numbers:not(.disabled):hover, ' . $scope . ' .ugcify-pagination .page-numbers.current { border-color: ' . esc_attr( $settings['primaryColor'] ) . ' !important; color: ' . esc_attr( $settings['primaryColor'] ) . ' !important; }';
		}

		// Button text color.
		if ( ! empty( $settings['buttonTextColor'] ) ) {
			$css .= $scope . ' .ugcify-write-review-btn { color: ' . esc_attr( $settings['buttonTextColor'] ) . ' !important; }';
		}

		// Body font color (includes text, date, counts, pagination info).
		if ( ! empty( $settings['bodyFontColor'] ) ) {
			$css .= $scope . ' .ugcify-review-card .ugcify-card-text, ' . $scope . ' .ugcify-review-card .ugcify-card-date { color: ' . esc_attr( $settings['bodyFontColor'] ) . ' !important; }';
			$css .= $scope . ' .ugcify-total-count, ' . $scope . ' .ugcify-pagination-info, ' . $scope . ' .ugcify-pagination .page-status { color: ' . esc_attr( $settings['bodyFontColor'] ) . ' !important; }';
		}

		// Card background color.
		if ( 'gallery' !== $widget_type_for_css && ! empty( $settings['cardBgColor'] ) ) {
			$css .= $scope . ' .ugcify-review-card { background-color: ' . esc_attr( $settings['cardBgColor'] ) . ' !important; }';
		}

		// Rating color (stars).
		if ( ! empty( $settings['ratingColor'] ) ) {
			$rating_color = esc_attr( $settings['ratingColor'] );
			$css         .= $scope . ' .ugcify-review-card .ugcify-card-rating .ugcify-star-display.full, ' . $scope . ' .ugcify-stars-summary .ugcify-star-display.full, ' . $scope . ' .ugcify-stars-display .ugcify-star-display.full { background-color: ' . $rating_color . ' !important; }';
			$css         .= $scope . ' .ugcify-review-card .ugcify-card-rating .ugcify-star-display.half, ' . $scope . ' .ugcify-stars-summary .ugcify-star-display.half, ' . $scope . ' .ugcify-stars-display .ugcify-star-display.half { background: linear-gradient(90deg, ' . $rating_color . ' 50%, #e0e0e0 50%) !important; }';
		}

		// Read more color.
		if ( ! empty( $settings['readMoreColor'] ) ) {
			$css .= $scope . ' .ugcify-review-card .ugcify-read-more { color: ' . esc_attr( $settings['readMoreColor'] ) . ' !important; }';
		}

		// Filter colors.
		if ( ! empty( $settings['filterFillColor'] ) ) {
			$css .= $scope . ' .ugcify-filter-btn { background-color: ' . esc_attr( $settings['filterFillColor'] ) . ' !important; }';
		}
		if ( ! empty( $settings['filterIconColor'] ) ) {
			$css .= $scope . ' .ugcify-filter-btn svg path { stroke: ' . esc_attr( $settings['filterIconColor'] ) . ' !important; }';
		}

		// Review card border radius.
		if ( 'gallery' !== $widget_type_for_css && ! empty( $settings['reviewCardBorderRadius'] ) ) {
			$css .= $scope . ' .ugcify-review-card { border-radius: ' . esc_attr( $settings['reviewCardBorderRadius'] ) . 'px !important; }';
		}

		// Grid/Masonry columns and layout.
		if ( 'gallery' !== $widget_type_for_css && ! empty( $settings['reviewColumn'] ) ) {
			$columns        = max( 1, min( 6, intval( $settings['reviewColumn'] ) ) );
			$tablet_columns = min( 2, $columns );
			$widget_type = $widget_type_for_css;
			
			if ( 'masonry' === $widget_type ) {
				$css .= $scope . ' .ugcify-reviews-grid.ugcify-layout-masonry { column-count: ' . esc_attr( $columns ) . ' !important; display: block !important; }';
				$css .= '@media (max-width: 768px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-masonry { column-count: ' . esc_attr( $tablet_columns ) . ' !important; }}';
				$css .= '@media (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-masonry { column-count: 1 !important; }}';
				$css .= '@container (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-masonry { column-count: 1 !important; }}';
				$css .= $scope . ' .ugcify-reviews-grid.ugcify-layout-masonry .ugcify-review-card { break-inside: avoid !important; margin-bottom: 30px !important; display: inline-block !important; width: 100% !important; }';
			} elseif ( 'list' === $widget_type ) {
				$css .= $scope . ' .ugcify-reviews-grid.ugcify-layout-list { display: flex !important; flex-direction: column !important; gap: 20px !important; }';
				$css .= $scope . ' .ugcify-reviews-grid.ugcify-layout-list .ugcify-review-card { width: 100% !important; }';
			} elseif ( 'card_carousel' === $widget_type ) {
				$css .= $scope . ' .ugcify-reviews-grid.ugcify-layout-card-carousel { grid-template-columns: repeat(' . esc_attr( $columns ) . ', minmax(0, 1fr)) !important; }';
				$css .= '@media (max-width: 1024px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-card-carousel { grid-template-columns: repeat(' . esc_attr( $tablet_columns ) . ', minmax(0, 1fr)) !important; }}';
				$css .= '@media (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-card-carousel { grid-template-columns: 1fr !important; }}';
				$css .= '@container (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-card-carousel { grid-template-columns: 1fr !important; }}';
				$css .= $scope . ' .ugcify-review-card.ugcify-card-carousel-card .ugcify-card-media { aspect-ratio: 1 / 1 !important; height: auto !important; }';
				$css .= $scope . ' .ugcify-review-card.ugcify-card-carousel-card .ugcify-card-content, ' . $scope . ' .ugcify-review-card.ugcify-card-carousel-card .ugcify-card-text, ' . $scope . ' .ugcify-review-card.ugcify-card-carousel-card .ugcify-card-date { text-align: center !important; }';
				$css .= $scope . ' .ugcify-review-card.ugcify-card-carousel-card .ugcify-card-rating { justify-content: center !important; margin: 20px 0 14px !important; }';
				$css .= $scope . ' .ugcify-review-card.ugcify-card-carousel-card .ugcify-card-author { justify-content: center !important; }';
			} elseif ( 'testimonial_carousel' === $widget_type ) {
				$css .= $scope . ' .ugcify-reviews-grid.ugcify-layout-testimonial-carousel { grid-template-columns: repeat(' . esc_attr( $columns ) . ', minmax(0, 1fr)) !important; }';
				$css .= '@media (max-width: 1024px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-testimonial-carousel { grid-template-columns: repeat(' . esc_attr( $tablet_columns ) . ', minmax(0, 1fr)) !important; }}';
				$css .= '@media (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-testimonial-carousel { grid-template-columns: 1fr !important; }}';
				$css .= '@container (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-testimonial-carousel { grid-template-columns: 1fr !important; }}';
				$css .= $scope . ' .ugcify-review-card.ugcify-testimonial-carousel-card { background-color: ' . esc_attr( $settings['cardBgColor'] ?? '#FFFFFF' ) . ' !important; border-radius: ' . esc_attr( isset( $settings['reviewCardBorderRadius'] ) ? intval( $settings['reviewCardBorderRadius'] ) : 10 ) . 'px !important; }';
				$css .= $scope . ' .ugcify-review-card.ugcify-testimonial-carousel-card, ' . $scope . ' .ugcify-review-card.ugcify-testimonial-carousel-card .ugcify-card-text, ' . $scope . ' .ugcify-review-card.ugcify-testimonial-carousel-card .ugcify-card-date { text-align: center !important; }';
				$css .= $scope . ' .ugcify-review-card.ugcify-testimonial-carousel-card .ugcify-card-rating, ' . $scope . ' .ugcify-review-card.ugcify-testimonial-carousel-card .ugcify-card-author { justify-content: center !important; }';
				if ( ! empty( $settings['primaryColor'] ) ) {
					$css .= $scope . ' .ugcify-testimonial-nav { color: ' . esc_attr( $settings['primaryColor'] ) . ' !important; }';
				}
			} elseif ( 'media_mosaic' === $widget_type ) {
				$media_mosaic_style = isset( $settings['mediaMosaicStyle'] ) ? sanitize_key( (string) $settings['mediaMosaicStyle'] ) : 'grid';
				if ( 'masonry' === $media_mosaic_style ) {
					$css .= $scope . ' .ugcify-reviews-grid.ugcify-layout-media-mosaic { display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr)) !important; gap: 16px !important; grid-auto-flow: row !important; grid-auto-rows: 8px !important; align-items: start !important; }';
					$css .= $scope . ' .ugcify-review-card.ugcify-media-mosaic-card { grid-row-end: span var(--ugcify-mosaic-row-span, 32) !important; }';
					$css .= '@media (max-width: 1024px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-media-mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }}';
					$css .= '@media (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-media-mosaic { grid-template-columns: 1fr !important; }}';
					$css .= '@container (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-media-mosaic { grid-template-columns: 1fr !important; }}';
				} else {
					$css .= $scope . ' .ugcify-reviews-grid.ugcify-layout-media-mosaic { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; gap: 16px !important; }';
					$css .= '@media (max-width: 1024px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-media-mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }}';
					$css .= '@media (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-media-mosaic { grid-template-columns: 1fr !important; }}';
					$css .= '@container (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid.ugcify-layout-media-mosaic { grid-template-columns: 1fr !important; }}';
				}
				$css .= $scope . ' .ugcify-review-card.ugcify-media-mosaic-card { background-color: ' . esc_attr( $settings['cardBgColor'] ?? '#FFFFFF' ) . ' !important; border-radius: ' . esc_attr( isset( $settings['reviewCardBorderRadius'] ) ? intval( $settings['reviewCardBorderRadius'] ) : 10 ) . 'px !important; }';
				$css .= $scope . ' .ugcify-review-card.ugcify-media-mosaic-card .ugcify-card-author, ' . $scope . ' .ugcify-review-card.ugcify-media-mosaic-card .ugcify-card-author-main { align-items: flex-start !important; }';
				$css .= $scope . ' .ugcify-review-card.ugcify-media-mosaic-card .ugcify-card-date, ' . $scope . ' .ugcify-review-card.ugcify-media-mosaic-card .ugcify-card-text { text-align: left !important; }';
				$css .= $scope . ' .ugcify-review-card.ugcify-media-mosaic-card .ugcify-card-rating { justify-content: flex-start !important; margin-top: 6px !important; }';
			} else {
				$css .= $scope . ' .ugcify-reviews-grid { grid-template-columns: repeat(' . esc_attr( $columns ) . ', 1fr) !important; }';
				$css .= '@media (max-width: 768px) {' . $scope . ' .ugcify-reviews-grid { grid-template-columns: repeat(' . esc_attr( $tablet_columns ) . ', 1fr) !important; }}';
				$css .= '@media (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid { grid-template-columns: 1fr !important; }}';
				$css .= '@container (max-width: 640px) {' . $scope . ' .ugcify-reviews-grid { grid-template-columns: 1fr !important; }}';
			}
		}

		// Reviewer name font size.
		if ( ! empty( $settings['reviewerNameFontSize'] ) && isset( $text_font_sizes[ $settings['reviewerNameFontSize'] ] ) ) {
			$css .= $scope . ' .ugcify-review-card .ugcify-author-name { font-size: ' . esc_attr( $text_font_sizes[ $settings['reviewerNameFontSize'] ] ) . ' !important; }';
		}

		// Review text size.
		if ( ! empty( $settings['reviewTextSize'] ) && isset( $text_font_sizes[ $settings['reviewTextSize'] ] ) ) {
			$css .= $scope . ' .ugcify-review-card .ugcify-card-text { font-size: ' . esc_attr( $text_font_sizes[ $settings['reviewTextSize'] ] ) . ' !important; }';
		}

		// Star rating size (applies to all star displays).
		if ( ! empty( $settings['starRatingSize'] ) ) {
			$star_sizes = array(
				'small'  => '14px',
				'medium' => '16px',
				'large'  => '18px',
			);
			if ( isset( $star_sizes[ $settings['starRatingSize'] ] ) ) {
				$star_size = esc_attr( $star_sizes[ $settings['starRatingSize'] ] );
				$css      .= $scope . ' .ugcify-review-card .ugcify-card-rating .ugcify-star-display, ' . $scope . ' .ugcify-stars-summary .ugcify-star-display, ' . $scope . ' .ugcify-stars-display .ugcify-star-display { width: ' . $star_size . ' !important; height: ' . $star_size . ' !important; }';
			}
		}

		// Custom CSS.
		if ( ! empty( $settings['customCSS'] ) ) {
			$css .= self::scope_custom_css( (string) $settings['customCSS'], $scope );
		}

		// Gap spacing between cards.
		if ( isset( $settings['gapSpacing'] ) && '' !== $settings['gapSpacing'] ) {
			$gap = intval( $settings['gapSpacing'] );
			if ( $gap >= 0 ) {
				$css .= $scope . ' .ugcify-reviews-grid { gap: ' . $gap . 'px !important; column-gap: ' . $gap . 'px !important; }' ;
			}
		}

		return $css;
	}

	/**
	 * Extract template settings overrides from shortcode atts.
	 *
	 * @param array $atts Shortcode attributes.
	 * @return array
	 */
	private function extract_template_settings_from_atts( $atts ) {
		$defaults  = self::get_default_template_customization_settings();
		$overrides = array();

		$boolean_keys = array(
			'showHeaderTitle',
			'showCustomerAvatar',
			'showVerifiedCheck',
			'showInfluencerBadge',
			'showReviews',
			'showLoadMoreButton',
			'showMediaCarousel',
			'lazyLoadImages',
			'autoplayVideo',
			'enableLightbox',
			'directAddToCart',
			'enableUpvotes',
			'socialShareButtons',
			'customersAlsoBought',
		);

		foreach ( $defaults as $key => $default_value ) {
			$att_value = $this->get_shortcode_att_value( $atts, $key );
			if ( null === $att_value ) {
				continue;
			}

			if ( in_array( $key, $boolean_keys, true ) ) {
				$overrides[ $key ] = filter_var( $att_value, FILTER_VALIDATE_BOOLEAN );
				continue;
			}

			if ( 'customCSS' === $key ) {
				$overrides[ $key ] = sanitize_textarea_field( (string) $att_value );
				continue;
			}

			if ( is_numeric( $default_value ) ) {
				$overrides[ $key ] = (string) absint( $att_value );
				continue;
			}

			$overrides[ $key ] = sanitize_text_field( (string) $att_value );
		}

		return $overrides;
	}

	/**
	 * Resolve a shortcode attribute value by canonical setting key.
	 *
	 * WordPress lowercases shortcode attribute names, so block-provided camelCase
	 * keys (for example, `widgetType`) arrive as lowercase (`widgettype`).
	 *
	 * @param array  $atts          Shortcode attributes.
	 * @param string $canonical_key Canonical template setting key.
	 * @return mixed|null
	 */
	private function get_shortcode_att_value( $atts, $canonical_key ) {
		if ( ! is_array( $atts ) || '' === $canonical_key ) {
			return null;
		}

		$aliases = array(
			$canonical_key,
			strtolower( $canonical_key ),
			strtolower( preg_replace( '/([a-z])([A-Z])/', '$1_$2', $canonical_key ) ),
		);
		if ( 'blockTemplate' === $canonical_key ) {
			$aliases[] = 'template';
		}
		$aliases = array_values( array_unique( array_filter( $aliases ) ) );

		foreach ( $aliases as $alias ) {
			if ( array_key_exists( $alias, $atts ) ) {
				return $atts[ $alias ];
			}
		}

		return null;
	}

	/**
	 * Scope raw CSS rules to a container selector.
	 *
	 * @param string $css   Raw CSS.
	 * @param string $scope Scope selector.
	 * @return string
	 */
	private static function scope_custom_css( $css, $scope ) {
		$css = trim( (string) $css );
		if ( '' === $css ) {
			return '';
		}

		$rules  = preg_split( '/\}(?![^{]*\})/', $css );
		$output = '';

		if ( ! is_array( $rules ) ) {
			return '';
		}

		foreach ( $rules as $rule ) {
			$rule = trim( (string) $rule );
			if ( '' === $rule ) {
				continue;
			}

			$parts = explode( '{', $rule, 2 );
			if ( 2 !== count( $parts ) ) {
				continue;
			}

			$selector = trim( $parts[0] );
			$body     = trim( $parts[1] );

			if ( '' === $selector || '' === $body ) {
				continue;
			}

			if ( 0 === strpos( $selector, '@' ) ) {
				$output .= $selector . '{' . $body . '}';
				continue;
			}

			$selector_parts = array_map( 'trim', explode( ',', $selector ) );
			$selector_parts = array_filter( $selector_parts );
			if ( empty( $selector_parts ) ) {
				continue;
			}

			$scoped_selector_parts = array();
			foreach ( $selector_parts as $selector_part ) {
				$scoped_selector_parts[] = $scope . ' ' . $selector_part;
			}

			$output .= implode( ',', $scoped_selector_parts ) . '{' . $body . '}';
		}

		return $output;
	}
}
