<?php
/**
 * Stats counter widget render layer.
 *
 * Shortcode: [rsmspark_stats_counter]
 *
 * @package Rsmspark_Content_Widgets
 */

defined( 'ABSPATH' ) || exit;

/**
 * Rsmspark_Content_Widgets_Widget_Stats_Counter
 */
class Rsmspark_Content_Widgets_Widget_Stats_Counter {

	const REGISTRY_ID = 'stats_counter';

	/**
	 * @return void
	 */
	public static function register() {
		Rsmspark_Content_Widgets_Shortcodes::add(
			Rsmspark_Content_Widgets_Registry::shortcode_tag( self::REGISTRY_ID ),
			array( __CLASS__, 'shortcode' ),
			self::REGISTRY_ID
		);

		Rsmspark_Content_Widgets_Blocks::add(
			'stats-counter',
			array(
				'api_version'     => 3,
				'title'           => __( 'RSM Stats Counter', 'rsm-content-widgets' ),
				'description'     => __( 'Highlight key metrics with labels.', 'rsm-content-widgets' ),
				'category'        => Rsmspark_Content_Widgets_Blocks::BLOCK_CATEGORY,
				'icon'            => 'chart-bar',
				'keywords'        => array( 'stats', 'counter', 'numbers', 'metrics', 'rsm' ),
				'supports'        => array( 'html' => false ),
				'attributes'      => self::block_attributes(),
				'render_callback' => array( __CLASS__, 'render_block' ),
			)
		);
	}

	/**
	 * @return array<string, array<string, mixed>>
	 */
	public static function block_attributes() {
		return array(
			'title'   => array( 'type' => 'string', 'default' => '' ),
			'columns' => array( 'type' => 'string', 'default' => '3' ),
			'items'   => array( 'type' => 'array', 'default' => self::default_items() ),
		);
	}

	/**
	 * @return array<int, array<string, string>>
	 */
	public static function default_items() {
		return array(
			array(
				'value'   => '120',
				'prefix'  => '',
				'suffix'  => '+',
				'label'   => __( 'Projects delivered', 'rsm-content-widgets' ),
				'display' => 'number',
			),
			array(
				'value'   => '98',
				'prefix'  => '',
				'suffix'  => '%',
				'label'   => __( 'Client satisfaction', 'rsm-content-widgets' ),
				'display' => 'donut',
			),
			array(
				'value'   => '15',
				'prefix'  => '',
				'suffix'  => '',
				'label'   => __( 'Years of experience', 'rsm-content-widgets' ),
				'display' => 'number',
			),
		);
	}

	/**
	 * @param array<string, mixed> $atts Shortcode attributes.
	 * @param string               $content Inner content.
	 * @return string
	 */
	public static function shortcode( $atts, $content = '' ) {
		unset( $content );
		return self::render( self::normalize_atts( is_array( $atts ) ? $atts : array() ) );
	}

	/**
	 * @param array<string, mixed> $attributes Block attributes.
	 * @param string               $content Inner content.
	 * @param WP_Block|null        $block Block instance.
	 * @return string
	 */
	public static function render_block( $attributes, $content = '', $block = null ) {
		unset( $content );
		$attributes = self::merge_block_attributes( $attributes, $block );
		return self::render( self::prepare_template_vars( self::normalize_atts( $attributes ) ) );
	}

	/**
	 * @param mixed         $attributes Render callback attributes.
	 * @param WP_Block|null $block      Block instance.
	 * @return array<string, mixed>
	 */
	public static function merge_block_attributes( $attributes, $block = null ) {
		$merged = is_array( $attributes ) ? $attributes : array();

		if ( $block instanceof WP_Block ) {
			if ( ! empty( $block->parsed_block['attrs'] ) && is_array( $block->parsed_block['attrs'] ) ) {
				$merged = array_merge( $merged, $block->parsed_block['attrs'] );
			}
			if ( ! empty( $block->attributes ) && is_array( $block->attributes ) ) {
				$merged = array_merge( $merged, $block->attributes );
			}
		}

		return $merged;
	}

