<?php
/**
 * Widget Name: Star Rating
 * Description: A flexible star/icon rating widget with selectable visual styles,
 *              custom icon, fractional (half-star) support, optional title and
 *              numeric value, plus full color, size and spacing controls.
 *              Only the selected style CSS file is loaded on the frontend.
 *
 * @package    captain-widgets-kit
 * @subpackage captain-widgets-kit/includes
 * @since      1.3.0
 */

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

use Elementor\Widget_Base;
use Elementor\Controls_Manager;
use Elementor\Group_Control_Typography;
use Elementor\Group_Control_Text_Shadow;
use Elementor\Group_Control_Box_Shadow;
use Elementor\Group_Control_Border;
use Elementor\Group_Control_Background;
use Elementor\Icons_Manager;

/**
 * Class Captwiki_Star_Rating
 *
 * @since 1.3.0
 */
class Captwiki_Star_Rating extends Widget_Base {

	/**
	 * Get widget name.
	 *
	 * @since 1.3.0
	 * @return string
	 */
	public function get_name(): string {
		return 'captwiki-star-rating';
	}

	/**
	 * Get widget title.
	 *
	 * @since 1.3.0
	 * @return string
	 */
	public function get_title(): string {
		return esc_html__( 'Star Rating', 'captain-widgets-kit' );
	}

	/**
	 * Get widget icon.
	 *
	 * @since 1.3.0
	 * @return string
	 */
	public function get_icon(): string {
		return 'eicon-star captwiki-el-logo';
	}

	/**
	 * Get widget categories.
	 *
	 * @since 1.3.0
	 * @return array
	 */
	public function get_categories(): array {
		return array( 'captwiki-essential' );
	}

	/**
	 * Get widget keywords.
	 *
	 * @since 1.3.0
	 * @return array
	 */
	public function get_keywords(): array {
		return array( 'star', 'rating', 'review', 'stars', 'score', 'captain-widgets-kit' );
	}

	/**
	 * Common stylesheet dependency.
	 *
	 * @since 1.3.0
	 * @return array
	 */
	public function get_style_depends(): array {
		return array( 'captwiki_star_rating' );
	}

	/**
	 * Disable Elementor's default inner wrapper.
	 *
	 * @since 1.3.0
	 * @return bool
	 */
	public function has_widget_inner_wrapper(): bool {
		return ! \Elementor\Plugin::$instance->experiments->is_feature_active( 'e_optimized_markup' );
	}

	/**
	 * Selectable visual styles.
	 *
	 * @since 1.3.0
	 * @return array
	 */
	private function captwiki_style_options(): array {
		return array(
			'style-1' => esc_html__( 'Style 1 – Classic', 'captain-widgets-kit' ),
			'style-2' => esc_html__( 'Style 2 – Accent Pill', 'captain-widgets-kit' ),
			'style-3' => esc_html__( 'Style 3 – Boxed', 'captain-widgets-kit' ),
			'style-4' => esc_html__( 'Style 4 – Showcase', 'captain-widgets-kit' ),
			'style-5' => esc_html__( 'Style 5 – Minimal', 'captain-widgets-kit' ),
		);
	}

	/**
	 * Register widget controls.
	 *
	 * @since 1.3.0
	 * @return void
	 */
	protected function register_controls(): void {
		$this->register_content_controls();
		$this->register_general_style_controls();
		$this->register_stars_style_controls();
		$this->register_value_style_controls();
		$this->register_title_style_controls();
	}

