<?php
/**
 * Shared HTML escaping for Content Widgets frontend output.
 *
 * @package Rsmspark_Content_Widgets
 */

defined( 'ABSPATH' ) || exit;

/**
 * Late-escape helpers for rendered widget/shortcode HTML.
 */
final class Rsmspark_Content_Widgets_Kses {

	/**
	 * Allowlist for CW frontend markup (post HTML + data attrs + SVG icons).
	 *
	 * Exposed so echo sites can call `wp_kses( $html, self::allowed_html() )`
	 * and satisfy Plugin Check / WordPress.Security.EscapeOutput without phpcs:ignore.
	 *
	 * @return array<string, array<string, bool>>
	 */
	public static function allowed_html() {
		$allowed = wp_kses_allowed_html( 'post' );
		$extra   = self::data_attributes();

		foreach ( array( 'div', 'section', 'nav', 'article', 'aside', 'header', 'footer', 'main', 'ul', 'ol', 'li', 'p', 'span', 'a', 'button', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'figure', 'figcaption', 'time', 'img', 'i', 'strong', 'em', 'label', 'form', 'input', 'select', 'option', 'textarea', 'fieldset', 'legend', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'details', 'summary' ) as $tag ) {
			if ( ! isset( $allowed[ $tag ] ) ) {
				$allowed[ $tag ] = array();
			}
			$allowed[ $tag ] = array_merge( $allowed[ $tag ], $extra );
		}

		$allowed['svg'] = array_merge(
			isset( $allowed['svg'] ) && is_array( $allowed['svg'] ) ? $allowed['svg'] : array(),
			array(
				'class'       => true,
				'id'          => true,
				'style'       => true,
				'xmlns'       => true,
				'width'       => true,
				'height'      => true,
				'viewbox'     => true,
				'viewBox'     => true,
				'fill'        => true,
				'aria-hidden' => true,
				'focusable'   => true,
				'role'        => true,
			)
		);
		$allowed['use'] = array(
			'href'        => true,
			'xlink:href'  => true,
			'xmlns:xlink' => true,
		);
		$allowed['path'] = array(
			'class'        => true,
			'd'            => true,
			'fill'         => true,
			'stroke'       => true,
			'stroke-width' => true,
			'transform'    => true,
		);
		$allowed['circle'] = array(
			'class'               => true,
			'cx'                  => true,
			'cy'                  => true,
			'r'                   => true,
			'fill'                => true,
			'stroke'              => true,
			'stroke-width'        => true,
			'stroke-dasharray'    => true,
			'stroke-dashoffset'   => true,
			'transform'           => true,
		);
		$allowed['g'] = array(
			'class'     => true,
			'transform' => true,
			'fill'      => true,
		);
		$allowed['symbol'] = array(
			'id'      => true,
			'viewBox' => true,
			'viewbox' => true,
		);
		$allowed['defs']  = array();
		$allowed['title'] = array();
		$allowed['desc']  = array();

		return $allowed;
	}

	/**
	 * Allow post HTML plus CW/RSM data attributes and SVG icons.
	 *
	 * @param string $html HTML.
	 * @return string
	 */
	public static function frontend_html( $html ) {
		$html = (string) $html;
		if ( '' === $html ) {
			return '';
		}

		return wp_kses( $html, self::allowed_html() );
	}

	/**
	 * Trusted plugin SVG sprite file.
	 *
	 * @param string $svg SVG markup.
	 * @return string
	 */
	public static function svg_sprite( $svg ) {
		return self::frontend_html( $svg );
	}

	/**
	 * @return array<string, bool>
	 */
	private static function data_attributes() {
		$attrs = array(
			'class'            => true,
			'id'               => true,
			'style'            => true,
			'role'             => true,
			'tabindex'         => true,
			'hidden'           => true,
			'aria-hidden'      => true,
			'aria-label'       => true,
			'aria-labelledby'  => true,
			'aria-pressed'     => true,
			'aria-live'        => true,
			'aria-modal'       => true,
			'aria-controls'    => true,
			'aria-selected'    => true,
			'aria-expanded'    => true,
			'type'             => true,
		);

		$prefixes = array( 'data-rsmspark-', 'data-rsm-', 'data-openrsm-', 'data-rf-', 'data-cw-' );
		$suffixes = array(
			'ajax', 'form-id', 'schema', 'calendar', 'calendar-ajax', 'panel', 'tab', 'feature', 'index',
			'countdown', 'stat', 'value', 'animate', 'chart', 'atts', 'atts-b64', 'loaded', 'toggle',
			'widget', 'instance', 'source-filter', 'source-index', 'event', 'event-card', 'month',
			'month-key', 'month-select', 'calendar-id', 'row', 'field-id', 'field-slug', 'field-type',
			'page-index', 'payments', 'payment-config', 'payment-status', 'save', 'preview', 'gateway',
			'use-recaptcha', 'recaptcha-v3', 'recaptcha-site', 'use-turnstile', 'turnstile-site',
			'container-border-shape', 'open', 'close', 'popout', 'trigger', 'modal', 'dismiss',
			'cw-tabs', 'cw-tab', 'cw-panel', 'cw-tab-slug', 'cw-panel-slug', 'cw-tabs-remember',
			'cw-tabs-storage-key', 'cw-accordion-trigger', 'cw-accordion-slug', 'cw-carousel',
			'cw-carousel-slide', 'cw-carousel-dot', 'cw-carousel-prev', 'cw-carousel-next',
			'cw-pricing-period', 'cw-tip-init', 'cw-pricing-tips-bound', 'cw-stats-host',
			'cw-event-countdown', 'cw-hero-parallax', 'cw-hero-mobile-order', 'features',
			'logo-grid', 'footer', 'button-id',
		);

		foreach ( $prefixes as $prefix ) {
			foreach ( $suffixes as $suffix ) {
				$attrs[ $prefix . $suffix ] = true;
			}
		}

		// Common one-off data keys used in CW templates.
		foreach ( array( 'data-date', 'data-sitekey', 'data-count', 'data-target', 'data-end', 'data-start', 'data-end-ts', 'data-expired-message', 'data-hide-when-expired', 'data-sync-group' ) as $key ) {
			$attrs[ $key ] = true;
		}

		return $attrs;
	}
}