	/**
	 * @param array<string, mixed> $atts Raw attributes.
	 * @return array<string, mixed>
	 */
	public static function normalize_atts( array $atts ) {
		$defaults = array(
			'title'   => '',
			'columns' => '3',
			'items'   => self::default_items(),
		);

		$parsed = Rsmspark_Content_Widgets_Render::parse_atts( $atts, $defaults, 'rsmspark_stats_counter' );

		$columns = (string) $parsed['columns'];
		if ( ! in_array( $columns, array( '2', '3', '4' ), true ) ) {
			$columns = '3';
		}

		return array(
			'title'   => sanitize_text_field( (string) $parsed['title'] ),
			'columns' => $columns,
			'animate' => false,
			'items'   => self::coerce_items( $parsed['items'] ),
		);
	}

	/**
	 * @param mixed $items Raw items.
	 * @return array<int, array<string, string>>
	 */
	public static function coerce_items( $items ) {
		if ( is_string( $items ) && '' !== $items ) {
			$items = Rsmspark_Content_Widgets_Render::parse_json_attr( $items, self::default_items() );
		}
		if ( ! is_array( $items ) ) {
			$items = self::default_items();
		}

		$clean = self::sanitize_items( $items );
		return ! empty( $clean ) ? $clean : self::sanitize_items( self::default_items() );
	}

	/**
	 * @param array<string, mixed> $content SiteOrigin content section.
	 * @return array<string, mixed>
	 */
	public static function prepare_from_instance( array $content ) {
		return self::normalize_atts( $content );
	}

	/**
	 * @param array<string, mixed> $instance SiteOrigin widget instance.
	 * @return array<string, mixed>
	 */
	public static function prepare_from_siteorigin_instance( array $instance ) {
		return self::prepare_from_instance(
			Rsmspark_Content_Widgets_Render::section_instance( $instance, 'content' )
		);
	}

	/**
	 * @param array<string, mixed> $attrs Block attributes.
	 * @return array<string, mixed>
	 */
	public static function instance_from_atts( array $attrs ) {
		$normalized = self::normalize_atts( $attrs );

		return array(
			'content' => array(
				'title'   => $normalized['title'],
				'columns' => $normalized['columns'],
				'animate' => $normalized['animate'],
				'items'   => $normalized['items'],
			),
		);
	}

	/**
	 * @param array<string, mixed> $instance SiteOrigin or import instance.
	 * @return array<string, mixed>
	 */
	public static function sanitize_instance( array $instance ) {
		unset( $instance['import_export'], $instance['panels_info'] );

		if ( isset( $instance['content'] ) ) {
			return self::instance_from_atts( self::prepare_from_siteorigin_instance( $instance ) );
		}

		return self::instance_from_atts( $instance );
	}

	/**
	 * @param array<int, mixed> $items Raw items.
	 * @return array<int, array<string, string>>
	 */
	public static function sanitize_items( array $items ) {
		$clean = array();

		foreach ( $items as $item ) {
			if ( ! is_array( $item ) ) {
				continue;
			}

			$value = sanitize_text_field( (string) ( $item['value'] ?? '' ) );
			$label = sanitize_text_field( (string) ( $item['label'] ?? '' ) );
			if ( '' === $value && '' === $label ) {
				continue;
			}

			$clean[] = array(
				'value'        => $value,
				'prefix'       => sanitize_text_field( (string) ( $item['prefix'] ?? '' ) ),
				'suffix'       => sanitize_text_field( (string) ( $item['suffix'] ?? '' ) ),
				'label'        => $label,
				'display'      => self::sanitize_display( $item['display'] ?? 'number' ),
				'value_color'  => sanitize_hex_color( (string) ( $item['value_color'] ?? '' ) ) ?: '',
				'prefix_color' => sanitize_hex_color( (string) ( $item['prefix_color'] ?? '' ) ) ?: '',
				'suffix_color' => sanitize_hex_color( (string) ( $item['suffix_color'] ?? '' ) ) ?: '',
			);
		}

		return $clean;
	}

	/**
	 * @param mixed $display Raw display mode.
	 * @return string
	 */
	public static function sanitize_display( $display ) {
		$display = sanitize_key( (string) $display );
		if ( ! in_array( $display, array( 'number', 'pie', 'donut' ), true ) ) {
			return 'number';
		}
		return $display;
	}