	/**
	 * Content tab controls.
	 *
	 * @since 1.3.0
	 * @return void
	 */
	private function register_content_controls(): void {

		$this->start_controls_section(
			'section_rating_content',
			array(
				'label' => esc_html__( 'Rating', 'captain-widgets-kit' ),
				'tab'   => Controls_Manager::TAB_CONTENT,
			)
		);

		$this->add_control(
			'widget_style',
			array(
				'label'   => esc_html__( 'Style', 'captain-widgets-kit' ),
				'type'    => Controls_Manager::SELECT,
				'default' => 'style-1',
				'options' => $this->captwiki_style_options(),
			)
		);

		$this->add_control(
			'rating_scale',
			array(
				'label'   => esc_html__( 'Total Stars', 'captain-widgets-kit' ),
				'type'    => Controls_Manager::NUMBER,
				'min'     => 1,
				'max'     => 10,
				'step'    => 1,
				'default' => 5,
			)
		);

		$this->add_control(
			'rating',
			array(
				'label'   => esc_html__( 'Rating', 'captain-widgets-kit' ),
				'type'    => Controls_Manager::SLIDER,
				'range'   => array(
					'px' => array( 'min' => 0, 'max' => 10, 'step' => 0.1 ),
				),
				'default' => array( 'size' => 4.5 ),
			)
		);

		$this->add_control(
			'star_icon',
			array(
				'label'            => esc_html__( 'Icon', 'captain-widgets-kit' ),
				'type'             => Controls_Manager::ICONS,
				'fa4compatibility' => 'icon',
				'default'          => array(
					'value'   => 'fas fa-star',
					'library' => 'fa-solid',
				),
				'skin'             => 'inline',
				'label_block'      => false,
			)
		);

		$this->add_control(
			'show_value',
			array(
				'label'        => esc_html__( 'Show Rating Value', 'captain-widgets-kit' ),
				'type'         => Controls_Manager::SWITCHER,
				'label_on'     => esc_html__( 'Show', 'captain-widgets-kit' ),
				'label_off'    => esc_html__( 'Hide', 'captain-widgets-kit' ),
				'return_value' => 'yes',
				'default'      => 'yes',
			)
		);

		$this->add_control(
			'value_format',
			array(
				'label'     => esc_html__( 'Value Format', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::SELECT,
				'default'   => 'fraction',
				'options'   => array(
					'simple'     => esc_html__( 'Rating ( 4.5 )', 'captain-widgets-kit' ),
					'fraction'   => esc_html__( 'Rating / Total ( 4.5/5 )', 'captain-widgets-kit' ),
					'percentage' => esc_html__( 'Percentage ( 90% )', 'captain-widgets-kit' ),
				),
				'condition' => array( 'show_value' => 'yes' ),
			)
		);

		$this->add_control(
			'title_text',
			array(
				'label'       => esc_html__( 'Title / Caption', 'captain-widgets-kit' ),
				'type'        => Controls_Manager::TEXT,
				'default'     => '',
				'placeholder' => esc_html__( 'e.g. Excellent — 1,240 reviews', 'captain-widgets-kit' ),
				'label_block' => true,
				'dynamic'     => array( 'active' => true ),
			)
		);

		$this->add_control(
			'title_position',
			array(
				'label'     => esc_html__( 'Title Position', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::SELECT,
				'default'   => 'below',
				'options'   => array(
					'above' => esc_html__( 'Above Stars', 'captain-widgets-kit' ),
					'below' => esc_html__( 'Below Stars', 'captain-widgets-kit' ),
				),
				'condition' => array( 'title_text!' => '' ),
			)
		);

		$this->end_controls_section();
	}

	/**
	 * General (wrapper) style controls.
	 *
	 * @since 1.3.0
	 * @return void
	 */
	private function register_general_style_controls(): void {

		$this->start_controls_section(
			'section_general_style',
			array(
				'label' => esc_html__( 'General', 'captain-widgets-kit' ),
				'tab'   => Controls_Manager::TAB_STYLE,
			)
		);

		$this->add_responsive_control(
			'alignment',
			array(
				'label'     => esc_html__( 'Alignment', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::CHOOSE,
				'default'   => 'flex-start',
				'options'   => array(
					'flex-start' => array(
						'title' => esc_html__( 'Left', 'captain-widgets-kit' ),
						'icon'  => 'eicon-text-align-left',
					),
					'center'     => array(
						'title' => esc_html__( 'Center', 'captain-widgets-kit' ),
						'icon'  => 'eicon-text-align-center',
					),
					'flex-end'   => array(
						'title' => esc_html__( 'Right', 'captain-widgets-kit' ),
						'icon'  => 'eicon-text-align-right',
					),
				),
				'selectors' => array(
					'{{WRAPPER}} .captwiki-star-rating' => 'align-items: {{VALUE}};',
				),
			)
		);

		$this->add_group_control(
			Group_Control_Background::get_type(),
			array(
				'name'     => 'wrapper_background',
				'label'    => esc_html__( 'Background', 'captain-widgets-kit' ),
				'types'    => array( 'classic', 'gradient' ),
				'selector' => '{{WRAPPER}} .captwiki-star-rating',
			)
		);

		$this->add_group_control(
			Group_Control_Border::get_type(),
			array(
				'name'     => 'wrapper_border',
				'label'    => esc_html__( 'Border', 'captain-widgets-kit' ),
				'selector' => '{{WRAPPER}} .captwiki-star-rating',
			)
		);

		$this->add_responsive_control(
			'wrapper_border_radius',
			array(
				'label'      => esc_html__( 'Border Radius', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::DIMENSIONS,
				'size_units' => array( 'px', '%', 'em' ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-star-rating' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				),
			)
		);

		$this->add_group_control(
			Group_Control_Box_Shadow::get_type(),
			array(
				'name'     => 'wrapper_box_shadow',
				'label'    => esc_html__( 'Box Shadow', 'captain-widgets-kit' ),
				'selector' => '{{WRAPPER}} .captwiki-star-rating',
			)
		);

		$this->add_responsive_control(
			'wrapper_padding',
			array(
				'label'      => esc_html__( 'Padding', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::DIMENSIONS,
				'size_units' => array( 'px', '%', 'em', 'rem' ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-star-rating' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				),
			)
		);

		$this->add_responsive_control(
			'wrapper_margin',
			array(
				'label'      => esc_html__( 'Margin', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::DIMENSIONS,
				'size_units' => array( 'px', '%', 'em', 'rem' ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-star-rating' => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				),
			)
		);

		$this->end_controls_section();
	}

	/**
	 * Stars style controls.
	 *
	 * @since 1.3.0
	 * @return void
	 */
	private function register_stars_style_controls(): void {

		$this->start_controls_section(
			'section_stars_style',
			array(
				'label' => esc_html__( 'Stars', 'captain-widgets-kit' ),
				'tab'   => Controls_Manager::TAB_STYLE,
			)
		);

		$this->add_responsive_control(
			'star_size',
			array(
				'label'      => esc_html__( 'Size', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::SLIDER,
				'size_units' => array( 'px', 'em' ),
				'range'      => array(
					'px' => array( 'min' => 8, 'max' => 120, 'step' => 1 ),
					'em' => array( 'min' => 0.5, 'max' => 8, 'step' => 0.1 ),
				),
				'default'    => array( 'unit' => 'px', 'size' => 24 ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-star-rating__stars' => 'font-size: {{SIZE}}{{UNIT}};',
				),
			)
		);

		$this->add_responsive_control(
			'star_gap',
			array(
				'label'      => esc_html__( 'Gap', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::SLIDER,
				'size_units' => array( 'px', 'em' ),
				'range'      => array(
					'px' => array( 'min' => 0, 'max' => 40, 'step' => 1 ),
				),
				'default'    => array( 'unit' => 'px', 'size' => 4 ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-star-rating__layer' => 'gap: {{SIZE}}{{UNIT}};',
				),
			)
		);

		$this->add_control(
			'star_empty_color',
			array(
				'label'     => esc_html__( 'Empty Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-star-rating__layer--base' => 'color: {{VALUE}};',
					'{{WRAPPER}} .captwiki-star-rating__layer--base svg' => 'fill: {{VALUE}};',
				),
			)
		);

		$this->add_control(
			'star_filled_color',
			array(
				'label'     => esc_html__( 'Filled Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-star-rating__layer--fill' => 'color: {{VALUE}};',
					'{{WRAPPER}} .captwiki-star-rating__layer--fill svg' => 'fill: {{VALUE}};',
				),
			)
		);

		$this->end_controls_section();
	}

	/**
	 * Value text style controls.
	 *
	 * @since 1.3.0
	 * @return void
	 */
	private function register_value_style_controls(): void {

		$this->start_controls_section(
			'section_value_style',
			array(
				'label'     => esc_html__( 'Rating Value', 'captain-widgets-kit' ),
				'tab'       => Controls_Manager::TAB_STYLE,
				'condition' => array( 'show_value' => 'yes' ),
			)
		);

		$this->add_control(
			'value_color',
			array(
				'label'     => esc_html__( 'Text Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-star-rating__value' => 'color: {{VALUE}};',
				),
			)
		);

		$this->add_control(
			'value_bg_color',
			array(
				'label'     => esc_html__( 'Background Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-star-rating__value' => 'background-color: {{VALUE}};',
				),
			)
		);

		$this->add_group_control(
			Group_Control_Typography::get_type(),
			array(
				'name'     => 'value_typography',
				'selector' => '{{WRAPPER}} .captwiki-star-rating__value',
			)
		);

		$this->add_responsive_control(
			'value_padding',
			array(
				'label'      => esc_html__( 'Padding', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::DIMENSIONS,
				'size_units' => array( 'px', 'em' ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-star-rating__value' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
				),
			)
		);

		$this->add_control(
			'value_radius',
			array(
				'label'      => esc_html__( 'Border Radius', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::SLIDER,
				'size_units' => array( 'px', '%' ),
				'range'      => array( 'px' => array( 'min' => 0, 'max' => 50 ) ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-star-rating__value' => 'border-radius: {{SIZE}}{{UNIT}};',
				),
			)
		);

		$this->end_controls_section();
	}

	/**
	 * Title / caption style controls.
	 *
	 * @since 1.3.0
	 * @return void
	 */
	private function register_title_style_controls(): void {

		$this->start_controls_section(
			'section_title_style',
			array(
				'label'     => esc_html__( 'Title', 'captain-widgets-kit' ),
				'tab'       => Controls_Manager::TAB_STYLE,
				'condition' => array( 'title_text!' => '' ),
			)
		);

		$this->add_control(
			'title_color',
			array(
				'label'     => esc_html__( 'Text Color', 'captain-widgets-kit' ),
				'type'      => Controls_Manager::COLOR,
				'default'   => '',
				'selectors' => array(
					'{{WRAPPER}} .captwiki-star-rating__title' => 'color: {{VALUE}};',
				),
			)
		);

		$this->add_group_control(
			Group_Control_Typography::get_type(),
			array(
				'name'     => 'title_typography',
				'selector' => '{{WRAPPER}} .captwiki-star-rating__title',
			)
		);

		$this->add_group_control(
			Group_Control_Text_Shadow::get_type(),
			array(
				'name'     => 'title_text_shadow',
				'label'    => esc_html__( 'Text Shadow', 'captain-widgets-kit' ),
				'selector' => '{{WRAPPER}} .captwiki-star-rating__title',
			)
		);

		$this->add_responsive_control(
			'title_spacing',
			array(
				'label'      => esc_html__( 'Spacing', 'captain-widgets-kit' ),
				'type'       => Controls_Manager::SLIDER,
				'size_units' => array( 'px', 'em' ),
				'range'      => array( 'px' => array( 'min' => 0, 'max' => 60 ) ),
				'default'    => array( 'unit' => 'px', 'size' => 8 ),
				'selectors'  => array(
					'{{WRAPPER}} .captwiki-star-rating__title' => 'margin: {{SIZE}}{{UNIT}} 0;',
				),
			)
		);

		$this->end_controls_section();
	}

	/**
	 * Normalise the rating and scale into safe numeric values.
	 *
	 * @since 1.3.0
	 * @param array $settings Widget settings.
	 * @return array { float $rating, int $scale, float $percent }
	 */
	private function captwiki_get_rating_data( array $settings ): array {
		$scale  = isset( $settings['rating_scale'] ) ? absint( $settings['rating_scale'] ) : 5;
		$scale  = max( 1, min( 10, $scale ) );
		$rating = isset( $settings['rating']['size'] ) ? (float) $settings['rating']['size'] : 0.0;
		$rating = max( 0.0, min( (float) $scale, $rating ) );

		$percent = ( $rating / $scale ) * 100;
		$percent = max( 0.0, min( 100.0, $percent ) );

		return array(
			'rating'  => $rating,
			'scale'   => $scale,
			'percent' => round( $percent, 2 ),
		);
	}

	/**
	 * Build the formatted value label.
	 *
	 * @since 1.3.0
	 * @param array $settings Widget settings.
	 * @param array $data     Rating data from captwiki_get_rating_data().
	 * @return string
	 */
	private function captwiki_get_value_label( array $settings, array $data ): string {
		$rating  = $this->captwiki_format_number( $data['rating'] );
		$scale   = $this->captwiki_format_number( (float) $data['scale'] );
		$percent = $this->captwiki_format_number( $data['percent'] );

		switch ( $settings['value_format'] ) {
			case 'simple':
				return $rating;
			case 'percentage':
				return $percent . '%';
			case 'fraction':
			default:
				return $rating . '/' . $scale;
		}
	}

	/**
	 * Format a float without trailing zeros ( 4.0 => 4, 4.50 => 4.5 ).
	 *
	 * @since 1.3.0
	 * @param float $number The number to format.
	 * @return string
	 */
	private function captwiki_format_number( float $number ): string {
		return rtrim( rtrim( number_format( $number, 2, '.', '' ), '0' ), '.' );
	}

	/**
	 * Render a single star layer ( base or fill ) with the icon repeated.
	 *
	 * @since 1.3.0
	 * @param string $modifier  Layer modifier ( 'base' or 'fill' ).
	 * @param array  $icon      Elementor icon setting.
	 * @param int    $count     Number of icons to render.
	 * @return void
	 */
	private function captwiki_render_star_layer( string $modifier, array $icon, int $count ): void {
		$hidden = 'fill' === $modifier ? ' aria-hidden="true"' : '';

		echo '<span class="captwiki-star-rating__layer captwiki-star-rating__layer--' . esc_attr( $modifier ) . '"' . $hidden . '>';

		for ( $i = 0; $i < $count; $i++ ) {
			echo '<span class="captwiki-star-rating__icon">';
			Icons_Manager::render_icon( $icon, array( 'aria-hidden' => 'true' ) );
			echo '</span>';
		}

		echo '</span>';
	}

	/**
	 * Render the title / caption.
	 *
	 * @since 1.3.0
	 * @param array $settings Widget settings.
	 * @return void
	 */
	private function captwiki_render_title( array $settings ): void {
		if ( '' === trim( (string) $settings['title_text'] ) ) {
			return;
		}

		echo '<span class="captwiki-star-rating__title">' . esc_html( $settings['title_text'] ) . '</span>';
	}

	/**
	 * Render widget output on the frontend.
	 *
	 * Loads only the selected style CSS file ( depends on the common stylesheet ).
	 *
	 * @since 1.3.0
	 * @return void
	 */
	protected function render(): void {
		$settings = $this->get_settings_for_display();

		$icon = ! empty( $settings['star_icon']['value'] ) ? $settings['star_icon'] : array(
			'value'   => 'fas fa-star',
			'library' => 'fa-solid',
		);

		$allowed_styles = array_keys( $this->captwiki_style_options() );
		$style          = in_array( $settings['widget_style'], $allowed_styles, true ) ? $settings['widget_style'] : 'style-1';

		$data        = $this->captwiki_get_rating_data( $settings );
		$value_label = $this->captwiki_get_value_label( $settings, $data );
		$show_value  = 'yes' === $settings['show_value'];
		$title_pos   = ! empty( $settings['title_position'] ) ? $settings['title_position'] : 'below';

		// Load only the active style stylesheet, depending on the common one.
		wp_enqueue_style(
			'captwiki-star-rating-' . $style,
			CAPTWIKI_URL . 'assets/css/widgets/star-rating/captwiki-star-rating-' . $style . '.css',
			array( 'captwiki_star_rating' ),
			CAPTWIKI_VERSION
		);

		$unique_class = 'captwiki-widget-' . $this->get_id();

		/* translators: 1: rating value, 2: total stars. */
		$aria_label = sprintf(
			esc_attr__( 'Rated %1$s out of %2$s', 'captain-widgets-kit' ),
			$this->captwiki_format_number( $data['rating'] ),
			$this->captwiki_format_number( (float) $data['scale'] )
		);

		// CSS variable carries the dynamic fill width ( data, not styling ).
		$fill_style = 'style="--captwiki-star-fill:' . esc_attr( $data['percent'] ) . '%;"';

		echo '<div class="captwiki-star-rating captwiki-' . esc_attr( $style ) . ' ' . esc_attr( $unique_class ) . '" role="img" aria-label="' . esc_attr( $aria_label ) . '">';

		if ( 'above' === $title_pos ) {
			$this->captwiki_render_title( $settings );
		}

		echo '<div class="captwiki-star-rating__row">';

			echo '<div class="captwiki-star-rating__stars" ' . $fill_style . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				$this->captwiki_render_star_layer( 'base', $icon, $data['scale'] );
				$this->captwiki_render_star_layer( 'fill', $icon, $data['scale'] );
			echo '</div>';

		if ( $show_value && '' !== $value_label ) {
			echo '<span class="captwiki-star-rating__value">' . esc_html( $value_label ) . '</span>';
		}

		echo '</div>';

		if ( 'below' === $title_pos ) {
			$this->captwiki_render_title( $settings );
		}

		echo '</div>';
	}
}