	/**
	 * Parse a metric value into a 0–100 percentage for chart modes.
	 *
	 * @param string $value Raw value field.
	 * @return float
	 */
	public static function metric_percent( $value ) {
		$clean = preg_replace( '/[^0-9.]/', '', (string) $value );
		if ( '' === $clean ) {
			return 0.0;
		}
		return (float) max( 0, min( 100, (float) $clean ) );
	}

	/**
	 * SVG pie wedge path (12 o'clock origin, clockwise).
	 *
	 * @param float $percent 0–100.
	 * @param float $cx      Center X.
	 * @param float $cy      Center Y.
	 * @param float $radius  Radius.
	 * @return string
	 */
	public static function pie_svg_path( $percent, $cx = 60.0, $cy = 60.0, $radius = 54.0 ) {
		$percent = max( 0, min( 100, (float) $percent ) );
		if ( $percent <= 0 ) {
			return sprintf( 'M %s,%s', $cx, $cy );
		}
		if ( $percent >= 100 ) {
			return sprintf(
				'M %1$s,%2$s m -%3$s,0 a %3$s,%3$s 0 1,1 %4$s,0 a %3$s,%3$s 0 1,1 -%4$s,0',
				$cx,
				$cy,
				$radius,
				( 2 * $radius )
			);
		}

		$angle = ( $percent / 100 ) * 360;
		$rad   = deg2rad( $angle - 90 );
		$x     = $cx + ( $radius * cos( $rad ) );
		$y     = $cy + ( $radius * sin( $rad ) );
		$large = $angle > 180 ? 1 : 0;

		return sprintf(
			'M %1$s,%2$s L %1$s,%3$s A %4$s,%4$s 0 %5$d,1 %6$s,%7$s Z',
			$cx,
			$cy,
			( $cy - $radius ),
			$radius,
			$large,
			round( $x, 3 ),
			round( $y, 3 )
		);
	}

	/**
	 * @param array<string, string> $item Sanitized metric row.
	 * @return array<string, mixed>
	 */
	public static function enrich_item( array $item ) {
		$display = self::sanitize_display( $item['display'] ?? 'number' );
		$percent = self::metric_percent( $item['value'] ?? '' );
		$radius  = 54.0;

		$enriched = array_merge(
			$item,
			array(
				'display' => $display,
				'percent' => $percent,
				'radius'  => $radius,
			)
		);

		if ( 'pie' === $display ) {
			$enriched['pie_path'] = self::pie_svg_path( $percent, 60.0, 60.0, $radius );
		}

		return $enriched;
	}

	/**
	 * @param array<int, array<string, string>> $items Sanitized items.
	 * @return array<int, array<string, mixed>>
	 */
	public static function enrich_items( array $items ) {
		return array_map( array( __CLASS__, 'enrich_item' ), $items );
	}

	/**
	 * @param array<string, mixed> $args Normalized args.
	 * @return array<string, mixed>
	 */
	public static function prepare_template_vars( array $args ) {
		$items = self::enrich_items( $args['items'] ?? array() );

		return array(
			'title'      => $args['title'] ?? '',
			'columns'    => $args['columns'] ?? '3',
			'animate'    => ! empty( $args['animate'] ),
			'items'      => $items,
			'has_charts' => self::has_chart_items( $items ),
			'style_vars' => Rsmspark_Content_Widgets_Render::inline_style_vars(),
		);
	}

	/**
	 * @param array<int, array<string, mixed>> $items Enriched items.
	 * @return bool
	 */
	public static function has_chart_items( array $items ) {
		foreach ( $items as $item ) {
			$display = self::sanitize_display( $item['display'] ?? 'number' );
			if ( in_array( $display, array( 'pie', 'donut' ), true ) ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @param array<string, mixed> $args Template variables.
	 * @return string
	 */
	public static function render( array $args ) {
		$vars = isset( $args['style_vars'] ) ? $args : self::prepare_template_vars( $args );
		if ( empty( $vars['items'] ) ) {
			return '';
		}

		return Rsmspark_Content_Widgets_Render::render_template( self::REGISTRY_ID, 'stats-counter.php', $vars );
	}
}
